Add support for NamingStrategy to StringEnumConverter (#1885)

This commit is contained in:
James Newton-King
2018-10-21 18:27:00 +13:00
committed by GitHub
parent faaa23eb9e
commit d48558b53b
14 changed files with 380 additions and 171 deletions
@@ -82,7 +82,9 @@ namespace Newtonsoft.Json.Tests.Converters
string json = JsonConvert.SerializeObject(regex, Formatting.Indented, new JsonSerializerSettings
{
#pragma warning disable CS0618 // Type or member is obsolete
Converters = { new RegexConverter(), new StringEnumConverter() { CamelCaseText = true } },
#pragma warning restore CS0618 // Type or member is obsolete
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
@@ -29,6 +29,7 @@ using System.IO;
using System.Runtime.Serialization;
using System.Text;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
#if DNXCORE50
using Xunit;
using Test = Xunit.FactAttribute;
@@ -132,39 +133,173 @@ namespace Newtonsoft.Json.Tests.Converters
}
[JsonConverter(typeof(StringEnumConverter), true)]
public enum CamelCaseEnum
public enum CamelCaseEnumObsolete
{
This,
Is,
CamelCase
}
[JsonConverter(typeof(StringEnumConverter), true, false)]
[JsonConverter(typeof(StringEnumConverter), typeof(CamelCaseNamingStrategy))]
public enum CamelCaseEnumNew
{
This,
Is,
CamelCase
}
[JsonConverter(typeof(StringEnumConverter), typeof(SnakeCaseNamingStrategy))]
public enum SnakeCaseEnumNew
{
This,
Is,
SnakeCase
}
[JsonConverter(typeof(StringEnumConverter), typeof(CamelCaseNamingStrategy), new object[0], false)]
public enum NotAllowIntegerValuesEnum
{
Foo = 0,
Bar = 1
}
[JsonConverter(typeof(StringEnumConverter), true, true)]
[JsonConverter(typeof(StringEnumConverter), typeof(CamelCaseNamingStrategy))]
public enum AllowIntegerValuesEnum
{
Foo = 0,
Bar = 1
}
[JsonConverter(typeof(StringEnumConverter), typeof(CamelCaseNamingStrategy), null)]
public enum NullArgumentInAttribute
{
Foo = 0,
Bar = 1
}
[Test]
public void Serialize_CamelCaseFromAttribute_Obsolete()
{
string json = JsonConvert.SerializeObject(CamelCaseEnumObsolete.CamelCase);
Assert.AreEqual(@"""camelCase""", json);
}
[Test]
public void NamingStrategyAndCamelCaseText()
{
StringEnumConverter converter = new StringEnumConverter();
Assert.IsNull(converter.NamingStrategy);
#pragma warning disable CS0618 // Type or member is obsolete
converter.CamelCaseText = true;
#pragma warning restore CS0618 // Type or member is obsolete
Assert.IsNotNull(converter.NamingStrategy);
Assert.AreEqual(typeof(CamelCaseNamingStrategy), converter.NamingStrategy.GetType());
var camelCaseInstance = converter.NamingStrategy;
#pragma warning disable CS0618 // Type or member is obsolete
converter.CamelCaseText = true;
#pragma warning restore CS0618 // Type or member is obsolete
Assert.AreEqual(camelCaseInstance, converter.NamingStrategy);
converter.NamingStrategy = null;
#pragma warning disable CS0618 // Type or member is obsolete
Assert.IsFalse(converter.CamelCaseText);
#pragma warning restore CS0618 // Type or member is obsolete
converter.NamingStrategy = new CamelCaseNamingStrategy();
#pragma warning disable CS0618 // Type or member is obsolete
Assert.IsTrue(converter.CamelCaseText);
#pragma warning restore CS0618 // Type or member is obsolete
converter.NamingStrategy = new SnakeCaseNamingStrategy();
#pragma warning disable CS0618 // Type or member is obsolete
Assert.IsFalse(converter.CamelCaseText);
#pragma warning restore CS0618 // Type or member is obsolete
#pragma warning disable CS0618 // Type or member is obsolete
converter.CamelCaseText = false;
#pragma warning restore CS0618 // Type or member is obsolete
Assert.IsNotNull(converter.NamingStrategy);
Assert.AreEqual(typeof(SnakeCaseNamingStrategy), converter.NamingStrategy.GetType());
}
[Test]
public void StringEnumConverter_CamelCaseTextCtor()
{
#pragma warning disable CS0618 // Type or member is obsolete
StringEnumConverter converter = new StringEnumConverter(true);
#pragma warning restore CS0618 // Type or member is obsolete
Assert.IsNotNull(converter.NamingStrategy);
Assert.AreEqual(typeof(CamelCaseNamingStrategy), converter.NamingStrategy.GetType());
Assert.AreEqual(true, converter.AllowIntegerValues);
}
[Test]
public void StringEnumConverter_NamingStrategyTypeCtor()
{
StringEnumConverter converter = new StringEnumConverter(typeof(CamelCaseNamingStrategy), new object[] { true, true, true }, false);
Assert.IsNotNull(converter.NamingStrategy);
Assert.AreEqual(typeof(CamelCaseNamingStrategy), converter.NamingStrategy.GetType());
Assert.AreEqual(false, converter.AllowIntegerValues);
Assert.AreEqual(true, converter.NamingStrategy.OverrideSpecifiedNames);
Assert.AreEqual(true, converter.NamingStrategy.ProcessDictionaryKeys);
Assert.AreEqual(true, converter.NamingStrategy.ProcessExtensionDataNames);
}
[Test]
public void StringEnumConverter_NamingStrategyTypeCtor_Null()
{
ExceptionAssert.Throws<ArgumentNullException>(
() => new StringEnumConverter(null),
@"Value cannot be null.
Parameter name: namingStrategyType");
}
[Test]
public void StringEnumConverter_NamingStrategyTypeWithArgsCtor_Null()
{
ExceptionAssert.Throws<ArgumentNullException>(
() => new StringEnumConverter(null, new object[] { true, true, true }, false),
@"Value cannot be null.
Parameter name: namingStrategyType");
}
[Test]
public void Deserialize_CamelCaseFromAttribute_Obsolete()
{
CamelCaseEnumObsolete e = JsonConvert.DeserializeObject<CamelCaseEnumObsolete>(@"""camelCase""");
Assert.AreEqual(CamelCaseEnumObsolete.CamelCase, e);
}
[Test]
public void Serialize_CamelCaseFromAttribute()
{
string json = JsonConvert.SerializeObject(CamelCaseEnum.CamelCase);
string json = JsonConvert.SerializeObject(CamelCaseEnumNew.CamelCase);
Assert.AreEqual(@"""camelCase""", json);
}
[Test]
public void Deserialize_CamelCaseFromAttribute()
{
CamelCaseEnum e = JsonConvert.DeserializeObject<CamelCaseEnum>(@"""camelCase""");
Assert.AreEqual(CamelCaseEnum.CamelCase, e);
CamelCaseEnumNew e = JsonConvert.DeserializeObject<CamelCaseEnumNew>(@"""camelCase""");
Assert.AreEqual(CamelCaseEnumNew.CamelCase, e);
}
[Test]
public void Serialize_SnakeCaseFromAttribute()
{
string json = JsonConvert.SerializeObject(SnakeCaseEnumNew.SnakeCase);
Assert.AreEqual(@"""snake_case""", json);
}
[Test]
public void Deserialize_SnakeCaseFromAttribute()
{
SnakeCaseEnumNew e = JsonConvert.DeserializeObject<SnakeCaseEnumNew>(@"""snake_case""");
Assert.AreEqual(SnakeCaseEnumNew.SnakeCase, e);
}
[Test]
@@ -176,6 +311,17 @@ namespace Newtonsoft.Json.Tests.Converters
});
}
[Test]
public void CannotPassNullArgumentToConverter()
{
var ex = ExceptionAssert.Throws<JsonException>(() =>
{
JsonConvert.DeserializeObject<NullArgumentInAttribute>(@"""9""");
});
Assert.AreEqual("Cannot pass a null parameter to the constructor.", ex.InnerException.Message);
}
[Test]
public void Deserialize_AllowIntegerValuesAttribute()
{
@@ -311,7 +457,9 @@ namespace Newtonsoft.Json.Tests.Converters
NullableStoreColor2 = null
};
#pragma warning disable CS0618 // Type or member is obsolete
string json = JsonConvert.SerializeObject(enumClass, Formatting.Indented, new StringEnumConverter { CamelCaseText = true });
#pragma warning restore CS0618 // Type or member is obsolete
StringAssert.AreEqual(@"{
""StoreColor"": ""red"",
@@ -474,7 +622,9 @@ namespace Newtonsoft.Json.Tests.Converters
Enum = FlagsTestEnum.First | FlagsTestEnum.Second
};
#pragma warning disable CS0618 // Type or member is obsolete
string json = JsonConvert.SerializeObject(c, Formatting.Indented, new StringEnumConverter { CamelCaseText = true });
#pragma warning restore CS0618 // Type or member is obsolete
StringAssert.AreEqual(@"{
""Enum"": ""first, second""
}", json);
@@ -487,7 +637,9 @@ namespace Newtonsoft.Json.Tests.Converters
""Enum"": ""first, second""
}";
#pragma warning disable CS0618 // Type or member is obsolete
EnumContainer<FlagsTestEnum> c = JsonConvert.DeserializeObject<EnumContainer<FlagsTestEnum>>(json, new StringEnumConverter { CamelCaseText = true });
#pragma warning restore CS0618 // Type or member is obsolete
Assert.AreEqual(FlagsTestEnum.First | FlagsTestEnum.Second, c.Enum);
}
@@ -565,7 +717,9 @@ namespace Newtonsoft.Json.Tests.Converters
(Foo)int.MaxValue
};
#pragma warning disable CS0618 // Type or member is obsolete
string json1 = JsonConvert.SerializeObject(lfoo, Formatting.Indented, new StringEnumConverter { CamelCaseText = true });
#pragma warning restore CS0618 // Type or member is obsolete
StringAssert.AreEqual(@"[
""Bat, baz"",
@@ -588,7 +742,9 @@ namespace Newtonsoft.Json.Tests.Converters
List<Bar> lbar = new List<Bar>() { Bar.FooBar, Bar.Bat, Bar.SerializeAsBaz };
#pragma warning disable CS0618 // Type or member is obsolete
string json2 = JsonConvert.SerializeObject(lbar, Formatting.Indented, new StringEnumConverter { CamelCaseText = true });
#pragma warning restore CS0618 // Type or member is obsolete
StringAssert.AreEqual(@"[
""foo_bar"",
@@ -51,7 +51,7 @@ namespace Newtonsoft.Json.Tests.Utilities
{
Type enumType = expected.GetType();
Enum result = (Enum)EnumUtils.ParseEnum(enumType, value, false);
Enum result = (Enum)EnumUtils.ParseEnum(enumType, null, value, false);
Assert.AreEqual(expected, result);
}
@@ -63,7 +63,7 @@ namespace Newtonsoft.Json.Tests.Utilities
{
try
{
EnumUtils.ParseEnum(enumType, value, false);
EnumUtils.ParseEnum(enumType, null, value, false);
}
catch (Exception ex) when (ex.GetType() == exceptionType)
{
@@ -80,7 +80,7 @@ namespace Newtonsoft.Json.Tests.Utilities
[TestCaseSource(nameof(ToString_Format_TestData))]
public static void ToString_Format(Enum e, string expected)
{
EnumUtils.TryToString(e.GetType(), e, false, out string result);
EnumUtils.TryToString(e.GetType(), e, null, out string result);
Assert.AreEqual(expected, result);
}
@@ -31,6 +31,7 @@ using System.Globalization;
using System.Reflection;
using System.Runtime.Serialization;
using Newtonsoft.Json.Utilities;
using Newtonsoft.Json.Serialization;
#if !HAVE_LINQ
using Newtonsoft.Json.Utilities.LinqBridge;
#else
@@ -50,40 +51,121 @@ namespace Newtonsoft.Json.Converters
/// The default value is <c>false</c>.
/// </summary>
/// <value><c>true</c> if the written enum text will be camel case; otherwise, <c>false</c>.</value>
public bool CamelCaseText { get; set; }
[Obsolete("StringEnumConverter.CamelCaseText is obsolete. Set StringEnumConverter.NamingStrategy with CamelCaseNamingStrategy instead.")]
public bool CamelCaseText
{
get => NamingStrategy is CamelCaseNamingStrategy ? true : false;
set
{
if (value)
{
if (NamingStrategy is CamelCaseNamingStrategy)
{
return;
}
NamingStrategy = new CamelCaseNamingStrategy();
}
else
{
if (!(NamingStrategy is CamelCaseNamingStrategy))
{
return;
}
NamingStrategy = null;
}
}
}
/// <summary>
/// Gets or sets the naming strategy used to resolve how enum text is written.
/// </summary>
/// <value>The naming strategy used to resolve how enum text is written.</value>
public NamingStrategy NamingStrategy { get; set; }
/// <summary>
/// Gets or sets a value indicating whether integer values are allowed when serializing and deserializing.
/// The default value is <c>true</c>.
/// </summary>
/// <value><c>true</c> if integers are allowed when serializing and deserializing; otherwise, <c>false</c>.</value>
public bool AllowIntegerValues { get; set; }
public bool AllowIntegerValues { get; set; } = true;
/// <summary>
/// Initializes a new instance of the <see cref="StringEnumConverter"/> class.
/// </summary>
public StringEnumConverter()
{
AllowIntegerValues = true;
}
/// <summary>
/// Initializes a new instance of the <see cref="StringEnumConverter"/> class.
/// </summary>
/// <param name="camelCaseText"><c>true</c> if the written enum text will be camel case; otherwise, <c>false</c>.</param>
[Obsolete("StringEnumConverter(bool) is obsolete. Create a converter with StringEnumConverter(NamingStrategy, bool) instead.")]
public StringEnumConverter(bool camelCaseText)
: this()
{
CamelCaseText = camelCaseText;
if (camelCaseText)
{
NamingStrategy = new CamelCaseNamingStrategy();
}
}
/// <summary>
/// Initializes a new instance of the <see cref="StringEnumConverter"/> class.
/// </summary>
/// <param name="camelCaseText"><c>true</c> if the written enum text will be camel case; otherwise, <c>false</c>.</param>
/// <param name="namingStrategy">The naming strategy used to resolve how enum text is written.</param>
/// <param name="allowIntegerValues"><c>true</c> if integers are allowed when serializing and deserializing; otherwise, <c>false</c>.</param>
public StringEnumConverter(bool camelCaseText, bool allowIntegerValues) : this(camelCaseText)
public StringEnumConverter(NamingStrategy namingStrategy, bool allowIntegerValues = true)
{
NamingStrategy = namingStrategy;
AllowIntegerValues = allowIntegerValues;
}
/// <summary>
/// Initializes a new instance of the <see cref="StringEnumConverter"/> class.
/// </summary>
/// <param name="namingStrategyType">The <see cref="System.Type"/> of the <see cref="Newtonsoft.Json.Serialization.NamingStrategy"/> used to write enum text.</param>
public StringEnumConverter(Type namingStrategyType)
{
ValidationUtils.ArgumentNotNull(namingStrategyType, nameof(namingStrategyType));
NamingStrategy = JsonTypeReflector.CreateNamingStrategyInstance(namingStrategyType, null);
}
/// <summary>
/// Initializes a new instance of the <see cref="StringEnumConverter"/> class.
/// </summary>
/// <param name="namingStrategyType">The <see cref="System.Type"/> of the <see cref="Newtonsoft.Json.Serialization.NamingStrategy"/> used to write enum text.</param>
/// <param name="namingStrategyParameters">
/// The parameter list to use when constructing the <see cref="Newtonsoft.Json.Serialization.NamingStrategy"/> described by <paramref name="namingStrategyType"/>.
/// If <c>null</c>, the default constructor is used.
/// When non-<c>null</c>, there must be a constructor defined in the <see cref="Newtonsoft.Json.Serialization.NamingStrategy"/> that exactly matches the number,
/// order, and type of these parameters.
/// </param>
public StringEnumConverter(Type namingStrategyType, object[] namingStrategyParameters)
{
ValidationUtils.ArgumentNotNull(namingStrategyType, nameof(namingStrategyType));
NamingStrategy = JsonTypeReflector.CreateNamingStrategyInstance(namingStrategyType, namingStrategyParameters);
}
/// <summary>
/// Initializes a new instance of the <see cref="StringEnumConverter"/> class.
/// </summary>
/// <param name="namingStrategyType">The <see cref="System.Type"/> of the <see cref="Newtonsoft.Json.Serialization.NamingStrategy"/> used to write enum text.</param>
/// <param name="namingStrategyParameters">
/// The parameter list to use when constructing the <see cref="Newtonsoft.Json.Serialization.NamingStrategy"/> described by <paramref name="namingStrategyType"/>.
/// If <c>null</c>, the default constructor is used.
/// When non-<c>null</c>, there must be a constructor defined in the <see cref="Newtonsoft.Json.Serialization.NamingStrategy"/> that exactly matches the number,
/// order, and type of these parameters.
/// </param>
/// <param name="allowIntegerValues"><c>true</c> if integers are allowed when serializing and deserializing; otherwise, <c>false</c>.</param>
public StringEnumConverter(Type namingStrategyType, object[] namingStrategyParameters, bool allowIntegerValues)
{
ValidationUtils.ArgumentNotNull(namingStrategyType, nameof(namingStrategyType));
NamingStrategy = JsonTypeReflector.CreateNamingStrategyInstance(namingStrategyType, namingStrategyParameters);
AllowIntegerValues = allowIntegerValues;
}
@@ -103,7 +185,7 @@ namespace Newtonsoft.Json.Converters
Enum e = (Enum)value;
if (!EnumUtils.TryToString(e.GetType(), value, CamelCaseText, out string enumName))
if (!EnumUtils.TryToString(e.GetType(), value, NamingStrategy, out string enumName))
{
if (!AllowIntegerValues)
{
@@ -153,7 +235,7 @@ namespace Newtonsoft.Json.Converters
return null;
}
return EnumUtils.ParseEnum(t, enumText, !AllowIntegerValues);
return EnumUtils.ParseEnum(t, NamingStrategy, enumText, !AllowIntegerValues);
}
if (reader.TokenType == JsonToken.Integer)
+2 -2
View File
@@ -49,9 +49,9 @@ namespace Newtonsoft.Json
internal TypeNameHandling? _itemTypeNameHandling;
/// <summary>
/// Gets or sets the <see cref="JsonConverter"/> used when serializing the property's collection items.
/// Gets or sets the <see cref="JsonConverter"/> type used when serializing the property's collection items.
/// </summary>
/// <value>The collection's items <see cref="JsonConverter"/>.</value>
/// <value>The collection's items <see cref="JsonConverter"/> type.</value>
public Type ItemConverterType { get; set; }
/// <summary>
@@ -30,38 +30,6 @@ using Newtonsoft.Json.Utilities;
namespace Newtonsoft.Json.Serialization
{
internal readonly struct ResolverContractKey : IEquatable<ResolverContractKey>
{
private readonly Type _resolverType;
private readonly Type _contractType;
public ResolverContractKey(Type resolverType, Type contractType)
{
_resolverType = resolverType;
_contractType = contractType;
}
public override int GetHashCode()
{
return _resolverType.GetHashCode() ^ _contractType.GetHashCode();
}
public override bool Equals(object obj)
{
if (!(obj is ResolverContractKey key))
{
return false;
}
return Equals(key);
}
public bool Equals(ResolverContractKey other)
{
return (_resolverType == other._resolverType && _contractType == other._contractType);
}
}
/// <summary>
/// Resolves member mappings for a type, camel casing property names.
/// </summary>
@@ -69,7 +37,7 @@ namespace Newtonsoft.Json.Serialization
{
private static readonly object TypeContractCacheLock = new object();
private static readonly DefaultJsonNameTable NameTable = new DefaultJsonNameTable();
private static Dictionary<ResolverContractKey, JsonContract> _contractCache;
private static Dictionary<StructMultiKey<Type, Type>, JsonContract> _contractCache;
/// <summary>
/// Initializes a new instance of the <see cref="CamelCasePropertyNamesContractResolver"/> class.
@@ -96,8 +64,8 @@ namespace Newtonsoft.Json.Serialization
}
// for backwards compadibility the CamelCasePropertyNamesContractResolver shares contracts between instances
ResolverContractKey key = new ResolverContractKey(GetType(), type);
Dictionary<ResolverContractKey, JsonContract> cache = _contractCache;
StructMultiKey<Type, Type> key = new StructMultiKey<Type, Type>(GetType(), type);
Dictionary<StructMultiKey<Type, Type>, JsonContract> cache = _contractCache;
if (cache == null || !cache.TryGetValue(key, out JsonContract contract))
{
contract = CreateContract(type);
@@ -106,9 +74,9 @@ namespace Newtonsoft.Json.Serialization
lock (TypeContractCacheLock)
{
cache = _contractCache;
Dictionary<ResolverContractKey, JsonContract> updatedCache = (cache != null)
? new Dictionary<ResolverContractKey, JsonContract>(cache)
: new Dictionary<ResolverContractKey, JsonContract>();
Dictionary<StructMultiKey<Type, Type>, JsonContract> updatedCache = (cache != null)
? new Dictionary<StructMultiKey<Type, Type>, JsonContract>(cache)
: new Dictionary<StructMultiKey<Type, Type>, JsonContract>();
updatedCache[key] = contract;
_contractCache = updatedCache;
@@ -43,20 +43,20 @@ namespace Newtonsoft.Json.Serialization
{
internal static readonly DefaultSerializationBinder Instance = new DefaultSerializationBinder();
private readonly ThreadSafeStore<TypeNameKey, Type> _typeCache;
private readonly ThreadSafeStore<StructMultiKey<string, string>, Type> _typeCache;
/// <summary>
/// Initializes a new instance of the <see cref="DefaultSerializationBinder"/> class.
/// </summary>
public DefaultSerializationBinder()
{
_typeCache = new ThreadSafeStore<TypeNameKey, Type>(GetTypeFromTypeNameKey);
_typeCache = new ThreadSafeStore<StructMultiKey<string, string>, Type>(GetTypeFromTypeNameKey);
}
private Type GetTypeFromTypeNameKey(TypeNameKey typeNameKey)
private Type GetTypeFromTypeNameKey(StructMultiKey<string, string> typeNameKey)
{
string assemblyName = typeNameKey.AssemblyName;
string typeName = typeNameKey.TypeName;
string assemblyName = typeNameKey.Value1;
string typeName = typeNameKey.Value2;
if (assemblyName != null)
{
@@ -159,7 +159,7 @@ namespace Newtonsoft.Json.Serialization
{
string typeArgAssemblyQualifiedName = typeName.Substring(typeArgStartIndex, i - typeArgStartIndex);
TypeNameKey typeNameKey = ReflectionUtils.SplitFullyQualifiedTypeName(typeArgAssemblyQualifiedName);
StructMultiKey<string, string> typeNameKey = ReflectionUtils.SplitFullyQualifiedTypeName(typeArgAssemblyQualifiedName);
genericTypeArguments.Add(GetTypeByName(typeNameKey));
}
break;
@@ -173,7 +173,7 @@ namespace Newtonsoft.Json.Serialization
return type;
}
private Type GetTypeByName(TypeNameKey typeNameKey)
private Type GetTypeByName(StructMultiKey<string, string> typeNameKey)
{
return _typeCache.Get(typeNameKey);
}
@@ -188,7 +188,7 @@ namespace Newtonsoft.Json.Serialization
/// </returns>
public override Type BindToType(string assemblyName, string typeName)
{
return GetTypeByName(new TypeNameKey(assemblyName, typeName));
return GetTypeByName(new StructMultiKey<string, string>(assemblyName, typeName));
}
/// <summary>
@@ -777,12 +777,12 @@ namespace Newtonsoft.Json.Serialization
if (resolvedTypeNameHandling != TypeNameHandling.None)
{
TypeNameKey typeNameKey = ReflectionUtils.SplitFullyQualifiedTypeName(qualifiedTypeName);
StructMultiKey<string, string> typeNameKey = ReflectionUtils.SplitFullyQualifiedTypeName(qualifiedTypeName);
Type specifiedType;
try
{
specifiedType = Serializer._serializationBinder.BindToType(typeNameKey.AssemblyName, typeNameKey.TypeName);
specifiedType = Serializer._serializationBinder.BindToType(typeNameKey.Value1, typeNameKey.Value2);
}
catch (Exception ex)
{
@@ -955,7 +955,7 @@ namespace Newtonsoft.Json.Serialization
{
if (value is string s)
{
return EnumUtils.ParseEnum(contract.NonNullableUnderlyingType, s, false);
return EnumUtils.ParseEnum(contract.NonNullableUnderlyingType, null, s, false);
}
if (ConvertUtils.IsInteger(primitiveContract.TypeCode))
{
@@ -1150,7 +1150,7 @@ namespace Newtonsoft.Json.Serialization
{
escape = true;
if (primitiveContract.IsEnum && EnumUtils.TryToString(primitiveContract.NonNullableUnderlyingType, name, false, out string enumName))
if (primitiveContract.IsEnum && EnumUtils.TryToString(primitiveContract.NonNullableUnderlyingType, name, null, out string enumName))
{
return enumName;
}
@@ -198,18 +198,18 @@ namespace Newtonsoft.Json.Serialization
/// Lookup and create an instance of the <see cref="JsonConverter"/> type described by the argument.
/// </summary>
/// <param name="converterType">The <see cref="JsonConverter"/> type to create.</param>
/// <param name="converterArgs">Optional arguments to pass to an initializing constructor of the JsonConverter.
/// <param name="args">Optional arguments to pass to an initializing constructor of the JsonConverter.
/// If <c>null</c>, the default constructor is used.</param>
public static JsonConverter CreateJsonConverterInstance(Type converterType, object[] converterArgs)
public static JsonConverter CreateJsonConverterInstance(Type converterType, object[] args)
{
Func<object[], object> converterCreator = CreatorCache.Get(converterType);
return (JsonConverter)converterCreator(converterArgs);
return (JsonConverter)converterCreator(args);
}
public static NamingStrategy CreateNamingStrategyInstance(Type namingStrategyType, object[] converterArgs)
public static NamingStrategy CreateNamingStrategyInstance(Type namingStrategyType, object[] args)
{
Func<object[], object> converterCreator = CreatorCache.Get(namingStrategyType);
return (NamingStrategy)converterCreator(converterArgs);
return (NamingStrategy)converterCreator(args);
}
public static NamingStrategy GetContainerNamingStrategy(JsonContainerAttribute containerAttribute)
@@ -239,10 +239,18 @@ namespace Newtonsoft.Json.Serialization
{
if (parameters != null)
{
Type[] paramTypes = parameters.Select(param => param.GetType()).ToArray();
Type[] paramTypes = parameters.Select(param =>
{
if (param == null)
{
throw new InvalidOperationException("Cannot pass a null parameter to the constructor.");
}
return param.GetType();
}).ToArray();
ConstructorInfo parameterizedConstructorInfo = type.GetConstructor(paramTypes);
if (null != parameterizedConstructorInfo)
if (parameterizedConstructorInfo != null)
{
ObjectConstructor<object> parameterizedConstructor = ReflectionDelegateFactory.CreateParameterizedConstructor(parameterizedConstructorInfo);
return parameterizedConstructor(parameters);
+9 -40
View File
@@ -248,46 +248,15 @@ namespace Newtonsoft.Json.Utilities
#endif
}
internal readonly struct TypeConvertKey : IEquatable<TypeConvertKey>
private static readonly ThreadSafeStore<StructMultiKey<Type, Type>, Func<object, object>> CastConverters =
new ThreadSafeStore<StructMultiKey<Type, Type>, Func<object, object>>(CreateCastConverter);
private static Func<object, object> CreateCastConverter(StructMultiKey<Type, Type> t)
{
public Type InitialType { get; }
public Type TargetType { get; }
public TypeConvertKey(Type initialType, Type targetType)
{
InitialType = initialType;
TargetType = targetType;
}
public override int GetHashCode()
{
return InitialType.GetHashCode() ^ TargetType.GetHashCode();
}
public override bool Equals(object obj)
{
if (!(obj is TypeConvertKey key))
{
return false;
}
return Equals(key);
}
public bool Equals(TypeConvertKey other)
{
return (InitialType == other.InitialType && TargetType == other.TargetType);
}
}
private static readonly ThreadSafeStore<TypeConvertKey, Func<object, object>> CastConverters =
new ThreadSafeStore<TypeConvertKey, Func<object, object>>(CreateCastConverter);
private static Func<object, object> CreateCastConverter(TypeConvertKey t)
{
MethodInfo castMethodInfo = t.TargetType.GetMethod("op_Implicit", new[] { t.InitialType })
?? t.TargetType.GetMethod("op_Explicit", new[] { t.InitialType });
Type initialType = t.Value1;
Type targetType = t.Value2;
MethodInfo castMethodInfo = targetType.GetMethod("op_Implicit", new[] { initialType })
?? targetType.GetMethod("op_Explicit", new[] { initialType });
if (castMethodInfo == null)
{
@@ -630,7 +599,7 @@ namespace Newtonsoft.Json.Utilities
return value;
}
Func<object, object> castConverter = CastConverters.Get(new TypeConvertKey(valueType, targetType));
Func<object, object> castConverter = CastConverters.Get(new StructMultiKey<Type, Type>(valueType, targetType));
if (castConverter != null)
{
return castConverter(value);
+15 -20
View File
@@ -35,6 +35,7 @@ using System.Linq;
#endif
using System.Reflection;
using System.Text;
using Newtonsoft.Json.Serialization;
namespace Newtonsoft.Json.Utilities
{
@@ -43,10 +44,11 @@ namespace Newtonsoft.Json.Utilities
private const char EnumSeparatorChar = ',';
private const string EnumSeparatorString = ", ";
private static readonly ThreadSafeStore<Type, EnumInfo> ValuesAndNamesPerEnum = new ThreadSafeStore<Type, EnumInfo>(InitializeValuesAndNames);
private static readonly ThreadSafeStore<StructMultiKey<Type, NamingStrategy>, EnumInfo> ValuesAndNamesPerEnum = new ThreadSafeStore<StructMultiKey<Type, NamingStrategy>, EnumInfo>(InitializeValuesAndNames);
private static EnumInfo InitializeValuesAndNames(Type enumType)
private static EnumInfo InitializeValuesAndNames(StructMultiKey<Type, NamingStrategy> key)
{
Type enumType = key.Value1;
string[] names = Enum.GetNames(enumType);
string[] resolvedNames = new string[names.Length];
ulong[] values = new ulong[names.Length];
@@ -72,7 +74,9 @@ namespace Newtonsoft.Json.Utilities
resolvedName = name;
#endif
resolvedNames[i] = resolvedName;
resolvedNames[i] = key.Value2 != null
? key.Value2.GetPropertyName(resolvedName, false)
: resolvedName;
}
bool isFlags = enumType.IsDefined(typeof(FlagsAttribute), false);
@@ -113,9 +117,9 @@ namespace Newtonsoft.Json.Utilities
return selectedFlagsValues;
}
public static bool TryToString(Type enumType, object value, bool camelCaseText, out string name)
public static bool TryToString(Type enumType, object value, NamingStrategy namingStrategy, out string name)
{
EnumInfo enumInfo = ValuesAndNamesPerEnum.Get(enumType);
EnumInfo enumInfo = ValuesAndNamesPerEnum.Get(new StructMultiKey<Type, NamingStrategy>(enumType, namingStrategy));
ulong v = ToUInt64(value);
if (!enumInfo.IsFlags)
@@ -124,11 +128,6 @@ namespace Newtonsoft.Json.Utilities
if (index >= 0)
{
name = enumInfo.ResolvedNames[index];
if (camelCaseText)
{
name = StringUtils.ToCamelCase(name);
}
return true;
}
@@ -138,12 +137,12 @@ namespace Newtonsoft.Json.Utilities
}
else // These are flags OR'ed together (We treat everything as unsigned types)
{
name = InternalFlagsFormat(enumInfo, v, camelCaseText);
name = InternalFlagsFormat(enumInfo, v);
return name != null;
}
}
private static String InternalFlagsFormat(EnumInfo entry, ulong result, bool camelCaseText)
private static string InternalFlagsFormat(EnumInfo entry, ulong result)
{
string[] resolvedNames = entry.ResolvedNames;
ulong[] values = entry.Values;
@@ -172,7 +171,7 @@ namespace Newtonsoft.Json.Utilities
}
string resolvedName = resolvedNames[index];
sb.Insert(0, camelCaseText ? StringUtils.ToCamelCase(resolvedName) : resolvedName);
sb.Insert(0, resolvedName);
firstTime = false;
}
@@ -191,10 +190,6 @@ namespace Newtonsoft.Json.Utilities
if (values.Length > 0 && values[0] == 0)
{
returnString = resolvedNames[0]; // Zero was one of the enum values.
if (camelCaseText)
{
returnString = StringUtils.ToCamelCase(returnString);
}
}
else
{
@@ -211,7 +206,7 @@ namespace Newtonsoft.Json.Utilities
public static EnumInfo GetEnumValuesAndNames(Type enumType)
{
return ValuesAndNamesPerEnum.Get(enumType);
return ValuesAndNamesPerEnum.Get(new StructMultiKey<Type, NamingStrategy>(enumType, null));
}
private static ulong ToUInt64(object value)
@@ -247,7 +242,7 @@ namespace Newtonsoft.Json.Utilities
}
}
public static object ParseEnum(Type enumType, string value, bool disallowNumber)
public static object ParseEnum(Type enumType, NamingStrategy namingStrategy, string value, bool disallowNumber)
{
ValidationUtils.ArgumentNotNull(enumType, nameof(enumType));
ValidationUtils.ArgumentNotNull(value, nameof(value));
@@ -257,7 +252,7 @@ namespace Newtonsoft.Json.Utilities
throw new ArgumentException("Type provided must be an Enum.", nameof(enumType));
}
EnumInfo entry = ValuesAndNamesPerEnum.Get(enumType);
EnumInfo entry = ValuesAndNamesPerEnum.Get(new StructMultiKey<Type, NamingStrategy>(enumType, namingStrategy));
string[] enumNames = entry.Names;
string[] resolvedNames = entry.ResolvedNames;
ulong[] enumValues = entry.Values;
@@ -824,7 +824,7 @@ namespace Newtonsoft.Json.Utilities
}
#endif
public static TypeNameKey SplitFullyQualifiedTypeName(string fullyQualifiedTypeName)
public static StructMultiKey<string, string> SplitFullyQualifiedTypeName(string fullyQualifiedTypeName)
{
int? assemblyDelimiterIndex = GetAssemblyDelimiterIndex(fullyQualifiedTypeName);
@@ -842,7 +842,7 @@ namespace Newtonsoft.Json.Utilities
assemblyName = null;
}
return new TypeNameKey(assemblyName, typeName);
return new StructMultiKey<string, string>(assemblyName, typeName);
}
private static int? GetAssemblyDelimiterIndex(string fullyQualifiedTypeName)
@@ -1094,36 +1094,4 @@ namespace Newtonsoft.Json.Utilities
return Activator.CreateInstance(type);
}
}
internal readonly struct TypeNameKey : IEquatable<TypeNameKey>
{
internal readonly string AssemblyName;
internal readonly string TypeName;
public TypeNameKey(string assemblyName, string typeName)
{
AssemblyName = assemblyName;
TypeName = typeName;
}
public override int GetHashCode()
{
return (AssemblyName?.GetHashCode() ?? 0) ^ (TypeName?.GetHashCode() ?? 0);
}
public override bool Equals(object obj)
{
if (!(obj is TypeNameKey key))
{
return false;
}
return Equals(key);
}
public bool Equals(TypeNameKey other)
{
return (AssemblyName == other.AssemblyName && TypeName == other.TypeName);
}
}
}
@@ -0,0 +1,61 @@
#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;
namespace Newtonsoft.Json.Utilities
{
internal readonly struct StructMultiKey<T1, T2> : IEquatable<StructMultiKey<T1, T2>>
{
public readonly T1 Value1;
public readonly T2 Value2;
public StructMultiKey(T1 v1, T2 v2)
{
Value1 = v1;
Value2 = v2;
}
public override int GetHashCode()
{
return (Value1?.GetHashCode() ?? 0) ^ (Value2?.GetHashCode() ?? 0);
}
public override bool Equals(object obj)
{
if (!(obj is StructMultiKey<T1, T2> key))
{
return false;
}
return Equals(key);
}
public bool Equals(StructMultiKey<T1, T2> other)
{
return (Equals(Value1, other.Value1) && Equals(Value2, other.Value2));
}
}
}