-Refactored enum name writing
-Improved performance of reading and writing enum names -Tests
This commit is contained in:
@@ -201,6 +201,32 @@ namespace Newtonsoft.Json.Tests
|
||||
]
|
||||
}", sw.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task WriteLazy_Property()
|
||||
{
|
||||
LazyStringWriter sw = new LazyStringWriter(CultureInfo.InvariantCulture);
|
||||
|
||||
using (JsonTextWriter writer = new JsonTextWriter(sw))
|
||||
{
|
||||
writer.Indentation = 4;
|
||||
writer.Formatting = Formatting.Indented;
|
||||
|
||||
await writer.WriteStartArrayAsync();
|
||||
|
||||
await writer.WriteStartObjectAsync();
|
||||
|
||||
await writer.WritePropertyNameAsync("IncompleteProp");
|
||||
|
||||
await writer.WriteEndArrayAsync();
|
||||
}
|
||||
|
||||
StringAssert.AreEqual(@"[
|
||||
{
|
||||
""IncompleteProp"": null
|
||||
}
|
||||
]", sw.ToString());
|
||||
}
|
||||
#endif
|
||||
|
||||
[Test]
|
||||
|
||||
@@ -0,0 +1,311 @@
|
||||
#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
|
||||
|
||||
#if !(NET20 || NET35)
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json.Utilities;
|
||||
#if DNXCORE50
|
||||
using Xunit;
|
||||
using Test = Xunit.FactAttribute;
|
||||
using Assert = Newtonsoft.Json.Tests.XUnitAssert;
|
||||
using TestCaseSource = Xunit.MemberDataAttribute;
|
||||
#else
|
||||
using NUnit.Framework;
|
||||
#endif
|
||||
|
||||
namespace Newtonsoft.Json.Tests.Utilities
|
||||
{
|
||||
public class EnumUtilsTests : TestFixtureBase
|
||||
{
|
||||
#if DNXCORE50
|
||||
[Theory]
|
||||
#endif
|
||||
[TestCaseSource(nameof(Parse_TestData))]
|
||||
public void Parse(string value, object expected)
|
||||
{
|
||||
Type enumType = expected.GetType();
|
||||
|
||||
Enum result = (Enum)EnumUtils.ParseEnumName(value, false, false, enumType);
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
#if DNXCORE50
|
||||
[Theory]
|
||||
#endif
|
||||
[TestCaseSource(nameof(Parse_Invalid_TestData))]
|
||||
public void Parse_Invalid(Type enumType, string value, Type exceptionType)
|
||||
{
|
||||
try
|
||||
{
|
||||
EnumUtils.ParseEnumName(value, false, false, enumType);
|
||||
}
|
||||
catch (Exception ex) when (ex.GetType() == exceptionType)
|
||||
{
|
||||
// nom nom nom
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.Fail($"Expected {exceptionType.FullName} exception.");
|
||||
}
|
||||
|
||||
#if DNXCORE50
|
||||
[Theory]
|
||||
#endif
|
||||
[TestCaseSource(nameof(ToString_Format_TestData))]
|
||||
public static void ToString_Format(Enum e, string expected)
|
||||
{
|
||||
EnumUtils.TryToString(e.GetType(), e, false, out string result);
|
||||
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
#region Test data
|
||||
// test data from https://github.com/dotnet/corefx/blob/master/src/System.Runtime/tests/System/EnumTests.cs
|
||||
public static IEnumerable<object[]> Parse_TestData()
|
||||
{
|
||||
// SByte
|
||||
yield return new object[] { "Min", SByteEnum.Min };
|
||||
yield return new object[] { "mAx", SByteEnum.Max };
|
||||
yield return new object[] { "1", SByteEnum.One };
|
||||
yield return new object[] { "5", (SByteEnum)5 };
|
||||
|
||||
// Byte
|
||||
yield return new object[] { "Min", ByteEnum.Min };
|
||||
yield return new object[] { "mAx", ByteEnum.Max };
|
||||
yield return new object[] { "1", ByteEnum.One };
|
||||
yield return new object[] { "5", (ByteEnum)5 };
|
||||
|
||||
// Int16
|
||||
yield return new object[] { "Min", Int16Enum.Min };
|
||||
yield return new object[] { "mAx", Int16Enum.Max };
|
||||
yield return new object[] { "1", Int16Enum.One };
|
||||
yield return new object[] { "5", (Int16Enum)5 };
|
||||
|
||||
// UInt16
|
||||
yield return new object[] { "Min", UInt16Enum.Min };
|
||||
yield return new object[] { "mAx", UInt16Enum.Max };
|
||||
yield return new object[] { "1", UInt16Enum.One };
|
||||
yield return new object[] { "5", (UInt16Enum)5 };
|
||||
|
||||
// Int32
|
||||
yield return new object[] { "Min", Int32Enum.Min };
|
||||
yield return new object[] { "mAx", Int32Enum.Max };
|
||||
yield return new object[] { "1", Int32Enum.One };
|
||||
yield return new object[] { "5", (Int32Enum)5 };
|
||||
|
||||
// UInt32
|
||||
yield return new object[] { "Min", UInt32Enum.Min };
|
||||
yield return new object[] { "mAx", UInt32Enum.Max };
|
||||
yield return new object[] { "1", UInt32Enum.One };
|
||||
yield return new object[] { "5", (UInt32Enum)5 };
|
||||
|
||||
// Int64
|
||||
yield return new object[] { "Min", Int64Enum.Min };
|
||||
yield return new object[] { "mAx", Int64Enum.Max };
|
||||
yield return new object[] { "1", Int64Enum.One };
|
||||
yield return new object[] { "5", (Int64Enum)5 };
|
||||
|
||||
// UInt64
|
||||
yield return new object[] { "Min", UInt64Enum.Min };
|
||||
yield return new object[] { "mAx", UInt64Enum.Max };
|
||||
yield return new object[] { "1", UInt64Enum.One };
|
||||
yield return new object[] { "5", (UInt64Enum)5 };
|
||||
|
||||
// SimpleEnum
|
||||
yield return new object[] { "Red", SimpleEnum.Red };
|
||||
yield return new object[] { " Red", SimpleEnum.Red };
|
||||
yield return new object[] { "Red ", SimpleEnum.Red };
|
||||
yield return new object[] { " red ", SimpleEnum.Red };
|
||||
yield return new object[] { "B", SimpleEnum.B };
|
||||
yield return new object[] { "B,B", SimpleEnum.B };
|
||||
yield return new object[] { " Red , Blue ", SimpleEnum.Red | SimpleEnum.Blue };
|
||||
yield return new object[] { "Blue,Red,Green", SimpleEnum.Red | SimpleEnum.Blue | SimpleEnum.Green };
|
||||
yield return new object[] { "Blue,Red,Red,Red,Green", SimpleEnum.Red | SimpleEnum.Blue | SimpleEnum.Green };
|
||||
yield return new object[] { "Red,Blue, Green", SimpleEnum.Red | SimpleEnum.Blue | SimpleEnum.Green };
|
||||
yield return new object[] { "1", SimpleEnum.Red };
|
||||
yield return new object[] { " 1 ", SimpleEnum.Red };
|
||||
yield return new object[] { "2", SimpleEnum.Blue };
|
||||
yield return new object[] { "99", (SimpleEnum)99 };
|
||||
yield return new object[] { "-42", (SimpleEnum)(-42) };
|
||||
yield return new object[] { " -42", (SimpleEnum)(-42) };
|
||||
yield return new object[] { " -42 ", (SimpleEnum)(-42) };
|
||||
}
|
||||
|
||||
// test data from https://github.com/dotnet/corefx/blob/master/src/System.Runtime/tests/System/EnumTests.cs
|
||||
public static IEnumerable<object[]> Parse_Invalid_TestData()
|
||||
{
|
||||
// SimpleEnum
|
||||
yield return new object[] { null, "", typeof(ArgumentNullException) };
|
||||
yield return new object[] { typeof(SimpleEnum), null, typeof(ArgumentNullException) };
|
||||
yield return new object[] { typeof(object), "", typeof(ArgumentException) };
|
||||
yield return new object[] { typeof(SimpleEnum), "", typeof(ArgumentException) };
|
||||
yield return new object[] { typeof(SimpleEnum), " \t", typeof(ArgumentException) };
|
||||
yield return new object[] { typeof(SimpleEnum), "Purple", typeof(ArgumentException) };
|
||||
yield return new object[] { typeof(SimpleEnum), ",Red", typeof(ArgumentException) };
|
||||
yield return new object[] { typeof(SimpleEnum), "Red,", typeof(ArgumentException) };
|
||||
yield return new object[] { typeof(SimpleEnum), "B,", typeof(ArgumentException) };
|
||||
yield return new object[] { typeof(SimpleEnum), " , , ,", typeof(ArgumentException) };
|
||||
yield return new object[] { typeof(SimpleEnum), "Red,Blue,", typeof(ArgumentException) };
|
||||
yield return new object[] { typeof(SimpleEnum), "Red,,Blue", typeof(ArgumentException) };
|
||||
yield return new object[] { typeof(SimpleEnum), "Red,Blue, ", typeof(ArgumentException) };
|
||||
yield return new object[] { typeof(SimpleEnum), "Red Blue", typeof(ArgumentException) };
|
||||
yield return new object[] { typeof(SimpleEnum), "1,Blue", typeof(ArgumentException) };
|
||||
yield return new object[] { typeof(SimpleEnum), "Blue,1", typeof(ArgumentException) };
|
||||
yield return new object[] { typeof(SimpleEnum), "Blue, 1", typeof(ArgumentException) };
|
||||
yield return new object[] { typeof(SimpleEnum), "2147483649", typeof(OverflowException) };
|
||||
yield return new object[] { typeof(SimpleEnum), "2147483648", typeof(OverflowException) };
|
||||
}
|
||||
|
||||
// test data from https://github.com/dotnet/corefx/blob/master/src/System.Runtime/tests/System/EnumTests.cs
|
||||
public static IEnumerable<object[]> ToString_Format_TestData()
|
||||
{
|
||||
yield return new object[] { SByteEnum.Min, "Min" };
|
||||
yield return new object[] { (SByteEnum)5, null };
|
||||
yield return new object[] { SByteEnum.Max, "Max" };
|
||||
|
||||
yield return new object[] { ByteEnum.Min, "Min" };
|
||||
yield return new object[] { (ByteEnum)5, null };
|
||||
yield return new object[] { (ByteEnum)0xff, "Max" };
|
||||
yield return new object[] { (ByteEnum)3, null };
|
||||
|
||||
yield return new object[] { Int16Enum.Min, "Min" };
|
||||
yield return new object[] { (Int16Enum)5, null };
|
||||
yield return new object[] { Int16Enum.Max, "Max" };
|
||||
yield return new object[] { (Int16Enum)3, null };
|
||||
|
||||
yield return new object[] { UInt16Enum.Min, "Min" };
|
||||
yield return new object[] { (UInt16Enum)5, null };
|
||||
yield return new object[] { UInt16Enum.Max, "Max" };
|
||||
yield return new object[] { (UInt16Enum)3, null };
|
||||
|
||||
yield return new object[] { Int32Enum.Min, "Min" };
|
||||
yield return new object[] { (Int32Enum)5, null };
|
||||
yield return new object[] { Int32Enum.Max, "Max" };
|
||||
yield return new object[] { (Int32Enum)3, null };
|
||||
|
||||
yield return new object[] { UInt32Enum.Min, "Min" };
|
||||
yield return new object[] { (UInt32Enum)5, null };
|
||||
yield return new object[] { UInt32Enum.Max, "Max" };
|
||||
yield return new object[] { (UInt32Enum)3, null };
|
||||
|
||||
yield return new object[] { Int64Enum.Min, "Min" };
|
||||
yield return new object[] { (Int64Enum)5, null };
|
||||
yield return new object[] { Int64Enum.Max, "Max" };
|
||||
yield return new object[] { (Int64Enum)3, null };
|
||||
|
||||
yield return new object[] { UInt64Enum.Min, "Min" };
|
||||
yield return new object[] { (UInt64Enum)5, null };
|
||||
yield return new object[] { UInt64Enum.Max, "Max" };
|
||||
yield return new object[] { (UInt64Enum)3, null };
|
||||
|
||||
yield return new object[] { SimpleEnum.Red, "Red" };
|
||||
yield return new object[] { SimpleEnum.Blue, "Blue" };
|
||||
yield return new object[] { (SimpleEnum)99, null };
|
||||
yield return new object[] { (SimpleEnum)0, null };
|
||||
|
||||
yield return new object[] { AttributeTargets.Class | AttributeTargets.Delegate, "Class, Delegate" };
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public enum SimpleEnum
|
||||
{
|
||||
Red = 1,
|
||||
Blue = 2,
|
||||
Green = 3,
|
||||
Green_a = 3,
|
||||
Green_b = 3,
|
||||
B = 4
|
||||
}
|
||||
|
||||
public enum ByteEnum : byte
|
||||
{
|
||||
Min = byte.MinValue,
|
||||
One = 1,
|
||||
Two = 2,
|
||||
Max = byte.MaxValue,
|
||||
}
|
||||
|
||||
public enum SByteEnum : sbyte
|
||||
{
|
||||
Min = sbyte.MinValue,
|
||||
One = 1,
|
||||
Two = 2,
|
||||
Max = sbyte.MaxValue,
|
||||
}
|
||||
|
||||
public enum UInt16Enum : ushort
|
||||
{
|
||||
Min = ushort.MinValue,
|
||||
One = 1,
|
||||
Two = 2,
|
||||
Max = ushort.MaxValue,
|
||||
}
|
||||
|
||||
public enum Int16Enum : short
|
||||
{
|
||||
Min = short.MinValue,
|
||||
One = 1,
|
||||
Two = 2,
|
||||
Max = short.MaxValue,
|
||||
}
|
||||
|
||||
public enum UInt32Enum : uint
|
||||
{
|
||||
Min = uint.MinValue,
|
||||
One = 1,
|
||||
Two = 2,
|
||||
Max = uint.MaxValue,
|
||||
}
|
||||
|
||||
public enum Int32Enum : int
|
||||
{
|
||||
Min = int.MinValue,
|
||||
One = 1,
|
||||
Two = 2,
|
||||
Max = int.MaxValue,
|
||||
}
|
||||
|
||||
public enum UInt64Enum : ulong
|
||||
{
|
||||
Min = ulong.MinValue,
|
||||
One = 1,
|
||||
Two = 2,
|
||||
Max = ulong.MaxValue,
|
||||
}
|
||||
|
||||
public enum Int64Enum : long
|
||||
{
|
||||
Min = long.MinValue,
|
||||
One = 1,
|
||||
Two = 2,
|
||||
Max = long.MaxValue,
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -93,13 +93,11 @@ namespace Newtonsoft.Json.Converters
|
||||
|
||||
Enum e = (Enum)value;
|
||||
|
||||
string enumName = e.ToString("G");
|
||||
|
||||
if (char.IsNumber(enumName[0]) || enumName[0] == '-')
|
||||
if (!EnumUtils.TryToString(e.GetType(), value, CamelCaseText, out string enumName))
|
||||
{
|
||||
if (!AllowIntegerValues)
|
||||
{
|
||||
throw JsonSerializationException.Create(null, writer.ContainerPath, "Integer value {0} is not allowed.".FormatWith(CultureInfo.InvariantCulture, enumName), null);
|
||||
throw JsonSerializationException.Create(null, writer.ContainerPath, "Integer value {0} is not allowed.".FormatWith(CultureInfo.InvariantCulture, e.ToString("D")), null);
|
||||
}
|
||||
|
||||
// enum value has no name so write number
|
||||
@@ -107,11 +105,7 @@ namespace Newtonsoft.Json.Converters
|
||||
}
|
||||
else
|
||||
{
|
||||
Type enumType = e.GetType();
|
||||
|
||||
string finalName = EnumUtils.ToEnumName(enumType, enumName, CamelCaseText);
|
||||
|
||||
writer.WriteValue(finalName);
|
||||
writer.WriteValue(enumName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -293,7 +293,7 @@ namespace Newtonsoft.Json
|
||||
t = WriteNullAsync(cancellationToken);
|
||||
if (!t.IsCompletedSucessfully())
|
||||
{
|
||||
return AwaitProperty(t, levelsToComplete, token, type, cancellationToken);
|
||||
return AwaitProperty(t, levelsToComplete, token, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,7 +304,7 @@ namespace Newtonsoft.Json
|
||||
t = WriteIndentAsync(cancellationToken);
|
||||
if (!t.IsCompletedSucessfully())
|
||||
{
|
||||
return AwaitIndent(t, levelsToComplete, token, type, cancellationToken);
|
||||
return AwaitIndent(t, levelsToComplete, token, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,7 +312,7 @@ namespace Newtonsoft.Json
|
||||
t = WriteEndAsync(token, cancellationToken);
|
||||
if (!t.IsCompletedSucessfully())
|
||||
{
|
||||
return AwaitEnd(t, levelsToComplete, type, cancellationToken);
|
||||
return AwaitEnd(t, levelsToComplete, cancellationToken);
|
||||
}
|
||||
|
||||
UpdateCurrentState();
|
||||
@@ -321,7 +321,7 @@ namespace Newtonsoft.Json
|
||||
return AsyncUtils.CompletedTask;
|
||||
|
||||
// Local functions, params renamed (capitalized) so as not to capture and allocate when calling async
|
||||
async Task AwaitProperty(Task task, int LevelsToComplete, JsonToken token, JsonContainerType Type, CancellationToken CancellationToken)
|
||||
async Task AwaitProperty(Task task, int LevelsToComplete, JsonToken token, CancellationToken CancellationToken)
|
||||
{
|
||||
await task.ConfigureAwait(false);
|
||||
|
||||
@@ -338,10 +338,10 @@ namespace Newtonsoft.Json
|
||||
|
||||
UpdateCurrentState();
|
||||
|
||||
await AwaitRemaining(LevelsToComplete, Type, CancellationToken).ConfigureAwait(false);
|
||||
await AwaitRemaining(LevelsToComplete, CancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
async Task AwaitIndent(Task task, int LevelsToComplete, JsonToken token, JsonContainerType Type, CancellationToken CancellationToken)
|
||||
async Task AwaitIndent(Task task, int LevelsToComplete, JsonToken token, CancellationToken CancellationToken)
|
||||
{
|
||||
await task.ConfigureAwait(false);
|
||||
|
||||
@@ -351,10 +351,10 @@ namespace Newtonsoft.Json
|
||||
|
||||
UpdateCurrentState();
|
||||
|
||||
await AwaitRemaining(LevelsToComplete, Type, CancellationToken).ConfigureAwait(false);
|
||||
await AwaitRemaining(LevelsToComplete, CancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
async Task AwaitEnd(Task task, int LevelsToComplete, JsonContainerType Type, CancellationToken CancellationToken)
|
||||
async Task AwaitEnd(Task task, int LevelsToComplete, CancellationToken CancellationToken)
|
||||
{
|
||||
await task.ConfigureAwait(false);
|
||||
|
||||
@@ -362,10 +362,10 @@ namespace Newtonsoft.Json
|
||||
|
||||
UpdateCurrentState();
|
||||
|
||||
await AwaitRemaining(LevelsToComplete, Type, CancellationToken).ConfigureAwait(false);
|
||||
await AwaitRemaining(LevelsToComplete, CancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
async Task AwaitRemaining(int LevelsToComplete, JsonContainerType Type, CancellationToken CancellationToken)
|
||||
async Task AwaitRemaining(int LevelsToComplete, CancellationToken CancellationToken)
|
||||
{
|
||||
while (LevelsToComplete-- > 0)
|
||||
{
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace Newtonsoft.Json
|
||||
State[] errorStates = StateArrayTempate[0];
|
||||
State[] valueStates = StateArrayTempate[7];
|
||||
|
||||
TypeValuesAndNames enumValuesAndNames = EnumUtils.GetEnumValuesAndNames(typeof(JsonToken));
|
||||
EnumInfo enumValuesAndNames = EnumUtils.GetEnumValuesAndNames(typeof(JsonToken));
|
||||
|
||||
foreach (ulong valueToken in enumValuesAndNames.Values)
|
||||
{
|
||||
|
||||
@@ -309,7 +309,7 @@ namespace Newtonsoft.Json.Schema
|
||||
{
|
||||
CurrentSchema.Enum = new List<JToken>();
|
||||
|
||||
TypeValuesAndNames enumValues = EnumUtils.GetEnumValuesAndNames(type);
|
||||
EnumInfo enumValues = EnumUtils.GetEnumValuesAndNames(type);
|
||||
for (int i = 0; i < enumValues.Names.Length; i++)
|
||||
{
|
||||
ulong v = enumValues.Values[i];
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Runtime.Serialization;
|
||||
#if !HAVE_LINQ
|
||||
@@ -33,16 +34,18 @@ using Newtonsoft.Json.Utilities.LinqBridge;
|
||||
using System.Linq;
|
||||
#endif
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace Newtonsoft.Json.Utilities
|
||||
{
|
||||
internal static class EnumUtils
|
||||
{
|
||||
private const char EnumSeparatorChar = ',';
|
||||
private const string EnumSeparatorString = ", ";
|
||||
|
||||
private static readonly ThreadSafeStore<Type, TypeValuesAndNames> ValuesAndNamesPerEnum = new ThreadSafeStore<Type, TypeValuesAndNames>(InitializeValuesAndNames);
|
||||
private static readonly ThreadSafeStore<Type, EnumInfo> ValuesAndNamesPerEnum = new ThreadSafeStore<Type, EnumInfo>(InitializeValuesAndNames);
|
||||
|
||||
private static TypeValuesAndNames InitializeValuesAndNames(Type enumType)
|
||||
private static EnumInfo InitializeValuesAndNames(Type enumType)
|
||||
{
|
||||
string[] names = Enum.GetNames(enumType);
|
||||
string[] resolvedNames = new string[names.Length];
|
||||
@@ -72,7 +75,9 @@ namespace Newtonsoft.Json.Utilities
|
||||
resolvedNames[i] = resolvedName;
|
||||
}
|
||||
|
||||
return new TypeValuesAndNames(values, names, resolvedNames);
|
||||
bool isFlags = enumType.IsDefined(typeof(FlagsAttribute), false);
|
||||
|
||||
return new EnumInfo(isFlags, values, names, resolvedNames);
|
||||
}
|
||||
|
||||
public static IList<T> GetFlagsValues<T>(T value) where T : struct
|
||||
@@ -87,7 +92,7 @@ namespace Newtonsoft.Json.Utilities
|
||||
Type underlyingType = Enum.GetUnderlyingType(value.GetType());
|
||||
|
||||
ulong num = ToUInt64(value);
|
||||
TypeValuesAndNames enumNameValues = GetEnumValuesAndNames(enumType);
|
||||
EnumInfo enumNameValues = GetEnumValuesAndNames(enumType);
|
||||
IList<T> selectedFlagsValues = new List<T>();
|
||||
|
||||
for (int i = 0; i < enumNameValues.Values.Length; i++)
|
||||
@@ -118,34 +123,103 @@ namespace Newtonsoft.Json.Utilities
|
||||
return ParseEnum(t, enumText, disallowValue);
|
||||
}
|
||||
|
||||
public static string ToEnumName(Type enumType, string enumText, bool camelCaseText)
|
||||
public static bool TryToString(Type enumType, object value, bool camelCaseText, out string name)
|
||||
{
|
||||
TypeValuesAndNames enumValuesAndNames = ValuesAndNamesPerEnum.Get(enumType);
|
||||
EnumInfo enumInfo = ValuesAndNamesPerEnum.Get(enumType);
|
||||
ulong v = ToUInt64(value);
|
||||
|
||||
string[] names = enumText.Split(',');
|
||||
for (int i = 0; i < names.Length; i++)
|
||||
if (!enumInfo.IsFlags)
|
||||
{
|
||||
string name = names[i].Trim();
|
||||
|
||||
int? matchingIndex = FindIndexByName(enumValuesAndNames.Names, name, 0, name.Length, StringComparison.Ordinal);
|
||||
string resolvedEnumName = matchingIndex != null
|
||||
? enumValuesAndNames.ResolvedNames[matchingIndex.Value]
|
||||
: name;
|
||||
|
||||
if (camelCaseText)
|
||||
int index = Array.BinarySearch(enumInfo.Values, v);
|
||||
if (index >= 0)
|
||||
{
|
||||
resolvedEnumName = StringUtils.ToCamelCase(resolvedEnumName);
|
||||
name = enumInfo.ResolvedNames[index];
|
||||
if (camelCaseText)
|
||||
{
|
||||
name = StringUtils.ToCamelCase(name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
names[i] = resolvedEnumName;
|
||||
// is number value
|
||||
name = null;
|
||||
return false;
|
||||
}
|
||||
else // These are flags OR'ed together (We treat everything as unsigned types)
|
||||
{
|
||||
name = InternalFlagsFormat(enumInfo, v, camelCaseText);
|
||||
return name != null;
|
||||
}
|
||||
|
||||
string finalName = string.Join(", ", names);
|
||||
|
||||
return finalName;
|
||||
}
|
||||
|
||||
public static TypeValuesAndNames GetEnumValuesAndNames(Type enumType)
|
||||
private static String InternalFlagsFormat(EnumInfo entry, ulong result, bool camelCaseText)
|
||||
{
|
||||
string[] resolvedNames = entry.ResolvedNames;
|
||||
ulong[] values = entry.Values;
|
||||
|
||||
int index = values.Length - 1;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
bool firstTime = true;
|
||||
ulong saveResult = result;
|
||||
|
||||
// We will not optimize this code further to keep it maintainable. There are some boundary checks that can be applied
|
||||
// to minimize the comparsions required. This code works the same for the best/worst case. In general the number of
|
||||
// items in an enum are sufficiently small and not worth the optimization.
|
||||
while (index >= 0)
|
||||
{
|
||||
if (index == 0 && values[index] == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if ((result & values[index]) == values[index])
|
||||
{
|
||||
result -= values[index];
|
||||
if (!firstTime)
|
||||
{
|
||||
sb.Insert(0, EnumSeparatorString);
|
||||
}
|
||||
|
||||
string resolvedName = resolvedNames[index];
|
||||
sb.Insert(0, camelCaseText ? StringUtils.ToCamelCase(resolvedName) : resolvedName);
|
||||
firstTime = false;
|
||||
}
|
||||
|
||||
index--;
|
||||
}
|
||||
|
||||
string returnString;
|
||||
if (result != 0)
|
||||
{
|
||||
// We were unable to represent this number as a bitwise or of valid flags
|
||||
returnString = null; // return null so the caller knows to .ToString() the input
|
||||
}
|
||||
else if (saveResult == 0)
|
||||
{
|
||||
// For the cases when we have zero
|
||||
if (values.Length > 0 && values[0] == 0)
|
||||
{
|
||||
returnString = resolvedNames[0]; // Zero was one of the enum values.
|
||||
if (camelCaseText)
|
||||
{
|
||||
returnString = StringUtils.ToCamelCase(returnString);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
returnString = null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
returnString = sb.ToString(); // Return the string representation
|
||||
}
|
||||
|
||||
return returnString;
|
||||
}
|
||||
|
||||
public static EnumInfo GetEnumValuesAndNames(Type enumType)
|
||||
{
|
||||
return ValuesAndNamesPerEnum.Get(enumType);
|
||||
}
|
||||
@@ -193,7 +267,7 @@ namespace Newtonsoft.Json.Utilities
|
||||
throw new ArgumentException("Type provided must be an Enum.", nameof(enumType));
|
||||
}
|
||||
|
||||
TypeValuesAndNames entry = ValuesAndNamesPerEnum.Get(enumType);
|
||||
EnumInfo entry = ValuesAndNamesPerEnum.Get(enumType);
|
||||
string[] enumNames = entry.Names;
|
||||
string[] resolvedNames = entry.ResolvedNames;
|
||||
ulong[] enumValues = entry.Values;
|
||||
@@ -333,15 +407,17 @@ namespace Newtonsoft.Json.Utilities
|
||||
}
|
||||
}
|
||||
|
||||
internal class TypeValuesAndNames
|
||||
internal class EnumInfo
|
||||
{
|
||||
public TypeValuesAndNames(ulong[] values, string[] names, string[] resolvedNames)
|
||||
public EnumInfo(bool isFlags, ulong[] values, string[] names, string[] resolvedNames)
|
||||
{
|
||||
IsFlags = isFlags;
|
||||
Values = values;
|
||||
Names = names;
|
||||
ResolvedNames = resolvedNames;
|
||||
}
|
||||
|
||||
public readonly bool IsFlags;
|
||||
public readonly ulong[] Values;
|
||||
public readonly string[] Names;
|
||||
public readonly string[] ResolvedNames;
|
||||
|
||||
Reference in New Issue
Block a user