diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj
index 5dc30cae..648bf706 100644
--- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj
+++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj
@@ -96,6 +96,7 @@
+
diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj
index 1427dc21..d5fa425b 100644
--- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj
+++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj
@@ -139,6 +139,7 @@
+
diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net40.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net40.csproj
index 7c6e598b..f0cdaacf 100644
--- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net40.csproj
+++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net40.csproj
@@ -257,6 +257,7 @@
+
diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable.csproj
index 0e7c701f..b39e015c 100644
--- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable.csproj
+++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable.csproj
@@ -132,6 +132,7 @@
+
diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable40.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable40.csproj
index 4e3230e8..0b84de7d 100644
--- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable40.csproj
+++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Portable40.csproj
@@ -136,6 +136,7 @@
+
diff --git a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj
index 4d0de2a9..0d74f34c 100644
--- a/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj
+++ b/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj
@@ -262,6 +262,7 @@
+
diff --git a/Src/Newtonsoft.Json.Tests/Serialization/CamelCaseNamingStrategyTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/CamelCaseNamingStrategyTests.cs
new file mode 100644
index 00000000..392933ea
--- /dev/null
+++ b/Src/Newtonsoft.Json.Tests/Serialization/CamelCaseNamingStrategyTests.cs
@@ -0,0 +1,327 @@
+#region License
+// Copyright (c) 2007 James Newton-King
+//
+// Permission is hereby granted, free of charge, to any person
+// obtaining a copy of this software and associated documentation
+// files (the "Software"), to deal in the Software without
+// restriction, including without limitation the rights to use,
+// copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the
+// Software is furnished to do so, subject to the following
+// conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+// OTHER DEALINGS IN THE SOFTWARE.
+#endregion
+
+using System;
+using System.Collections.Generic;
+using Newtonsoft.Json.Serialization;
+#if NETFX_CORE
+using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
+using TestFixture = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestClassAttribute;
+using Test = Microsoft.VisualStudio.TestPlatform.UnitTestFramework.TestMethodAttribute;
+#elif DNXCORE50
+using Xunit;
+using Test = Xunit.FactAttribute;
+using Assert = Newtonsoft.Json.Tests.XUnitAssert;
+#else
+using NUnit.Framework;
+#endif
+using Newtonsoft.Json.Tests.TestObjects;
+using Newtonsoft.Json.Linq;
+using System.Reflection;
+using Newtonsoft.Json.Utilities;
+
+namespace Newtonsoft.Json.Tests.Serialization
+{
+ [TestFixture]
+ public class CamelCaseNamingStrategyTests : TestFixtureBase
+ {
+ [Test]
+ public void JsonConvertSerializerSettings()
+ {
+ Person person = new Person();
+ person.BirthDate = new DateTime(2000, 11, 20, 23, 55, 44, DateTimeKind.Utc);
+ person.LastModified = new DateTime(2000, 11, 20, 23, 55, 44, DateTimeKind.Utc);
+ person.Name = "Name!";
+
+ DefaultContractResolver contractResolver = new DefaultContractResolver
+ {
+ NamingStrategy = new CamelCaseNamingStrategy()
+ };
+
+ string json = JsonConvert.SerializeObject(person, Formatting.Indented, new JsonSerializerSettings
+ {
+ ContractResolver = contractResolver
+ });
+
+ StringAssert.AreEqual(@"{
+ ""name"": ""Name!"",
+ ""birthDate"": ""2000-11-20T23:55:44Z"",
+ ""lastModified"": ""2000-11-20T23:55:44Z""
+}", json);
+
+ Person deserializedPerson = JsonConvert.DeserializeObject(json, new JsonSerializerSettings
+ {
+ ContractResolver = contractResolver
+ });
+
+ Assert.AreEqual(person.BirthDate, deserializedPerson.BirthDate);
+ Assert.AreEqual(person.LastModified, deserializedPerson.LastModified);
+ Assert.AreEqual(person.Name, deserializedPerson.Name);
+
+ json = JsonConvert.SerializeObject(person, Formatting.Indented);
+ StringAssert.AreEqual(@"{
+ ""Name"": ""Name!"",
+ ""BirthDate"": ""2000-11-20T23:55:44Z"",
+ ""LastModified"": ""2000-11-20T23:55:44Z""
+}", json);
+ }
+
+ [Test]
+ public void JTokenWriter_OverrideSpecifiedName()
+ {
+ JsonIgnoreAttributeOnClassTestClass ignoreAttributeOnClassTestClass = new JsonIgnoreAttributeOnClassTestClass();
+ ignoreAttributeOnClassTestClass.Field = int.MinValue;
+
+ DefaultContractResolver contractResolver = new DefaultContractResolver
+ {
+ NamingStrategy = new CamelCaseNamingStrategy
+ {
+ OverrideSpecifiedNames = true
+ }
+ };
+
+ JsonSerializer serializer = new JsonSerializer();
+ serializer.ContractResolver = contractResolver;
+
+ JTokenWriter writer = new JTokenWriter();
+
+ serializer.Serialize(writer, ignoreAttributeOnClassTestClass);
+
+ JObject o = (JObject)writer.Token;
+ JProperty p = o.Property("theField");
+
+ Assert.IsNotNull(p);
+ Assert.AreEqual(int.MinValue, (int)p.Value);
+ }
+
+ [Test]
+ public void BlogPostExample()
+ {
+ Product product = new Product
+ {
+ ExpiryDate = new DateTime(2010, 12, 20, 18, 1, 0, DateTimeKind.Utc),
+ Name = "Widget",
+ Price = 9.99m,
+ Sizes = new[] { "Small", "Medium", "Large" }
+ };
+
+ DefaultContractResolver contractResolver = new DefaultContractResolver
+ {
+ NamingStrategy = new CamelCaseNamingStrategy()
+ };
+
+ string json =
+ JsonConvert.SerializeObject(
+ product,
+ Formatting.Indented,
+ new JsonSerializerSettings { ContractResolver = contractResolver }
+ );
+
+ //{
+ // "name": "Widget",
+ // "expiryDate": "\/Date(1292868060000)\/",
+ // "price": 9.99,
+ // "sizes": [
+ // "Small",
+ // "Medium",
+ // "Large"
+ // ]
+ //}
+
+ StringAssert.AreEqual(@"{
+ ""name"": ""Widget"",
+ ""expiryDate"": ""2010-12-20T18:01:00Z"",
+ ""price"": 9.99,
+ ""sizes"": [
+ ""Small"",
+ ""Medium"",
+ ""Large""
+ ]
+}", json);
+ }
+
+#if !(NET35 || NET20 || PORTABLE40)
+ [Test]
+ public void DynamicCamelCasePropertyNames()
+ {
+ dynamic o = new TestDynamicObject();
+ o.Text = "Text!";
+ o.Integer = int.MaxValue;
+
+ DefaultContractResolver contractResolver = new DefaultContractResolver
+ {
+ NamingStrategy = new CamelCaseNamingStrategy
+ {
+ CamelCaseDictionaryKeys = true
+ }
+ };
+
+ string json = JsonConvert.SerializeObject(o, Formatting.Indented,
+ new JsonSerializerSettings
+ {
+ ContractResolver = contractResolver
+ });
+
+ StringAssert.AreEqual(@"{
+ ""explicit"": false,
+ ""text"": ""Text!"",
+ ""integer"": 2147483647,
+ ""int"": 0,
+ ""childObject"": null
+}", json);
+ }
+#endif
+
+ [Test]
+ public void DictionaryCamelCasePropertyNames_Disabled()
+ {
+ Dictionary values = new Dictionary
+ {
+ { "First", "Value1!" },
+ { "Second", "Value2!" }
+ };
+
+ DefaultContractResolver contractResolver = new DefaultContractResolver
+ {
+ NamingStrategy = new CamelCaseNamingStrategy()
+ };
+
+ string json = JsonConvert.SerializeObject(values, Formatting.Indented,
+ new JsonSerializerSettings
+ {
+ ContractResolver = contractResolver
+ });
+
+ StringAssert.AreEqual(@"{
+ ""First"": ""Value1!"",
+ ""Second"": ""Value2!""
+}", json);
+ }
+
+ [Test]
+ public void DictionaryCamelCasePropertyNames_Enabled()
+ {
+ Dictionary values = new Dictionary
+ {
+ { "First", "Value1!" },
+ { "Second", "Value2!" }
+ };
+
+ DefaultContractResolver contractResolver = new DefaultContractResolver
+ {
+ NamingStrategy = new CamelCaseNamingStrategy
+ {
+ CamelCaseDictionaryKeys = true
+ }
+ };
+
+ string json = JsonConvert.SerializeObject(values, Formatting.Indented,
+ new JsonSerializerSettings
+ {
+ ContractResolver = contractResolver
+ });
+
+ StringAssert.AreEqual(@"{
+ ""first"": ""Value1!"",
+ ""second"": ""Value2!""
+}", json);
+ }
+
+ public class PropertyAttributeNamingStrategyTestClass
+ {
+ [JsonProperty]
+ public string HasNoAttributeNamingStrategy { get; set; }
+
+ [JsonProperty(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
+ public string HasAttributeNamingStrategy { get; set; }
+ }
+
+ [Test]
+ public void JsonPropertyAttribute_NamingStrategyType()
+ {
+ PropertyAttributeNamingStrategyTestClass c = new PropertyAttributeNamingStrategyTestClass
+ {
+ HasNoAttributeNamingStrategy = "Value1!",
+ HasAttributeNamingStrategy = "Value2!"
+ };
+
+ string json = JsonConvert.SerializeObject(c, Formatting.Indented);
+
+ StringAssert.AreEqual(@"{
+ ""HasNoAttributeNamingStrategy"": ""Value1!"",
+ ""hasAttributeNamingStrategy"": ""Value2!""
+}", json);
+ }
+
+ [JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
+ public class ContainerAttributeNamingStrategyTestClass
+ {
+ public string Prop1 { get; set; }
+ public string Prop2 { get; set; }
+ [JsonProperty(NamingStrategyType = typeof(DefaultNamingStrategy))]
+ public string HasAttributeNamingStrategy { get; set; }
+ }
+
+ [Test]
+ public void JsonObjectAttribute_NamingStrategyType()
+ {
+ ContainerAttributeNamingStrategyTestClass c = new ContainerAttributeNamingStrategyTestClass
+ {
+ Prop1 = "Value1!",
+ Prop2 = "Value2!"
+ };
+
+ string json = JsonConvert.SerializeObject(c, Formatting.Indented);
+
+ StringAssert.AreEqual(@"{
+ ""prop1"": ""Value1!"",
+ ""prop2"": ""Value2!"",
+ ""HasAttributeNamingStrategy"": null
+}", json);
+ }
+
+ [JsonDictionary(NamingStrategyType = typeof(CamelCaseNamingStrategy), NamingStrategyParameters = new object[] { true, true })]
+ public class DictionaryAttributeNamingStrategyTestClass : Dictionary
+ {
+ }
+
+ [Test]
+ public void JsonDictionaryAttribute_NamingStrategyType()
+ {
+ DictionaryAttributeNamingStrategyTestClass c = new DictionaryAttributeNamingStrategyTestClass
+ {
+ ["Key1"] = "Value1!",
+ ["Key2"] = "Value2!"
+ };
+
+ string json = JsonConvert.SerializeObject(c, Formatting.Indented);
+
+ StringAssert.AreEqual(@"{
+ ""key1"": ""Value1!"",
+ ""key2"": ""Value2!""
+}", json);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Src/Newtonsoft.Json/JsonContainerAttribute.cs b/Src/Newtonsoft.Json/JsonContainerAttribute.cs
index cfc1b58f..bf894657 100644
--- a/Src/Newtonsoft.Json/JsonContainerAttribute.cs
+++ b/Src/Newtonsoft.Json/JsonContainerAttribute.cs
@@ -24,6 +24,7 @@
#endregion
using System;
+using Newtonsoft.Json.Serialization;
namespace Newtonsoft.Json
{
@@ -68,12 +69,36 @@ namespace Newtonsoft.Json
///
public object[] ItemConverterParameters { get; set; }
+ public Type NamingStrategyType
+ {
+ get { return _namingStrategyType; }
+ set
+ {
+ _namingStrategyType = value;
+ NamingStrategy = null;
+ }
+ }
+
+ public object[] NamingStrategyParameters
+ {
+ get { return _namingStrategyParameters; }
+ set
+ {
+ _namingStrategyParameters = value;
+ NamingStrategy = null;
+ }
+ }
+
+ internal INamingStrategy NamingStrategy { get; set; }
+
// yuck. can't set nullable properties on an attribute in C#
// have to use this approach to get an unset default state
internal bool? _isReference;
internal bool? _itemIsReference;
internal ReferenceLoopHandling? _itemReferenceLoopHandling;
internal TypeNameHandling? _itemTypeNameHandling;
+ private Type _namingStrategyType;
+ private object[] _namingStrategyParameters;
///
/// Gets or sets a value that indicates whether to preserve object references.
diff --git a/Src/Newtonsoft.Json/JsonPropertyAttribute.cs b/Src/Newtonsoft.Json/JsonPropertyAttribute.cs
index 6efe0ed7..211bd842 100644
--- a/Src/Newtonsoft.Json/JsonPropertyAttribute.cs
+++ b/Src/Newtonsoft.Json/JsonPropertyAttribute.cs
@@ -64,6 +64,9 @@ namespace Newtonsoft.Json
///
public object[] ItemConverterParameters { get; set; }
+ public Type NamingStrategyType { get; set; }
+ public object[] NamingStrategyParameters { get; set; }
+
///
/// Gets or sets the null value handling used when serializing this property.
///
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj
index b63455d0..768041e0 100644
--- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj
+++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj
@@ -128,9 +128,12 @@
+
+
+
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj
index 09ea61c9..1330dc52 100644
--- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj
+++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj
@@ -142,9 +142,12 @@
+
+
+
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj
index e4cf0e32..f78c0ef7 100644
--- a/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj
+++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Net40.csproj
@@ -141,9 +141,12 @@
+
+
+
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj
index 2ff19dd6..963f2d2f 100644
--- a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj
+++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable.csproj
@@ -164,7 +164,9 @@
+
+
@@ -175,6 +177,7 @@
+
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj
index 762db4d0..9ff94ac4 100644
--- a/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj
+++ b/Src/Newtonsoft.Json/Newtonsoft.Json.Portable40.csproj
@@ -169,8 +169,10 @@
+
+
@@ -180,6 +182,7 @@
+
diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj
index d69978da..363c7dda 100644
--- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj
+++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj
@@ -168,8 +168,10 @@
+
+
@@ -179,6 +181,7 @@
+
diff --git a/Src/Newtonsoft.Json/Serialization/CamelCaseNamingStrategy.cs b/Src/Newtonsoft.Json/Serialization/CamelCaseNamingStrategy.cs
new file mode 100644
index 00000000..a2d8a8c8
--- /dev/null
+++ b/Src/Newtonsoft.Json/Serialization/CamelCaseNamingStrategy.cs
@@ -0,0 +1,40 @@
+using Newtonsoft.Json.Utilities;
+
+namespace Newtonsoft.Json.Serialization
+{
+ public class CamelCaseNamingStrategy : INamingStrategy
+ {
+ public bool CamelCaseDictionaryKeys { get; set; }
+ public bool OverrideSpecifiedNames { get; set; }
+
+ public CamelCaseNamingStrategy()
+ {
+ }
+
+ public CamelCaseNamingStrategy(bool camelCaseDictionaryKeys, bool overrideSpecifiedNames)
+ {
+ CamelCaseDictionaryKeys = camelCaseDictionaryKeys;
+ OverrideSpecifiedNames = overrideSpecifiedNames;
+ }
+
+ public string GetPropertyName(string s, bool hasSpecifiedName)
+ {
+ if (hasSpecifiedName && !OverrideSpecifiedNames)
+ {
+ return s;
+ }
+
+ return StringUtils.ToCamelCase(s);
+ }
+
+ public string GetDictionaryKey(string s)
+ {
+ if (!CamelCaseDictionaryKeys)
+ {
+ return s;
+ }
+
+ return StringUtils.ToCamelCase(s);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs b/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs
index ea9ee82d..42061260 100644
--- a/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs
+++ b/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs
@@ -41,17 +41,11 @@ namespace Newtonsoft.Json.Serialization
: base(true)
#pragma warning restore 612,618
{
- }
-
- ///
- /// Resolves the name of the property.
- ///
- /// Name of the property.
- /// The property name camel cased.
- protected override string ResolvePropertyName(string propertyName)
- {
- // lower case the first letter (or more) of the passed in name
- return StringUtils.ToCamelCase(propertyName);
+ NamingStrategy = new CamelCaseNamingStrategy
+ {
+ CamelCaseDictionaryKeys = true,
+ OverrideSpecifiedNames = true
+ };
}
}
}
\ No newline at end of file
diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs
index bf970338..3c0b769e 100644
--- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs
+++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs
@@ -185,6 +185,8 @@ namespace Newtonsoft.Json.Serialization
public bool IgnoreSerializableAttribute { get; set; }
#endif
+ public INamingStrategy NamingStrategy { get; set; }
+
///
/// Initializes a new instance of the class.
///
@@ -738,7 +740,6 @@ namespace Newtonsoft.Json.Serialization
#if NET35
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1903:UseOnlyApiFromTargetedFramework", MessageId = "System.Runtime.Serialization.DataContractAttribute.#get_IsReference()")]
#endif
-
private void InitializeContract(JsonContract contract)
{
JsonContainerAttribute containerAttribute = JsonTypeReflector.GetCachedAttribute(contract.NonNullableUnderlyingType);
@@ -906,7 +907,9 @@ namespace Newtonsoft.Json.Serialization
#endif
#if DOTNET
if (t.IsGenericType() && t.GetGenericTypeDefinition() == typeof(ConcurrentDictionary<,>))
+ {
return true;
+ }
#endif
return false;
@@ -938,7 +941,16 @@ namespace Newtonsoft.Json.Serialization
JsonDictionaryContract contract = new JsonDictionaryContract(objectType);
InitializeContract(contract);
- contract.DictionaryKeyResolver = ResolveDictionaryKey;
+ JsonContainerAttribute containerAttribute = JsonTypeReflector.GetAttribute(objectType);
+ if (containerAttribute?.NamingStrategyType != null)
+ {
+ INamingStrategy namingStrategy = JsonTypeReflector.GetContainerNamingStrategy(containerAttribute);
+ contract.DictionaryKeyResolver = s => namingStrategy.GetDictionaryKey(s);
+ }
+ else
+ {
+ contract.DictionaryKeyResolver = ResolveDictionaryKey;
+ }
ConstructorInfo overrideConstructor = GetAttributeConstructor(contract.NonNullableUnderlyingType);
@@ -1066,7 +1078,17 @@ namespace Newtonsoft.Json.Serialization
JsonDynamicContract contract = new JsonDynamicContract(objectType);
InitializeContract(contract);
- contract.PropertyNameResolver = ResolvePropertyName;
+ JsonContainerAttribute containerAttribute = JsonTypeReflector.GetAttribute(objectType);
+ if (containerAttribute?.NamingStrategyType != null)
+ {
+ INamingStrategy namingStrategy = JsonTypeReflector.GetContainerNamingStrategy(containerAttribute);
+ contract.PropertyNameResolver = s => namingStrategy.GetDictionaryKey(s);
+ }
+ else
+ {
+ contract.PropertyNameResolver = ResolveDictionaryKey;
+ }
+
contract.Properties.AddRange(CreateProperties(objectType, MemberSerialization.OptOut));
return contract;
@@ -1387,22 +1409,50 @@ namespace Newtonsoft.Json.Serialization
JsonRequiredAttribute requiredAttribute = JsonTypeReflector.GetAttribute(attributeProvider);
string mappedName;
+ bool hasSpecifiedName;
if (propertyAttribute != null && propertyAttribute.PropertyName != null)
{
mappedName = propertyAttribute.PropertyName;
+ hasSpecifiedName = true;
}
#if !NET20
else if (dataMemberAttribute != null && dataMemberAttribute.Name != null)
{
mappedName = dataMemberAttribute.Name;
+ hasSpecifiedName = true;
}
#endif
else
{
mappedName = name;
+ hasSpecifiedName = false;
}
- property.PropertyName = ResolvePropertyName(mappedName);
+ JsonContainerAttribute containerAttribute = JsonTypeReflector.GetAttribute(declaringType);
+
+ INamingStrategy namingStrategy;
+ if (propertyAttribute?.NamingStrategyType != null)
+ {
+ namingStrategy = JsonTypeReflector.CreateNamingStrategyInstance(propertyAttribute.NamingStrategyType, propertyAttribute.NamingStrategyParameters);
+ }
+ else if (containerAttribute?.NamingStrategyType != null)
+ {
+ namingStrategy = JsonTypeReflector.GetContainerNamingStrategy(containerAttribute);
+ }
+ else
+ {
+ namingStrategy = NamingStrategy;
+ }
+
+ if (namingStrategy != null)
+ {
+ property.PropertyName = namingStrategy.GetPropertyName(mappedName, hasSpecifiedName);
+ }
+ else
+ {
+ property.PropertyName = ResolvePropertyName(mappedName);
+ }
+
property.UnderlyingName = name;
bool hasMemberAttribute = false;
@@ -1543,6 +1593,11 @@ namespace Newtonsoft.Json.Serialization
/// Resolved name of the property.
protected virtual string ResolvePropertyName(string propertyName)
{
+ if (NamingStrategy != null)
+ {
+ return NamingStrategy.GetPropertyName(propertyName, false);
+ }
+
return propertyName;
}
@@ -1553,6 +1608,11 @@ namespace Newtonsoft.Json.Serialization
/// Resolved key of the dictionary.
protected virtual string ResolveDictionaryKey(string dictionaryKey)
{
+ if (NamingStrategy != null)
+ {
+ return NamingStrategy.GetDictionaryKey(dictionaryKey);
+ }
+
return ResolvePropertyName(dictionaryKey);
}
diff --git a/Src/Newtonsoft.Json/Serialization/DefaultNamingStrategy.cs b/Src/Newtonsoft.Json/Serialization/DefaultNamingStrategy.cs
new file mode 100644
index 00000000..2ddec384
--- /dev/null
+++ b/Src/Newtonsoft.Json/Serialization/DefaultNamingStrategy.cs
@@ -0,0 +1,15 @@
+namespace Newtonsoft.Json.Serialization
+{
+ public class DefaultNamingStrategy : INamingStrategy
+ {
+ public string GetPropertyName(string s, bool hasSpecifiedName)
+ {
+ return s;
+ }
+
+ public string GetDictionaryKey(string s)
+ {
+ return s;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Src/Newtonsoft.Json/Serialization/INamingStrategy.cs b/Src/Newtonsoft.Json/Serialization/INamingStrategy.cs
new file mode 100644
index 00000000..3767be6f
--- /dev/null
+++ b/Src/Newtonsoft.Json/Serialization/INamingStrategy.cs
@@ -0,0 +1,8 @@
+namespace Newtonsoft.Json.Serialization
+{
+ public interface INamingStrategy
+ {
+ string GetPropertyName(string s, bool hasSpecifiedName);
+ string GetDictionaryKey(string s);
+ }
+}
\ No newline at end of file
diff --git a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs
index 0744b943..ac649dc5 100644
--- a/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs
+++ b/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs
@@ -56,8 +56,8 @@ namespace Newtonsoft.Json.Serialization
public const string ShouldSerializePrefix = "ShouldSerialize";
public const string SpecifiedPostfix = "Specified";
- private static readonly ThreadSafeStore> JsonConverterCreatorCache =
- new ThreadSafeStore>(GetJsonConverterCreator);
+ private static readonly ThreadSafeStore> CreatorCache =
+ new ThreadSafeStore>(GetCreator);
#if !(NET20 || DOTNET)
private static readonly ThreadSafeStore AssociatedMetadataTypesCache = new ThreadSafeStore(GetAssociateMetadataTypeFromAttribute);
@@ -162,10 +162,10 @@ namespace Newtonsoft.Json.Serialization
if (converterAttribute != null)
{
- Func