From 2c7a51c50a2dfe4ca37871ac27488abdbc924669 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Wed, 9 Dec 2015 08:45:36 +1300 Subject: [PATCH] -Added generic JsonConvert.SerializeObject methods -Fixed JToken.ToObject not using StringEnumConverter on enums --- .../Converters/DataSetConverterTests.cs | 2 +- .../Converters/DataTableConverterTests.cs | 2 +- .../JsonTextReaderTest.cs | 70 +++++++++++++++++++ .../Linq/LinqToJsonTest.cs | 52 ++++++++++++++ .../Serialization/TypeNameHandlingTests.cs | 28 ++++++++ Src/Newtonsoft.Json/JsonConvert.cs | 60 ++++++++++++++++ Src/Newtonsoft.Json/Linq/JToken.cs | 5 +- .../JsonSerializerInternalWriter.cs | 12 ++-- 8 files changed, 222 insertions(+), 9 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/Converters/DataSetConverterTests.cs b/Src/Newtonsoft.Json.Tests/Converters/DataSetConverterTests.cs index cda7decb..091cdd06 100644 --- a/Src/Newtonsoft.Json.Tests/Converters/DataSetConverterTests.cs +++ b/Src/Newtonsoft.Json.Tests/Converters/DataSetConverterTests.cs @@ -133,7 +133,7 @@ namespace Newtonsoft.Json.Tests.Converters [Test] public void SerializeNullRoot() { - string json = JsonConvert.SerializeObject(null, typeof(DataSet), new JsonSerializerSettings + string json = JsonConvert.SerializeObject(null, new JsonSerializerSettings { Formatting = Formatting.Indented }); diff --git a/Src/Newtonsoft.Json.Tests/Converters/DataTableConverterTests.cs b/Src/Newtonsoft.Json.Tests/Converters/DataTableConverterTests.cs index 0a22a17d..30560baf 100644 --- a/Src/Newtonsoft.Json.Tests/Converters/DataTableConverterTests.cs +++ b/Src/Newtonsoft.Json.Tests/Converters/DataTableConverterTests.cs @@ -481,7 +481,7 @@ namespace Newtonsoft.Json.Tests.Converters [Test] public void SerializeNullRoot() { - string json = JsonConvert.SerializeObject(null, typeof(DataTable), new JsonSerializerSettings + string json = JsonConvert.SerializeObject(null, new JsonSerializerSettings { Formatting = Formatting.Indented }); diff --git a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs index f1086ddd..34716ac4 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs @@ -45,6 +45,9 @@ using NUnit.Framework; #endif using Newtonsoft.Json; using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Threading; #if NET20 using Newtonsoft.Json.Utilities.LinqBridge; #else @@ -3452,6 +3455,73 @@ null//comment Assert.IsFalse(r.Read()); } #endif + +#if !DNXCORE50 + [Test] + [Ignore] + public void ReadFromNetworkStream() + { + const int port = 11999; + const int jsonArrayElementsCount = 193; + + var serverStartedEvent = new ManualResetEvent(false); + var clientReceivedEvent = new ManualResetEvent(false); + + #region server + ThreadPool.QueueUserWorkItem(work => + { + var server = new TcpListener(IPAddress.Parse("0.0.0.0"), port); + server.Start(); + + serverStartedEvent.Set(); + + var serverSocket = server.AcceptSocket(); + + var jsonString = "[\r\n" + String.Join(",", Enumerable.Repeat(" \"testdata\"\r\n", jsonArrayElementsCount).ToArray()) + "]"; + var bytes = new UTF8Encoding().GetBytes(jsonString); + serverSocket.Send(bytes); + Console.WriteLine("server send: " + bytes.Length); + + + clientReceivedEvent.WaitOne(); + + }); + #endregion + + serverStartedEvent.WaitOne(); + + + var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + socket.Blocking = false; + socket.Connect("127.0.0.1", port); + + var stream = new NetworkStream(socket); + var serializer = new JsonSerializer(); + + int i = 0; + using (var sr = new StreamReader(stream, new UTF8Encoding(), false)) + using (var jsonTextReader = new JsonTextReader(sr)) + { + while (jsonTextReader.Read()) + { + i++; + + if (i == 193) + { + string s = string.Empty; + } + + Console.WriteLine($"{i} - {jsonTextReader.TokenType} - {jsonTextReader.Value}"); + } + //var result = serializer.Deserialize(jsonTextReader).ToString(); + //Console.WriteLine("client receive: " + new UTF8Encoding().GetBytes(result).Length); + } + + clientReceivedEvent.Set(); + + Console.WriteLine("Done"); + } +#endif } public class ToggleReaderError : TextReader diff --git a/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs b/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs index 1feb3079..769439f4 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs @@ -48,6 +48,7 @@ using Newtonsoft.Json.Utilities.LinqBridge; using System.Linq; #endif using System.IO; +using System.Runtime.Serialization; namespace Newtonsoft.Json.Tests.Linq { @@ -1329,6 +1330,57 @@ keyword such as type of business."" } #endif +#if !(NET20) + [JsonConverter(typeof(StringEnumConverter))] + public enum FooBar + { + [EnumMember(Value = "SOME_VALUE")] + SomeValue, + + [EnumMember(Value = "SOME_OTHER_VALUE")] + SomeOtherValue + } + + public class MyObject + { + public FooBar FooBar { get; set; } + } + + [Test] + public void ToObject_Enum_Converter() + { + JObject o = JObject.Parse("{'FooBar':'SOME_OTHER_VALUE'}"); + + FooBar e = o["FooBar"].ToObject(); + Assert.AreEqual(FooBar.SomeOtherValue, e); + } + + public enum FooBarNoEnum + { + [EnumMember(Value = "SOME_VALUE")] + SomeValue, + + [EnumMember(Value = "SOME_OTHER_VALUE")] + SomeOtherValue + } + + public class MyObjectNoEnum + { + public FooBarNoEnum FooBarNoEnum { get; set; } + } + + [Test] + public void ToObject_Enum_NoConverter() + { + JObject o = JObject.Parse("{'FooBarNoEnum':'SOME_OTHER_VALUE'}"); + + ExceptionAssert.Throws(() => + { + o["FooBarNoEnum"].ToObject(); + }, "Could not convert 'SOME_OTHER_VALUE' to FooBarNoEnum."); + } +#endif + [Test] public void SerializeWithNoRedundentIdPropertiesTest() { diff --git a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs index 833af7a9..3b0f4870 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs @@ -245,6 +245,34 @@ namespace Newtonsoft.Json.Tests.Serialization }", json); } + [Test] + public void SerializeRootTypeNameAutoWithJsonConvert_Generic() + { + string json = JsonConvert.SerializeObject(new WagePerson(), Formatting.Indented, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Auto + }); + + StringAssert.AreEqual(@"{ + ""$type"": ""Newtonsoft.Json.Tests.TestObjects.WagePerson, Newtonsoft.Json.Tests"", + ""HourlyWage"": 0.0, + ""Name"": null, + ""BirthDate"": ""0001-01-01T00:00:00"", + ""LastModified"": ""0001-01-01T00:00:00"" +}", json); + } + + [Test] + public void SerializeRootTypeNameAutoWithJsonConvert_Generic2() + { + string json = JsonConvert.SerializeObject(new WagePerson(), new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Auto + }); + + StringAssert.AreEqual(@"{""$type"":""Newtonsoft.Json.Tests.TestObjects.WagePerson, Newtonsoft.Json.Tests"",""HourlyWage"":0.0,""Name"":null,""BirthDate"":""0001-01-01T00:00:00"",""LastModified"":""0001-01-01T00:00:00""}", json); + } + public class Wrapper { public IList Array { get; set; } diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs index a2a89cec..59ddf658 100644 --- a/Src/Newtonsoft.Json/JsonConvert.cs +++ b/Src/Newtonsoft.Json/JsonConvert.cs @@ -530,6 +530,17 @@ namespace Newtonsoft.Json return SerializeObject(value, null, (JsonSerializerSettings)null); } + /// + /// Serializes the specified object to a JSON string. + /// + /// The type of the object to serialize. + /// The object to serialize. + /// A JSON string representation of the object. + public static string SerializeObject(object value) + { + return SerializeObject(value, typeof(T), (JsonSerializerSettings)null); + } + /// /// Serializes the specified object to a JSON string using formatting. /// @@ -543,6 +554,20 @@ namespace Newtonsoft.Json return SerializeObject(value, formatting, (JsonSerializerSettings)null); } + /// + /// Serializes the specified object to a JSON string using formatting. + /// + /// The type of the object to serialize. + /// The object to serialize. + /// Indicates how the output is formatted. + /// + /// A JSON string representation of the object. + /// + public static string SerializeObject(object value, Formatting formatting) + { + return SerializeObject(value, typeof(T), (JsonSerializerSettings)null); + } + /// /// Serializes the specified object to a JSON string using a collection of . /// @@ -588,6 +613,21 @@ namespace Newtonsoft.Json return SerializeObject(value, null, settings); } + /// + /// Serializes the specified object to a JSON string using . + /// + /// The type of the object to serialize. + /// The object to serialize. + /// The used to serialize the object. + /// If this is null, default serialization settings will be used. + /// + /// A JSON string representation of the object. + /// + public static string SerializeObject(T value, JsonSerializerSettings settings) + { + return SerializeObject(value, typeof(T), settings); + } + /// /// Serializes the specified object to a JSON string using a type, formatting and . /// @@ -624,6 +664,22 @@ namespace Newtonsoft.Json return SerializeObject(value, null, formatting, settings); } + /// + /// Serializes the specified object to a JSON string using formatting and . + /// + /// The type of the object to serialize. + /// The object to serialize. + /// Indicates how the output is formatted. + /// The used to serialize the object. + /// If this is null, default serialization settings will be used. + /// + /// A JSON string representation of the object. + /// + public static string SerializeObject(T value, Formatting formatting, JsonSerializerSettings settings) + { + return SerializeObject(value, typeof(T), formatting, settings); + } + /// /// Serializes the specified object to a JSON string using a type, formatting and . /// @@ -934,6 +990,7 @@ namespace Newtonsoft.Json #endif #endregion + #region Populate /// /// Populates the object with values from the JSON string. /// @@ -987,7 +1044,9 @@ namespace Newtonsoft.Json return Task.Factory.StartNew(() => PopulateObject(value, target, settings)); } #endif + #endregion + #region Xml #if !(PORTABLE40 || PORTABLE || DOTNET) /// /// Serializes the XML node to a JSON string. @@ -1145,5 +1204,6 @@ namespace Newtonsoft.Json return (XDocument)DeserializeObject(value, typeof(XDocument), converter); } #endif + #endregion } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index 95be1ff8..2ac39468 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -1948,13 +1948,14 @@ namespace Newtonsoft.Json.Linq { if (Type == JTokenType.String) { - Type enumType = objectType.IsEnum() ? objectType : Nullable.GetUnderlyingType(objectType); try { - return Enum.Parse(enumType, (string)this, true); + // use serializer so JsonConverter(typeof(StringEnumConverter)) + EnumMemberAttributes are respected + return ToObject(objectType, JsonSerializer.CreateDefault()); } catch (Exception ex) { + Type enumType = objectType.IsEnum() ? objectType : Nullable.GetUnderlyingType(objectType); throw new ArgumentException("Could not convert '{0}' to {1}.".FormatWith(CultureInfo.InvariantCulture, (string)this, enumType.Name), ex); } } diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index fa3c58d3..74eaf54d 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -48,7 +48,7 @@ namespace Newtonsoft.Json.Serialization { internal class JsonSerializerInternalWriter : JsonSerializerInternalBase { - private JsonContract _rootContract; + private Type _rootType; private int _rootLevel; private readonly List _serializeStack = new List(); @@ -64,7 +64,7 @@ namespace Newtonsoft.Json.Serialization throw new ArgumentNullException("jsonWriter"); } - _rootContract = (objectType != null) ? Serializer._contractResolver.ResolveContract(objectType) : null; + _rootType = objectType; _rootLevel = _serializeStack.Count + 1; JsonContract contract = GetContractSafe(value); @@ -99,7 +99,7 @@ namespace Newtonsoft.Json.Serialization { // clear root contract to ensure that if level was > 1 then it won't // accidently be used for non root values - _rootContract = null; + _rootType = null; } } @@ -1014,9 +1014,11 @@ namespace Newtonsoft.Json.Serialization return true; } } - else if (_rootContract != null && _serializeStack.Count == _rootLevel) + else if (_rootType != null && _serializeStack.Count == _rootLevel) { - if (contract.UnderlyingType != _rootContract.CreatedType) + JsonContract rootContract = Serializer._contractResolver.ResolveContract(_rootType); + + if (contract.UnderlyingType != rootContract.CreatedType) { return true; }