-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());
|
}", 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
|
#endif
|
||||||
|
|
||||||
[Test]
|
[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;
|
Enum e = (Enum)value;
|
||||||
|
|
||||||
string enumName = e.ToString("G");
|
if (!EnumUtils.TryToString(e.GetType(), value, CamelCaseText, out string enumName))
|
||||||
|
|
||||||
if (char.IsNumber(enumName[0]) || enumName[0] == '-')
|
|
||||||
{
|
{
|
||||||
if (!AllowIntegerValues)
|
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
|
// enum value has no name so write number
|
||||||
@@ -107,11 +105,7 @@ namespace Newtonsoft.Json.Converters
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Type enumType = e.GetType();
|
writer.WriteValue(enumName);
|
||||||
|
|
||||||
string finalName = EnumUtils.ToEnumName(enumType, enumName, CamelCaseText);
|
|
||||||
|
|
||||||
writer.WriteValue(finalName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -293,7 +293,7 @@ namespace Newtonsoft.Json
|
|||||||
t = WriteNullAsync(cancellationToken);
|
t = WriteNullAsync(cancellationToken);
|
||||||
if (!t.IsCompletedSucessfully())
|
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);
|
t = WriteIndentAsync(cancellationToken);
|
||||||
if (!t.IsCompletedSucessfully())
|
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);
|
t = WriteEndAsync(token, cancellationToken);
|
||||||
if (!t.IsCompletedSucessfully())
|
if (!t.IsCompletedSucessfully())
|
||||||
{
|
{
|
||||||
return AwaitEnd(t, levelsToComplete, type, cancellationToken);
|
return AwaitEnd(t, levelsToComplete, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateCurrentState();
|
UpdateCurrentState();
|
||||||
@@ -321,7 +321,7 @@ namespace Newtonsoft.Json
|
|||||||
return AsyncUtils.CompletedTask;
|
return AsyncUtils.CompletedTask;
|
||||||
|
|
||||||
// Local functions, params renamed (capitalized) so as not to capture and allocate when calling async
|
// 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);
|
await task.ConfigureAwait(false);
|
||||||
|
|
||||||
@@ -338,10 +338,10 @@ namespace Newtonsoft.Json
|
|||||||
|
|
||||||
UpdateCurrentState();
|
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);
|
await task.ConfigureAwait(false);
|
||||||
|
|
||||||
@@ -351,10 +351,10 @@ namespace Newtonsoft.Json
|
|||||||
|
|
||||||
UpdateCurrentState();
|
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);
|
await task.ConfigureAwait(false);
|
||||||
|
|
||||||
@@ -362,10 +362,10 @@ namespace Newtonsoft.Json
|
|||||||
|
|
||||||
UpdateCurrentState();
|
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)
|
while (LevelsToComplete-- > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ namespace Newtonsoft.Json
|
|||||||
State[] errorStates = StateArrayTempate[0];
|
State[] errorStates = StateArrayTempate[0];
|
||||||
State[] valueStates = StateArrayTempate[7];
|
State[] valueStates = StateArrayTempate[7];
|
||||||
|
|
||||||
TypeValuesAndNames enumValuesAndNames = EnumUtils.GetEnumValuesAndNames(typeof(JsonToken));
|
EnumInfo enumValuesAndNames = EnumUtils.GetEnumValuesAndNames(typeof(JsonToken));
|
||||||
|
|
||||||
foreach (ulong valueToken in enumValuesAndNames.Values)
|
foreach (ulong valueToken in enumValuesAndNames.Values)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -309,7 +309,7 @@ namespace Newtonsoft.Json.Schema
|
|||||||
{
|
{
|
||||||
CurrentSchema.Enum = new List<JToken>();
|
CurrentSchema.Enum = new List<JToken>();
|
||||||
|
|
||||||
TypeValuesAndNames enumValues = EnumUtils.GetEnumValuesAndNames(type);
|
EnumInfo enumValues = EnumUtils.GetEnumValuesAndNames(type);
|
||||||
for (int i = 0; i < enumValues.Names.Length; i++)
|
for (int i = 0; i < enumValues.Names.Length; i++)
|
||||||
{
|
{
|
||||||
ulong v = enumValues.Values[i];
|
ulong v = enumValues.Values[i];
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
#if !HAVE_LINQ
|
#if !HAVE_LINQ
|
||||||
@@ -33,16 +34,18 @@ using Newtonsoft.Json.Utilities.LinqBridge;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
#endif
|
#endif
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Newtonsoft.Json.Utilities
|
namespace Newtonsoft.Json.Utilities
|
||||||
{
|
{
|
||||||
internal static class EnumUtils
|
internal static class EnumUtils
|
||||||
{
|
{
|
||||||
private const char EnumSeparatorChar = ',';
|
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[] names = Enum.GetNames(enumType);
|
||||||
string[] resolvedNames = new string[names.Length];
|
string[] resolvedNames = new string[names.Length];
|
||||||
@@ -72,7 +75,9 @@ namespace Newtonsoft.Json.Utilities
|
|||||||
resolvedNames[i] = resolvedName;
|
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
|
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());
|
Type underlyingType = Enum.GetUnderlyingType(value.GetType());
|
||||||
|
|
||||||
ulong num = ToUInt64(value);
|
ulong num = ToUInt64(value);
|
||||||
TypeValuesAndNames enumNameValues = GetEnumValuesAndNames(enumType);
|
EnumInfo enumNameValues = GetEnumValuesAndNames(enumType);
|
||||||
IList<T> selectedFlagsValues = new List<T>();
|
IList<T> selectedFlagsValues = new List<T>();
|
||||||
|
|
||||||
for (int i = 0; i < enumNameValues.Values.Length; i++)
|
for (int i = 0; i < enumNameValues.Values.Length; i++)
|
||||||
@@ -118,34 +123,103 @@ namespace Newtonsoft.Json.Utilities
|
|||||||
return ParseEnum(t, enumText, disallowValue);
|
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(',');
|
if (!enumInfo.IsFlags)
|
||||||
for (int i = 0; i < names.Length; i++)
|
|
||||||
{
|
{
|
||||||
string name = names[i].Trim();
|
int index = Array.BinarySearch(enumInfo.Values, v);
|
||||||
|
if (index >= 0)
|
||||||
int? matchingIndex = FindIndexByName(enumValuesAndNames.Names, name, 0, name.Length, StringComparison.Ordinal);
|
|
||||||
string resolvedEnumName = matchingIndex != null
|
|
||||||
? enumValuesAndNames.ResolvedNames[matchingIndex.Value]
|
|
||||||
: name;
|
|
||||||
|
|
||||||
if (camelCaseText)
|
|
||||||
{
|
{
|
||||||
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);
|
return ValuesAndNamesPerEnum.Get(enumType);
|
||||||
}
|
}
|
||||||
@@ -193,7 +267,7 @@ namespace Newtonsoft.Json.Utilities
|
|||||||
throw new ArgumentException("Type provided must be an Enum.", nameof(enumType));
|
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[] enumNames = entry.Names;
|
||||||
string[] resolvedNames = entry.ResolvedNames;
|
string[] resolvedNames = entry.ResolvedNames;
|
||||||
ulong[] enumValues = entry.Values;
|
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;
|
Values = values;
|
||||||
Names = names;
|
Names = names;
|
||||||
ResolvedNames = resolvedNames;
|
ResolvedNames = resolvedNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public readonly bool IsFlags;
|
||||||
public readonly ulong[] Values;
|
public readonly ulong[] Values;
|
||||||
public readonly string[] Names;
|
public readonly string[] Names;
|
||||||
public readonly string[] ResolvedNames;
|
public readonly string[] ResolvedNames;
|
||||||
|
|||||||
Reference in New Issue
Block a user