-Refactor

This commit is contained in:
James Newton-King
2014-11-24 22:01:53 +13:00
parent b5ef767774
commit 04e9048d10
9 changed files with 259 additions and 91 deletions
@@ -33,4 +33,4 @@ using System.Runtime.InteropServices;
// by using the '*' as shown below:
// [assembly: AssemblyVersion("6.0.0.0")]
[assembly: AssemblyVersion("6.0.0.0")]
[assembly: AssemblyFileVersion("6.0.6.17911")]
[assembly: AssemblyFileVersion("6.0.6.17924")]
@@ -445,6 +445,14 @@ namespace Newtonsoft.Json.Tests.Converters
Assert.AreEqual(Bar.SerializeAsBaz, bars[2]);
}
[Test]
public void DuplicateNameEnumTest()
{
ExceptionAssert.Throws<JsonSerializationException>(
() => JsonConvert.DeserializeObject<DuplicateNameEnum>("'foo_bar'", new StringEnumConverter()),
@"Error converting value ""foo_bar"" to type 'Newtonsoft.Json.Tests.Converters.DuplicateNameEnum'. Path '', line 1, position 9.");
}
// Define other methods and classes here
[Flags]
[JsonConverter(typeof(StringEnumConverter))]
@@ -468,6 +476,74 @@ namespace Newtonsoft.Json.Tests.Converters
[EnumMember(Value = "baz")]
SerializeAsBaz
}
[Test]
public void DataContractSerializerDuplicateNameEnumTest()
{
MemoryStream ms = new MemoryStream();
var s = new DataContractSerializer(typeof(DuplicateEnumNameTestClass));
ExceptionAssert.Throws<InvalidDataContractException>(() =>
{
s.WriteObject(ms, new DuplicateEnumNameTestClass
{
Value = DuplicateNameEnum.foo_bar,
Value2 = DuplicateNameEnum2.foo_bar_NOT_USED
});
Console.WriteLine(Encoding.UTF8.GetString(ms.ToArray()));
string xml = @"<DuplicateEnumNameTestClass xmlns=""http://schemas.datacontract.org/2004/07/Newtonsoft.Json.Tests.Converters"" xmlns:i=""http://www.w3.org/2001/XMLSchema-instance"">
<Value>foo_bar</Value>
<Value2>foo_bar</Value2>
</DuplicateEnumNameTestClass>";
Console.WriteLine(xml);
var o = (DuplicateEnumNameTestClass)s.ReadObject(new MemoryStream(Encoding.UTF8.GetBytes(xml)));
Assert.AreEqual(DuplicateNameEnum.foo_bar, o.Value);
Assert.AreEqual(DuplicateNameEnum2.FooBar, o.Value2);
}, "Type 'Newtonsoft.Json.Tests.Converters.DuplicateNameEnum' contains two members 'foo_bar' 'and 'FooBar' with the same name 'foo_bar'. Multiple members with the same name in one type are not supported. Consider changing one of the member names using EnumMemberAttribute attribute.");
}
#endif
}
#if !NET20
[DataContract]
public class DuplicateEnumNameTestClass
{
[DataMember]
public DuplicateNameEnum Value { get; set; }
[DataMember]
public DuplicateNameEnum2 Value2 { get; set; }
}
[DataContract]
public enum DuplicateNameEnum
{
[EnumMember]
first = 0,
[EnumMember]
foo_bar = 1,
[EnumMember(Value = "foo_bar")]
FooBar = 2,
[EnumMember]
foo_bar_NOT_USED = 3
}
[DataContract]
public enum DuplicateNameEnum2
{
[EnumMember]
first = 0,
[EnumMember(Value = "foo_bar")]
FooBar = 1,
[EnumMember]
foo_bar = 2,
[EnumMember(Value = "TEST")]
foo_bar_NOT_USED = 3
}
#endif
}
@@ -23,6 +23,7 @@
// OTHER DEALINGS IN THE SOFTWARE.
#endregion
using System.Diagnostics;
#if !(NET35 || NET20 || PORTABLE40)
using System;
using System.Collections.Generic;
@@ -52,6 +53,54 @@ namespace Newtonsoft.Json.Tests.Linq
[TestFixture]
public class DynamicTests : TestFixtureBase
{
private void UpdateValueCount(IDictionary<string, int> counts, dynamic d)
{
string s = d.ToString();
int c;
if (!counts.TryGetValue(s, out c))
{
c = 0;
}
c++;
counts[s] = c;
}
[Test]
public void DeserializeLargeDynamic()
{
dynamic d;
using (var jsonFile = System.IO.File.OpenText("large.json"))
using (JsonTextReader jsonTextReader = new JsonTextReader(jsonFile))
{
JsonSerializer serializer = new JsonSerializer();
d = serializer.Deserialize(jsonTextReader);
}
IDictionary<string, int> counts = new Dictionary<string, int>();
Stopwatch sw = new Stopwatch();
sw.Start();
foreach (dynamic o in d)
{
foreach (dynamic friend in o.friends)
{
UpdateValueCount(counts, friend.id);
UpdateValueCount(counts, ((string) friend.name).Split(' ')[0]);
}
}
//foreach (KeyValuePair<string, int> keyValuePair in counts)
//{
// Console.WriteLine(keyValuePair.Key + ": " + keyValuePair.Value);
//}
Console.WriteLine("Time (secs): " + sw.Elapsed.TotalSeconds);
}
[Test]
public void JObjectPropertyNames()
{
@@ -26,6 +26,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using Newtonsoft.Json.Tests.TestObjects;
#if !(NET20 || NET35 || PORTABLE || ASPNETCORE50)
using System.Numerics;
@@ -792,6 +793,20 @@ namespace Newtonsoft.Json.Tests.Linq
Assert.AreEqual(JTokenType.Integer, v.Type);
StringComparison e6 = v.ToObject<StringComparison>();
Assert.AreEqual(StringComparison.OrdinalIgnoreCase, e6);
// does not support EnumMember. breaking change to add
ExceptionAssert.Throws<ArgumentException>(() =>
{
d = new JValue("value_a");
EnumA e7 = (EnumA)d;
Assert.AreEqual(EnumA.ValueA, e7);
}, "Requested value 'value_a' was not found.");
}
public enum EnumA
{
[EnumMember(Value = "value_a")]
ValueA
}
#endif
}
@@ -74,4 +74,4 @@ using System.Security;
// by using the '*' as shown below:
[assembly: AssemblyVersion("6.0.0.0")]
[assembly: AssemblyFileVersion("6.0.6.17911")]
[assembly: AssemblyFileVersion("6.0.6.17924")]
@@ -95,6 +95,24 @@ namespace Newtonsoft.Json.Tests.Serialization
[TestFixture]
public class JsonSerializerTest : TestFixtureBase
{
#if !NET20
public enum EnumA
{
[EnumMember(Value = "value_a")]
ValueA
}
[Test]
public void DeserializeEnumsByName()
{
var e1 = JsonConvert.DeserializeObject<EnumA>("'ValueA'");
Assert.AreEqual(EnumA.ValueA, e1);
var e2 = JsonConvert.DeserializeObject<EnumA>("'value_a'", new StringEnumConverter());
Assert.AreEqual(EnumA.ValueA, e2);
}
#endif
#if !(NETFX_CORE || ASPNETCORE50 || NET20)
[MetadataType(typeof(CustomerValidation))]
public partial class CustomerWithMetadataType
@@ -33,7 +33,6 @@ using Newtonsoft.Json.Utilities;
using Newtonsoft.Json.Utilities.LinqBridge;
#else
using System.Linq;
#endif
namespace Newtonsoft.Json.Converters
@@ -43,8 +42,6 @@ namespace Newtonsoft.Json.Converters
/// </summary>
public class StringEnumConverter : JsonConverter
{
private static readonly ThreadSafeStore<Type, BidirectionalDictionary<string, string>> EnumMemberNamesPerType = new ThreadSafeStore<Type, BidirectionalDictionary<string, string>>(InitializeEnumType);
/// <summary>
/// Gets or sets a value indicating whether the written enum text should be camel case.
/// </summary>
@@ -90,24 +87,9 @@ namespace Newtonsoft.Json.Converters
}
else
{
BidirectionalDictionary<string, string> map = EnumMemberNamesPerType.Get(e.GetType());
Type enumType = e.GetType();
string[] names = enumName.Split(',');
for (int i = 0; i < names.Length; i++)
{
string name = names[i].Trim();
string resolvedEnumName;
map.TryGetByFirst(name, out resolvedEnumName);
resolvedEnumName = resolvedEnumName ?? name;
if (CamelCaseText)
resolvedEnumName = StringUtils.ToCamelCase(resolvedEnumName);
names[i] = resolvedEnumName;
}
string finalName = string.Join(", ", names);
string finalName = EnumUtils.ToEnumName(enumType, enumName, CamelCaseText);
writer.WriteValue(finalName);
}
@@ -139,30 +121,7 @@ namespace Newtonsoft.Json.Converters
if (reader.TokenType == JsonToken.String)
{
string enumText = reader.Value.ToString();
if (enumText == string.Empty && isNullable)
return null;
string finalEnumText;
BidirectionalDictionary<string, string> map = EnumMemberNamesPerType.Get(t);
if (enumText.IndexOf(',') != -1)
{
string[] names = enumText.Split(',');
for (int i = 0; i < names.Length; i++)
{
string name = names[i].Trim();
names[i] = ResolvedEnumName(map, name);
}
finalEnumText = string.Join(", ", names);
}
else
{
finalEnumText = ResolvedEnumName(map, enumText);
}
return Enum.Parse(t, finalEnumText, true);
return EnumUtils.ParseEnumName(enumText, isNullable, t);
}
if (reader.TokenType == JsonToken.Integer)
@@ -182,14 +141,6 @@ namespace Newtonsoft.Json.Converters
throw JsonSerializationException.Create(reader, "Unexpected token {0} when parsing enum.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType));
}
private static string ResolvedEnumName(BidirectionalDictionary<string, string> map, string enumText)
{
string resolvedEnumName;
map.TryGetBySecond(enumText, out resolvedEnumName);
resolvedEnumName = resolvedEnumName ?? enumText;
return resolvedEnumName;
}
/// <summary>
/// Determines whether this instance can convert the specified object type.
/// </summary>
@@ -205,35 +156,5 @@ namespace Newtonsoft.Json.Converters
return t.IsEnum();
}
private static BidirectionalDictionary<string, string> InitializeEnumType(Type type)
{
BidirectionalDictionary<string, string> map = new BidirectionalDictionary<string, string>(
StringComparer.OrdinalIgnoreCase,
StringComparer.OrdinalIgnoreCase);
foreach (FieldInfo f in type.GetFields())
{
string n1 = f.Name;
string n2;
#if !NET20
n2 = f.GetCustomAttributes(typeof(EnumMemberAttribute), true)
.Cast<EnumMemberAttribute>()
.Select(a => a.Value)
.SingleOrDefault() ?? f.Name;
#else
n2 = f.Name;
#endif
string s;
if (map.TryGetBySecond(n2, out s))
throw new InvalidOperationException("Enum name '{0}' already exists on enum '{1}'.".FormatWith(CultureInfo.InvariantCulture, n2, type.Name));
map.Set(n1, n2);
}
return map;
}
}
}
@@ -91,5 +91,5 @@ using System.Security;
// by using the '*' as shown below:
[assembly: AssemblyVersion("6.0.0.0")]
[assembly: AssemblyFileVersion("6.0.6.17911")]
[assembly: AssemblyFileVersion("6.0.6.17924")]
[assembly: CLSCompliant(true)]
+95 -6
View File
@@ -26,6 +26,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Runtime.Serialization;
#if NET20
using Newtonsoft.Json.Utilities.LinqBridge;
#else
@@ -37,6 +38,38 @@ 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 static BidirectionalDictionary<string, string> InitializeEnumType(Type type)
{
BidirectionalDictionary<string, string> map = new BidirectionalDictionary<string, string>(
StringComparer.OrdinalIgnoreCase,
StringComparer.OrdinalIgnoreCase);
foreach (FieldInfo f in type.GetFields())
{
string n1 = f.Name;
string n2;
#if !NET20
n2 = f.GetCustomAttributes(typeof(EnumMemberAttribute), true)
.Cast<EnumMemberAttribute>()
.Select(a => a.Value)
.SingleOrDefault() ?? f.Name;
#else
n2 = f.Name;
#endif
string s;
if (map.TryGetBySecond(n2, out s))
throw new InvalidOperationException("Enum name '{0}' already exists on enum '{1}'.".FormatWith(CultureInfo.InvariantCulture, n2, type.Name));
map.Set(n1, n2);
}
return map;
}
public static IList<T> GetFlagsValues<T>(T value) where T : struct
{
Type enumType = typeof(T);
@@ -112,9 +145,7 @@ namespace Newtonsoft.Json.Utilities
List<object> values = new List<object>();
var fields = from field in enumType.GetFields()
where field.IsLiteral
select field;
var fields = enumType.GetFields().Where(f => f.IsLiteral);
foreach (FieldInfo field in fields)
{
@@ -132,9 +163,7 @@ namespace Newtonsoft.Json.Utilities
List<string> values = new List<string>();
var fields = from field in enumType.GetFields()
where field.IsLiteral
select field;
var fields = enumType.GetFields().Where(f => f.IsLiteral);
foreach (FieldInfo field in fields)
{
@@ -143,5 +172,65 @@ namespace Newtonsoft.Json.Utilities
return values;
}
public static object ParseEnumName(string enumText, bool isNullable, Type t)
{
if (enumText == string.Empty && isNullable)
return null;
string finalEnumText;
BidirectionalDictionary<string, string> map = EnumMemberNamesPerType.Get(t);
if (enumText.IndexOf(',') != -1)
{
string[] names = enumText.Split(',');
for (int i = 0; i < names.Length; i++)
{
string name = names[i].Trim();
names[i] = ResolvedEnumName(map, name);
}
finalEnumText = string.Join(", ", names);
}
else
{
finalEnumText = ResolvedEnumName(map, enumText);
}
return Enum.Parse(t, finalEnumText, true);
}
public static string ToEnumName(Type enumType, string enumText, bool camelCaseText)
{
BidirectionalDictionary<string, string> map = EnumMemberNamesPerType.Get(enumType);
string[] names = enumText.Split(',');
for (int i = 0; i < names.Length; i++)
{
string name = names[i].Trim();
string resolvedEnumName;
map.TryGetByFirst(name, out resolvedEnumName);
resolvedEnumName = resolvedEnumName ?? name;
if (camelCaseText)
resolvedEnumName = StringUtils.ToCamelCase(resolvedEnumName);
names[i] = resolvedEnumName;
}
string finalName = string.Join(", ", names);
return finalName;
}
private static string ResolvedEnumName(BidirectionalDictionary<string, string> map, string enumText)
{
string resolvedEnumName;
map.TryGetBySecond(enumText, out resolvedEnumName);
resolvedEnumName = resolvedEnumName ?? enumText;
return resolvedEnumName;
}
}
}