-Refactor enum parsing
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFrameworks>net46;netcoreapp2.0</TargetFrameworks>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<RootNamespace>Newtonsoft.Json.TestConsole</RootNamespace>
|
||||
<AssemblyName>Newtonsoft.Json.TestConsole</AssemblyName>
|
||||
<IsPackable>false</IsPackable>
|
||||
|
||||
@@ -72,6 +72,18 @@ namespace Newtonsoft.Json.Tests.Converters
|
||||
Positive = 1
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum NegativeFlagsEnum
|
||||
{
|
||||
NegativeFour = -4,
|
||||
NegativeTwo = -2,
|
||||
NegativeOne = -1,
|
||||
Zero = 0,
|
||||
One = 1,
|
||||
Two = 2,
|
||||
Four = 4
|
||||
}
|
||||
|
||||
#if !NET20
|
||||
public enum NamedEnum
|
||||
{
|
||||
@@ -115,6 +127,12 @@ namespace Newtonsoft.Json.Tests.Converters
|
||||
public NegativeEnum Value2 { get; set; }
|
||||
}
|
||||
|
||||
public class NegativeFlagsEnumClass
|
||||
{
|
||||
public NegativeFlagsEnum Value1 { get; set; }
|
||||
public NegativeFlagsEnum Value2 { get; set; }
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter), true)]
|
||||
public enum CamelCaseEnum
|
||||
{
|
||||
@@ -210,6 +228,13 @@ namespace Newtonsoft.Json.Tests.Converters
|
||||
Assert.AreEqual(NamedEnumWithComma.JustComma, c2.Enum);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NamedEnumCommaCaseInsensitiveTest()
|
||||
{
|
||||
EnumContainer<NamedEnumWithComma> c2 = JsonConvert.DeserializeObject<EnumContainer<NamedEnumWithComma>>(@"{""Enum"":"",THIRD""}", new StringEnumConverter());
|
||||
Assert.AreEqual(NamedEnumWithComma.Third, c2.Enum);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeserializeNameEnumTest()
|
||||
{
|
||||
@@ -297,6 +322,35 @@ namespace Newtonsoft.Json.Tests.Converters
|
||||
}", json);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SerializeNegativeFlagsEnum()
|
||||
{
|
||||
NegativeFlagsEnumClass negativeEnumClass = new NegativeFlagsEnumClass();
|
||||
negativeEnumClass.Value1 = NegativeFlagsEnum.NegativeFour | NegativeFlagsEnum.NegativeTwo;
|
||||
negativeEnumClass.Value2 = NegativeFlagsEnum.Two | NegativeFlagsEnum.Four;
|
||||
|
||||
string json = JsonConvert.SerializeObject(negativeEnumClass, Formatting.Indented, new StringEnumConverter());
|
||||
|
||||
StringAssert.AreEqual(@"{
|
||||
""Value1"": ""NegativeTwo"",
|
||||
""Value2"": ""Two, Four""
|
||||
}", json);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DeserializeNegativeFlagsEnum()
|
||||
{
|
||||
string json = @"{
|
||||
""Value1"": ""NegativeFour,NegativeTwo"",
|
||||
""Value2"": ""NegativeFour,Four""
|
||||
}";
|
||||
|
||||
NegativeFlagsEnumClass negativeEnumClass = JsonConvert.DeserializeObject<NegativeFlagsEnumClass>(json, new StringEnumConverter());
|
||||
|
||||
Assert.AreEqual(NegativeFlagsEnum.NegativeFour | NegativeFlagsEnum.NegativeTwo, negativeEnumClass.Value1);
|
||||
Assert.AreEqual(NegativeFlagsEnum.NegativeFour | NegativeFlagsEnum.Four, negativeEnumClass.Value2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SerializeNegativeEnum()
|
||||
{
|
||||
@@ -695,9 +749,7 @@ namespace Newtonsoft.Json.Tests.Converters
|
||||
|
||||
e = JsonConvert.DeserializeObject<EnumWithDifferentCases>(@"""m""", new StringEnumConverter());
|
||||
|
||||
// unfortunatly Enum.Parse with ignoreCase will find the first match rather than the best match
|
||||
// it would be great to find a way around this
|
||||
Assert.AreEqual(EnumWithDifferentCases.M, e);
|
||||
Assert.AreEqual(EnumWithDifferentCases.m, e);
|
||||
}
|
||||
|
||||
#if !NET20
|
||||
|
||||
@@ -44,6 +44,7 @@ using NUnit.Framework;
|
||||
#endif
|
||||
using Newtonsoft.Json;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json.Converters;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Tests.TestObjects;
|
||||
@@ -1213,7 +1214,7 @@ _____'propertyName': NaN,
|
||||
|
||||
var valueStates = JsonWriter.StateArrayTempate[7];
|
||||
|
||||
foreach (JsonToken valueToken in EnumUtils.GetValues(typeof(JsonToken)))
|
||||
foreach (JsonToken valueToken in GetValues(typeof(JsonToken)))
|
||||
{
|
||||
switch (valueToken)
|
||||
{
|
||||
@@ -1231,6 +1232,24 @@ _____'propertyName': NaN,
|
||||
}
|
||||
}
|
||||
|
||||
private static IList<object> GetValues(Type enumType)
|
||||
{
|
||||
if (!enumType.IsEnum())
|
||||
{
|
||||
throw new ArgumentException("Type {0} is not an enum.".FormatWith(CultureInfo.InvariantCulture, enumType.Name), nameof(enumType));
|
||||
}
|
||||
|
||||
List<object> values = new List<object>();
|
||||
|
||||
foreach (FieldInfo field in enumType.GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static))
|
||||
{
|
||||
object value = field.GetValue(enumType);
|
||||
values.Add(value);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DateTimeZoneHandling()
|
||||
{
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks Condition="'$(TestFrameworks)'==''">net46;net451;net452;net40;net35;net20;netcoreapp2.0;netcoreapp1.1;netcoreapp1.0</TargetFrameworks>
|
||||
<TargetFrameworks Condition="'$(TestFrameworks)'!=''">$(TestFrameworks)</TargetFrameworks>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<VersionPrefix>1.0</VersionPrefix>
|
||||
<Authors>James Newton-King</Authors>
|
||||
<Company>Newtonsoft</Company>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Newtonsoft.Json.Utilities;
|
||||
#if DNXCORE50
|
||||
|
||||
@@ -82,11 +82,14 @@ namespace Newtonsoft.Json
|
||||
State[] errorStates = StateArrayTempate[0];
|
||||
State[] valueStates = StateArrayTempate[7];
|
||||
|
||||
foreach (JsonToken valueToken in EnumUtils.GetValues(typeof(JsonToken)))
|
||||
TypeValuesAndNames enumValuesAndNames = EnumUtils.GetEnumValuesAndNames(typeof(JsonToken));
|
||||
|
||||
foreach (ulong valueToken in enumValuesAndNames.Values)
|
||||
{
|
||||
if (allStates.Count <= (int)valueToken)
|
||||
{
|
||||
switch (valueToken)
|
||||
JsonToken token = (JsonToken)valueToken;
|
||||
switch (token)
|
||||
{
|
||||
case JsonToken.Integer:
|
||||
case JsonToken.Float:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks Condition="'$(LibraryFrameworks)'==''">net45;net40;net35;net20;netstandard1.0;netstandard1.3;netstandard2.0;portable-net45+win8+wpa81+wp8;portable-net40+win8+wpa81+wp8+sl5</TargetFrameworks>
|
||||
<TargetFrameworks Condition="'$(LibraryFrameworks)'!=''">$(LibraryFrameworks)</TargetFrameworks>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<AssemblyVersion>10.0.0.0</AssemblyVersion>
|
||||
<FileVersion>10.0.1</FileVersion>
|
||||
<VersionPrefix>10.0.1</VersionPrefix>
|
||||
|
||||
@@ -309,10 +309,11 @@ namespace Newtonsoft.Json.Schema
|
||||
{
|
||||
CurrentSchema.Enum = new List<JToken>();
|
||||
|
||||
IList<EnumValue<long>> enumValues = EnumUtils.GetNamesAndValues<long>(type);
|
||||
foreach (EnumValue<long> enumValue in enumValues)
|
||||
TypeValuesAndNames enumValues = EnumUtils.GetEnumValuesAndNames(type);
|
||||
for (int i = 0; i < enumValues.Names.Length; i++)
|
||||
{
|
||||
JToken value = JToken.FromObject(enumValue.Value);
|
||||
ulong v = enumValues.Values[i];
|
||||
JToken value = JToken.FromObject(Enum.ToObject(type, v));
|
||||
|
||||
CurrentSchema.Enum.Add(value);
|
||||
}
|
||||
|
||||
@@ -38,37 +38,41 @@ namespace Newtonsoft.Json.Utilities
|
||||
{
|
||||
internal static class EnumUtils
|
||||
{
|
||||
private static readonly ThreadSafeStore<Type, BidirectionalDictionary<string, string>> EnumMemberNamesPerType = new ThreadSafeStore<Type, BidirectionalDictionary<string, string>>(InitializeEnumType);
|
||||
private const char EnumSeparatorChar = ',';
|
||||
|
||||
private static BidirectionalDictionary<string, string> InitializeEnumType(Type type)
|
||||
private static readonly ThreadSafeStore<Type, TypeValuesAndNames> ValuesAndNamesPerEnum = new ThreadSafeStore<Type, TypeValuesAndNames>(InitializeValuesAndNames);
|
||||
|
||||
private static TypeValuesAndNames InitializeValuesAndNames(Type enumType)
|
||||
{
|
||||
BidirectionalDictionary<string, string> map = new BidirectionalDictionary<string, string>(
|
||||
StringComparer.Ordinal,
|
||||
StringComparer.Ordinal);
|
||||
string[] names = Enum.GetNames(enumType);
|
||||
string[] resolvedNames = new string[names.Length];
|
||||
ulong[] values = new ulong[names.Length];
|
||||
|
||||
foreach (FieldInfo f in type.GetFields(BindingFlags.Public | BindingFlags.Static))
|
||||
for (int i = 0; i < names.Length; i++)
|
||||
{
|
||||
string n1 = f.Name;
|
||||
string n2;
|
||||
string name = names[i];
|
||||
FieldInfo f = enumType.GetField(name, BindingFlags.Public | BindingFlags.Static);
|
||||
values[i] = ToUInt64(f.GetValue(null));
|
||||
|
||||
string resolvedName;
|
||||
#if HAVE_DATA_CONTRACTS
|
||||
n2 = f.GetCustomAttributes(typeof(EnumMemberAttribute), true)
|
||||
.Cast<EnumMemberAttribute>()
|
||||
.Select(a => a.Value)
|
||||
.SingleOrDefault() ?? f.Name;
|
||||
resolvedName = f.GetCustomAttributes(typeof(EnumMemberAttribute), true)
|
||||
.Cast<EnumMemberAttribute>()
|
||||
.Select(a => a.Value)
|
||||
.SingleOrDefault() ?? f.Name;
|
||||
|
||||
if (Array.IndexOf(resolvedNames, resolvedName, 0, i) != -1)
|
||||
{
|
||||
throw new InvalidOperationException("Enum name '{0}' already exists on enum '{1}'.".FormatWith(CultureInfo.InvariantCulture, resolvedName, enumType.Name));
|
||||
}
|
||||
#else
|
||||
n2 = f.Name;
|
||||
resolvedName = name;
|
||||
#endif
|
||||
|
||||
if (map.TryGetBySecond(n2, out _))
|
||||
{
|
||||
throw new InvalidOperationException("Enum name '{0}' already exists on enum '{1}'.".FormatWith(CultureInfo.InvariantCulture, n2, type.Name));
|
||||
}
|
||||
|
||||
map.Set(n1, n2);
|
||||
resolvedNames[i] = resolvedName;
|
||||
}
|
||||
|
||||
return map;
|
||||
return new TypeValuesAndNames(values, names, resolvedNames);
|
||||
}
|
||||
|
||||
public static IList<T> GetFlagsValues<T>(T value) where T : struct
|
||||
@@ -82,19 +86,21 @@ namespace Newtonsoft.Json.Utilities
|
||||
|
||||
Type underlyingType = Enum.GetUnderlyingType(value.GetType());
|
||||
|
||||
ulong num = Convert.ToUInt64(value, CultureInfo.InvariantCulture);
|
||||
IList<EnumValue<ulong>> enumNameValues = GetNamesAndValues<T>();
|
||||
ulong num = ToUInt64(value);
|
||||
TypeValuesAndNames enumNameValues = GetEnumValuesAndNames(enumType);
|
||||
IList<T> selectedFlagsValues = new List<T>();
|
||||
|
||||
foreach (EnumValue<ulong> enumNameValue in enumNameValues)
|
||||
for (int i = 0; i < enumNameValues.Values.Length; i++)
|
||||
{
|
||||
if ((num & enumNameValue.Value) == enumNameValue.Value && enumNameValue.Value != 0)
|
||||
ulong v = enumNameValues.Values[i];
|
||||
|
||||
if ((num & v) == v && v != 0)
|
||||
{
|
||||
selectedFlagsValues.Add((T)Convert.ChangeType(enumNameValue.Value, underlyingType, CultureInfo.CurrentCulture));
|
||||
selectedFlagsValues.Add((T)Convert.ChangeType(v, underlyingType, CultureInfo.CurrentCulture));
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedFlagsValues.Count == 0 && enumNameValues.SingleOrDefault(v => v.Value == 0) != null)
|
||||
if (selectedFlagsValues.Count == 0 && enumNameValues.Values.Any(v => v == 0))
|
||||
{
|
||||
selectedFlagsValues.Add(default(T));
|
||||
}
|
||||
@@ -102,89 +108,6 @@ namespace Newtonsoft.Json.Utilities
|
||||
return selectedFlagsValues;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a dictionary of the names and values of an <see cref="Enum"/> type.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static IList<EnumValue<ulong>> GetNamesAndValues<T>() where T : struct
|
||||
{
|
||||
return GetNamesAndValues<ulong>(typeof(T));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a dictionary of the names and values of an Enum type.
|
||||
/// </summary>
|
||||
/// <param name="enumType">The enum type to get names and values for.</param>
|
||||
/// <returns></returns>
|
||||
public static IList<EnumValue<TUnderlyingType>> GetNamesAndValues<TUnderlyingType>(Type enumType) where TUnderlyingType : struct
|
||||
{
|
||||
if (enumType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(enumType));
|
||||
}
|
||||
|
||||
if (!enumType.IsEnum())
|
||||
{
|
||||
throw new ArgumentException("Type {0} is not an enum.".FormatWith(CultureInfo.InvariantCulture, enumType.Name), nameof(enumType));
|
||||
}
|
||||
|
||||
IList<object> enumValues = GetValues(enumType);
|
||||
IList<string> enumNames = GetNames(enumType);
|
||||
|
||||
IList<EnumValue<TUnderlyingType>> nameValues = new List<EnumValue<TUnderlyingType>>();
|
||||
|
||||
for (int i = 0; i < enumValues.Count; i++)
|
||||
{
|
||||
try
|
||||
{
|
||||
nameValues.Add(new EnumValue<TUnderlyingType>(enumNames[i], (TUnderlyingType)Convert.ChangeType(enumValues[i], typeof(TUnderlyingType), CultureInfo.CurrentCulture)));
|
||||
}
|
||||
catch (OverflowException e)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Value from enum with the underlying type of {0} cannot be added to dictionary with a value type of {1}. Value was too large: {2}".FormatWith(CultureInfo.InvariantCulture,
|
||||
Enum.GetUnderlyingType(enumType), typeof(TUnderlyingType), Convert.ToUInt64(enumValues[i], CultureInfo.InvariantCulture)), e);
|
||||
}
|
||||
}
|
||||
|
||||
return nameValues;
|
||||
}
|
||||
|
||||
public static IList<object> GetValues(Type enumType)
|
||||
{
|
||||
if (!enumType.IsEnum())
|
||||
{
|
||||
throw new ArgumentException("Type {0} is not an enum.".FormatWith(CultureInfo.InvariantCulture, enumType.Name), nameof(enumType));
|
||||
}
|
||||
|
||||
List<object> values = new List<object>();
|
||||
|
||||
foreach (FieldInfo field in enumType.GetFields(BindingFlags.Public | BindingFlags.Static))
|
||||
{
|
||||
object value = field.GetValue(enumType);
|
||||
values.Add(value);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
public static IList<string> GetNames(Type enumType)
|
||||
{
|
||||
if (!enumType.IsEnum())
|
||||
{
|
||||
throw new ArgumentException("Type {0} is not an enum.".FormatWith(CultureInfo.InvariantCulture, enumType.Name), nameof(enumType));
|
||||
}
|
||||
|
||||
List<string> values = new List<string>();
|
||||
|
||||
foreach (FieldInfo field in enumType.GetFields(BindingFlags.Public | BindingFlags.Static))
|
||||
{
|
||||
values.Add(field.Name);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
public static object ParseEnumName(string enumText, bool isNullable, bool disallowValue, Type t)
|
||||
{
|
||||
if (enumText == string.Empty && isNullable)
|
||||
@@ -192,56 +115,22 @@ namespace Newtonsoft.Json.Utilities
|
||||
return null;
|
||||
}
|
||||
|
||||
string finalEnumText;
|
||||
|
||||
BidirectionalDictionary<string, string> map = EnumMemberNamesPerType.Get(t);
|
||||
if (TryResolvedEnumName(map, enumText, out string resolvedEnumName))
|
||||
{
|
||||
finalEnumText = resolvedEnumName;
|
||||
}
|
||||
else if (enumText.IndexOf(',') != -1)
|
||||
{
|
||||
string[] names = enumText.Split(',');
|
||||
for (int i = 0; i < names.Length; i++)
|
||||
{
|
||||
string name = names[i].Trim();
|
||||
|
||||
names[i] = TryResolvedEnumName(map, name, out resolvedEnumName)
|
||||
? resolvedEnumName
|
||||
: name;
|
||||
}
|
||||
|
||||
finalEnumText = string.Join(", ", names);
|
||||
}
|
||||
else
|
||||
{
|
||||
finalEnumText = enumText;
|
||||
|
||||
if (disallowValue)
|
||||
{
|
||||
bool isNumber = int.TryParse(finalEnumText, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out int _);
|
||||
|
||||
if (isNumber)
|
||||
{
|
||||
throw new FormatException("Integer string '{0}' is not allowed.".FormatWith(CultureInfo.InvariantCulture, enumText));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Enum.Parse(t, finalEnumText, true);
|
||||
return ParseEnum(t, enumText, disallowValue);
|
||||
}
|
||||
|
||||
public static string ToEnumName(Type enumType, string enumText, bool camelCaseText)
|
||||
{
|
||||
BidirectionalDictionary<string, string> map = EnumMemberNamesPerType.Get(enumType);
|
||||
TypeValuesAndNames enumValuesAndNames = ValuesAndNamesPerEnum.Get(enumType);
|
||||
|
||||
string[] names = enumText.Split(',');
|
||||
for (int i = 0; i < names.Length; i++)
|
||||
{
|
||||
string name = names[i].Trim();
|
||||
|
||||
map.TryGetByFirst(name, out string resolvedEnumName);
|
||||
resolvedEnumName = resolvedEnumName ?? name;
|
||||
int? matchingIndex = FindIndexByName(enumValuesAndNames.Names, name, name.Length, 0, StringComparison.Ordinal);
|
||||
string resolvedEnumName = matchingIndex != null
|
||||
? enumValuesAndNames.ResolvedNames[matchingIndex.Value]
|
||||
: name;
|
||||
|
||||
if (camelCaseText)
|
||||
{
|
||||
@@ -256,15 +145,211 @@ namespace Newtonsoft.Json.Utilities
|
||||
return finalName;
|
||||
}
|
||||
|
||||
private static bool TryResolvedEnumName(BidirectionalDictionary<string, string> map, string enumText, out string resolvedEnumName)
|
||||
public static TypeValuesAndNames GetEnumValuesAndNames(Type enumType)
|
||||
{
|
||||
if (map.TryGetBySecond(enumText, out resolvedEnumName))
|
||||
return ValuesAndNamesPerEnum.Get(enumType);
|
||||
}
|
||||
|
||||
private static ulong ToUInt64(object value)
|
||||
{
|
||||
PrimitiveTypeCode typeCode = ConvertUtils.GetTypeCode(value.GetType(), out bool _);
|
||||
|
||||
switch (typeCode)
|
||||
{
|
||||
return true;
|
||||
case PrimitiveTypeCode.SByte:
|
||||
return (ulong)(sbyte)value;
|
||||
case PrimitiveTypeCode.Byte:
|
||||
return (byte)value;
|
||||
case PrimitiveTypeCode.Boolean:
|
||||
// direct cast from bool to byte is not allowed
|
||||
return Convert.ToByte((bool)value);
|
||||
case PrimitiveTypeCode.Int16:
|
||||
return (ulong)(short)value;
|
||||
case PrimitiveTypeCode.UInt16:
|
||||
return (ushort)value;
|
||||
case PrimitiveTypeCode.Char:
|
||||
return (char)value;
|
||||
case PrimitiveTypeCode.UInt32:
|
||||
return (uint)value;
|
||||
case PrimitiveTypeCode.Int32:
|
||||
return (ulong)(int)value;
|
||||
case PrimitiveTypeCode.UInt64:
|
||||
return (ulong)value;
|
||||
case PrimitiveTypeCode.Int64:
|
||||
return (ulong)(long)value;
|
||||
// All unsigned types will be directly cast
|
||||
default:
|
||||
throw new InvalidOperationException("Unknown enum type.");
|
||||
}
|
||||
}
|
||||
|
||||
private static object ParseEnum(Type enumType, string value, bool disallowNumber)
|
||||
{
|
||||
if (enumType == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(enumType));
|
||||
}
|
||||
|
||||
resolvedEnumName = null;
|
||||
return false;
|
||||
if (!enumType.IsEnum())
|
||||
{
|
||||
throw new ArgumentException("Type provided must be an Enum.", nameof(enumType));
|
||||
}
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(value));
|
||||
}
|
||||
|
||||
TypeValuesAndNames entry = ValuesAndNamesPerEnum.Get(enumType);
|
||||
string[] enumNames = entry.Names;
|
||||
string[] resolvedNames = entry.ResolvedNames;
|
||||
ulong[] enumValues = entry.Values;
|
||||
|
||||
// first check if the entire text (including commas) matches a resolved name
|
||||
int? matchingIndex = MatchName(value, enumNames, resolvedNames, 0, value.Length, StringComparison.Ordinal);
|
||||
if (matchingIndex != null)
|
||||
{
|
||||
return Enum.ToObject(enumType, enumValues[matchingIndex.Value]);
|
||||
}
|
||||
|
||||
int firstNonWhitespaceIndex = -1;
|
||||
for (int i = 0; i < value.Length; i++)
|
||||
{
|
||||
if (!char.IsWhiteSpace(value[i]))
|
||||
{
|
||||
firstNonWhitespaceIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (firstNonWhitespaceIndex == -1)
|
||||
{
|
||||
throw new ArgumentException("Must specify valid information for parsing in the string.");
|
||||
}
|
||||
|
||||
// first check whether string is a number
|
||||
char firstNonWhitespaceChar = value[firstNonWhitespaceIndex];
|
||||
if (char.IsDigit(firstNonWhitespaceChar) || firstNonWhitespaceChar == '-' || firstNonWhitespaceChar == '+')
|
||||
{
|
||||
Type underlyingType = Enum.GetUnderlyingType(enumType);
|
||||
|
||||
value = value.Trim();
|
||||
object temp = null;
|
||||
|
||||
try
|
||||
{
|
||||
temp = Convert.ChangeType(value, underlyingType, CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch (FormatException)
|
||||
{
|
||||
// We need to Parse this as a String instead. There are cases
|
||||
// when you tlbimp enums that can have values of the form "3D".
|
||||
// Don't fix this code.
|
||||
}
|
||||
|
||||
if (temp != null)
|
||||
{
|
||||
if (disallowNumber)
|
||||
{
|
||||
throw new FormatException("Integer string '{0}' is not allowed.".FormatWith(CultureInfo.InvariantCulture, value));
|
||||
}
|
||||
|
||||
return Enum.ToObject(enumType, temp);
|
||||
}
|
||||
}
|
||||
|
||||
ulong result = 0;
|
||||
|
||||
int valueIndex = firstNonWhitespaceIndex;
|
||||
while (valueIndex <= value.Length) // '=' is to handle invalid case of an ending comma
|
||||
{
|
||||
// Find the next separator, if there is one, otherwise the end of the string.
|
||||
int endIndex = value.IndexOf(EnumSeparatorChar, valueIndex);
|
||||
if (endIndex == -1)
|
||||
{
|
||||
endIndex = value.Length;
|
||||
}
|
||||
|
||||
// Shift the starting and ending indices to eliminate whitespace
|
||||
int endIndexNoWhitespace = endIndex;
|
||||
while (valueIndex < endIndex && char.IsWhiteSpace(value[valueIndex]))
|
||||
{
|
||||
valueIndex++;
|
||||
}
|
||||
|
||||
while (endIndexNoWhitespace > valueIndex && char.IsWhiteSpace(value[endIndexNoWhitespace - 1]))
|
||||
{
|
||||
endIndexNoWhitespace--;
|
||||
}
|
||||
int valueSubstringLength = endIndexNoWhitespace - valueIndex;
|
||||
|
||||
matchingIndex = MatchName(value, enumNames, resolvedNames, valueIndex, valueSubstringLength, StringComparison.Ordinal);
|
||||
|
||||
// if no match found, attempt case insensitive search
|
||||
if (matchingIndex == null)
|
||||
{
|
||||
matchingIndex = MatchName(value, enumNames, resolvedNames, valueIndex, valueSubstringLength, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
// If we couldn't find a match
|
||||
if (matchingIndex == null)
|
||||
{
|
||||
// before we throw an error, check whether the entire string has a case insensitive match against resolve names
|
||||
matchingIndex = MatchName(value, enumNames, resolvedNames, 0, value.Length, StringComparison.OrdinalIgnoreCase);
|
||||
if (matchingIndex != null)
|
||||
{
|
||||
return Enum.ToObject(enumType, enumValues[matchingIndex.Value]);
|
||||
}
|
||||
|
||||
// no match so error
|
||||
throw new ArgumentException("Requested value '{0}' was not found.".FormatWith(CultureInfo.InvariantCulture, value));
|
||||
}
|
||||
|
||||
result |= enumValues[matchingIndex.Value];
|
||||
|
||||
// Move our pointer to the ending index to go again.
|
||||
valueIndex = endIndex + 1;
|
||||
}
|
||||
|
||||
return Enum.ToObject(enumType, result);
|
||||
}
|
||||
|
||||
private static int? MatchName(string value, string[] enumNames, string[] resolvedNames, int valueIndex, int valueSubstringLength, StringComparison comparison)
|
||||
{
|
||||
int? matchingIndex = FindIndexByName(resolvedNames, value, valueSubstringLength, valueIndex, comparison);
|
||||
if (matchingIndex == null)
|
||||
{
|
||||
matchingIndex = FindIndexByName(enumNames, value, valueSubstringLength, valueIndex, comparison);
|
||||
}
|
||||
|
||||
return matchingIndex;
|
||||
}
|
||||
|
||||
private static int? FindIndexByName(string[] enumNames, string value, int valueSubstringLength, int valueIndex, StringComparison comparison)
|
||||
{
|
||||
for (int i = 0; i < enumNames.Length; i++)
|
||||
{
|
||||
if (enumNames[i].Length == valueSubstringLength &&
|
||||
string.Compare(enumNames[i], 0, value, valueIndex, valueSubstringLength, comparison) == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal class TypeValuesAndNames
|
||||
{
|
||||
public TypeValuesAndNames(ulong[] values, string[] names, string[] resolvedNames)
|
||||
{
|
||||
Values = values;
|
||||
Names = names;
|
||||
ResolvedNames = resolvedNames;
|
||||
}
|
||||
|
||||
public readonly ulong[] Values;
|
||||
public readonly string[] Names;
|
||||
public readonly string[] ResolvedNames;
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#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
|
||||
|
||||
namespace Newtonsoft.Json.Utilities
|
||||
{
|
||||
internal class EnumValue<T> where T : struct
|
||||
{
|
||||
public string Name { get; }
|
||||
public T Value { get; }
|
||||
|
||||
public EnumValue(string name, T value)
|
||||
{
|
||||
Name = name;
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -338,14 +338,14 @@ namespace Newtonsoft.Json.Utilities
|
||||
TestAccessibility(m, bindingFlags)).ToArray();
|
||||
}
|
||||
|
||||
public static MemberInfo GetField(this Type type, string member)
|
||||
public static FieldInfo GetField(this Type type, string member)
|
||||
{
|
||||
return type.GetField(member, DefaultFlags);
|
||||
}
|
||||
|
||||
public static MemberInfo GetField(this Type type, string member, BindingFlags bindingFlags)
|
||||
public static FieldInfo GetField(this Type type, string member, BindingFlags bindingFlags)
|
||||
{
|
||||
MemberInfo field = type.GetTypeInfo().GetDeclaredField(member);
|
||||
FieldInfo field = type.GetTypeInfo().GetDeclaredField(member);
|
||||
if (field == null || !TestAccessibility(field, bindingFlags))
|
||||
{
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user