-Added support for extension data names to NamingStrategy

-Added ProcessExtensionDataNames to NamingStrategy
This commit is contained in:
James Newton-King
2016-12-29 20:46:29 +13:00
parent ee5a229922
commit 2da811b021
7 changed files with 260 additions and 0 deletions
@@ -30,6 +30,7 @@ using System.Text;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Tests.TestObjects;
using Newtonsoft.Json.Tests.TestObjects.Organization;
using Newtonsoft.Json.Serialization;
#if DNXCORE50
using Xunit;
using Test = Xunit.FactAttribute;
@@ -312,6 +313,15 @@ namespace Newtonsoft.Json.Tests.Serialization
}
}
[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy), NamingStrategyParameters = new object[] { true, true, true })]
public class ExtensionDataWithNamingStrategyTestClass
{
public string Name { get; set; }
[JsonExtensionData]
internal IDictionary<string, JToken> ExtensionData { get; set; }
}
public class JObjectExtensionDataTestClass
{
public string Name { get; set; }
@@ -371,6 +381,158 @@ namespace Newtonsoft.Json.Tests.Serialization
Assert.AreEqual(7, c.ExtensionData.Count);
}
[Test]
public void ExtensionDataTest_DeserializeWithNamingStrategy()
{
string json = @"{
""Ints"": [1,2,3],
""Ignored"": [1,2,3],
""Readonly"": ""Readonly"",
""Name"": ""Actually set!"",
""CustomName"": ""Wrong name!"",
""GetPrivate"": true,
""GetOnly"": true,
""NewValueSimple"": true,
""NewValueComplex"": [1,2,3]
}";
ExtensionDataTestClass c = JsonConvert.DeserializeObject<ExtensionDataTestClass>(json, new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy
{
ProcessExtensionDataNames = true
}
}
});
Assert.AreEqual("Actually set!", c.Name);
Assert.AreEqual(4, c.Ints.Count);
Assert.AreEqual("Readonly", (string)c.ExtensionData["Readonly"]);
Assert.AreEqual("Wrong name!", (string)c.ExtensionData["CustomName"]);
Assert.AreEqual(true, (bool)c.ExtensionData["GetPrivate"]);
Assert.AreEqual(true, (bool)c.ExtensionData["GetOnly"]);
Assert.AreEqual(true, (bool)c.ExtensionData["NewValueSimple"]);
Assert.IsTrue(JToken.DeepEquals(new JArray(1, 2, 3), c.ExtensionData["NewValueComplex"]));
Assert.IsTrue(JToken.DeepEquals(new JArray(1, 2, 3), c.ExtensionData["Ignored"]));
Assert.AreEqual(7, c.ExtensionData.Count);
}
[Test]
public void ExtensionDataTest_SerializeWithNamingStrategy_Enabled()
{
ExtensionDataTestClass c = new ExtensionDataTestClass()
{
ExtensionData = new Dictionary<string, JToken>
{
["TestValue1"] = 1,
["alreadyCamelCase"] = new JObject
{
["NotProcessed"] = true
}
}
};
string json = JsonConvert.SerializeObject(c, new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver
{
NamingStrategy = new CamelCaseNamingStrategy
{
ProcessExtensionDataNames = true
}
},
Formatting = Formatting.Indented
});
StringAssert.AreEqual(@"{
""readonly"": ""Readonly"",
""name"": null,
""custom_name"": null,
""getPrivate"": false,
""getOnly"": true,
""ints"": [
0
],
""testValue1"": 1,
""alreadyCamelCase"": {
""NotProcessed"": true
}
}", json);
}
[Test]
public void ExtensionDataTest_SerializeWithNamingStrategy_Disabled()
{
ExtensionDataTestClass c = new ExtensionDataTestClass()
{
ExtensionData = new Dictionary<string, JToken>
{
["TestValue1"] = 1,
["alreadyCamelCase"] = new JObject
{
["NotProcessed"] = true
}
}
};
string json = JsonConvert.SerializeObject(c, new JsonSerializerSettings
{
ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver
{
NamingStrategy = new Newtonsoft.Json.Serialization.CamelCaseNamingStrategy()
},
Formatting = Formatting.Indented
});
StringAssert.AreEqual(@"{
""readonly"": ""Readonly"",
""name"": null,
""custom_name"": null,
""getPrivate"": false,
""getOnly"": true,
""ints"": [
0
],
""TestValue1"": 1,
""alreadyCamelCase"": {
""NotProcessed"": true
}
}", json);
}
[Test]
public void ExtensionDataTest_SerializeWithNamingStrategyAttribute()
{
ExtensionDataWithNamingStrategyTestClass c = new ExtensionDataWithNamingStrategyTestClass()
{
ExtensionData = new Dictionary<string, JToken>
{
["TestValue1"] = 1,
["alreadyCamelCase"] = new JObject
{
["NotProcessed"] = true
}
}
};
string json = JsonConvert.SerializeObject(c, new JsonSerializerSettings
{
Formatting = Formatting.Indented
});
StringAssert.AreEqual(@"{
""name"": null,
""testValue1"": 1,
""alreadyCamelCase"": {
""NotProcessed"": true
}
}", json);
}
[JsonObject(MemberSerialization.OptIn)]
public class MyClass
{
@@ -23,6 +23,25 @@ namespace Newtonsoft.Json.Serialization
OverrideSpecifiedNames = overrideSpecifiedNames;
}
/// <summary>
/// Initializes a new instance of the <see cref="CamelCaseNamingStrategy"/> class.
/// </summary>
/// <param name="processDictionaryKeys">
/// A flag indicating whether dictionary keys should be processed.
/// </param>
/// <param name="overrideSpecifiedNames">
/// A flag indicating whether explicitly specified property names should be processed,
/// e.g. a property name customized with a <see cref="JsonPropertyAttribute"/>.
/// </param>
/// <param name="processExtensionDataNames">
/// A flag indicating whether extension data names should be processed.
/// </param>
public CamelCaseNamingStrategy(bool processDictionaryKeys, bool overrideSpecifiedNames, bool processExtensionDataNames)
: this(processDictionaryKeys, overrideSpecifiedNames)
{
ProcessExtensionDataNames = processExtensionDataNames;
}
/// <summary>
/// Initializes a new instance of the <see cref="CamelCaseNamingStrategy"/> class.
/// </summary>
@@ -398,12 +398,26 @@ namespace Newtonsoft.Json.Serialization
contract.MemberSerialization = JsonTypeReflector.GetObjectMemberSerialization(contract.NonNullableUnderlyingType, ignoreSerializableAttribute);
contract.Properties.AddRange(CreateProperties(contract.NonNullableUnderlyingType, contract.MemberSerialization));
Func<string, string> extensionDataNameResolver = null;
JsonObjectAttribute attribute = JsonTypeReflector.GetCachedAttribute<JsonObjectAttribute>(contract.NonNullableUnderlyingType);
if (attribute != null)
{
contract.ItemRequired = attribute._itemRequired;
if (attribute.NamingStrategyType != null)
{
NamingStrategy namingStrategy = JsonTypeReflector.GetContainerNamingStrategy(attribute);
extensionDataNameResolver = s => namingStrategy.GetDictionaryKey(s);
}
}
if (extensionDataNameResolver == null)
{
extensionDataNameResolver = ResolveExtensionDataName;
}
contract.ExtensionDataNameResolver = extensionDataNameResolver;
if (contract.IsInstantiable)
{
ConstructorInfo overrideConstructor = GetAttributeConstructor(contract.NonNullableUnderlyingType);
@@ -1604,6 +1618,21 @@ namespace Newtonsoft.Json.Serialization
return propertyName;
}
/// <summary>
/// Resolves the name of the extension data. By default no changes are made to extension data names.
/// </summary>
/// <param name="extensionDataName">Name of the extension data.</param>
/// <returns>Resolved name of the extension data.</returns>
protected virtual string ResolveExtensionDataName(string extensionDataName)
{
if (NamingStrategy != null)
{
return NamingStrategy.GetExtensionDataName(extensionDataName);
}
return extensionDataName;
}
/// <summary>
/// Resolves the key of the dictionary. By default <see cref="ResolvePropertyName"/> is used to resolve dictionary keys.
/// </summary>
@@ -158,6 +158,12 @@ namespace Newtonsoft.Json.Serialization
}
}
/// <summary>
/// Gets or sets the extension data name resolver.
/// </summary>
/// <value>The extension data name resolver.</value>
public Func<string, string> ExtensionDataNameResolver { get; set; }
internal bool ExtensionDataIsJToken;
private bool? _hasRequiredOrDefaultValueProperties;
private ConstructorInfo _parametrizedConstructor;
@@ -496,6 +496,10 @@ namespace Newtonsoft.Json.Serialization
bool escape;
string propertyName = GetPropertyName(writer, e.Key, keyContract, out escape);
propertyName = (contract.ExtensionDataNameResolver != null)
? contract.ExtensionDataNameResolver(propertyName)
: propertyName;
if (ShouldWriteReference(e.Value, null, valueContract, contract, member))
{
writer.WritePropertyName(propertyName);
@@ -11,6 +11,12 @@
/// </summary>
public bool ProcessDictionaryKeys { get; set; }
/// <summary>
/// A flag indicating whether extension data names should be processed.
/// Defaults to <c>false</c>.
/// </summary>
public bool ProcessExtensionDataNames { get; set; }
/// <summary>
/// A flag indicating whether explicitly specified property names,
/// e.g. a property name customized with a <see cref="JsonPropertyAttribute"/>, should be processed.
@@ -34,6 +40,21 @@
return ResolvePropertyName(name);
}
/// <summary>
/// Gets the serialized name for a given extension data name.
/// </summary>
/// <param name="name">The initial extension data name.</param>
/// <returns>The serialized extension data name.</returns>
public virtual string GetExtensionDataName(string name)
{
if (!ProcessExtensionDataNames)
{
return name;
}
return ResolvePropertyName(name);
}
/// <summary>
/// Gets the serialized key for a given dictionary key.
/// </summary>
@@ -23,6 +23,25 @@ namespace Newtonsoft.Json.Serialization
OverrideSpecifiedNames = overrideSpecifiedNames;
}
/// <summary>
/// Initializes a new instance of the <see cref="SnakeCaseNamingStrategy"/> class.
/// </summary>
/// <param name="processDictionaryKeys">
/// A flag indicating whether dictionary keys should be processed.
/// </param>
/// <param name="overrideSpecifiedNames">
/// A flag indicating whether explicitly specified property names should be processed,
/// e.g. a property name customized with a <see cref="JsonPropertyAttribute"/>.
/// </param>
/// <param name="processExtensionDataNames">
/// A flag indicating whether extension data names should be processed.
/// </param>
public SnakeCaseNamingStrategy(bool processDictionaryKeys, bool overrideSpecifiedNames, bool processExtensionDataNames)
: this(processDictionaryKeys, overrideSpecifiedNames)
{
ProcessExtensionDataNames = processExtensionDataNames;
}
/// <summary>
/// Initializes a new instance of the <see cref="SnakeCaseNamingStrategy"/> class.
/// </summary>