diff --git a/Src/Newtonsoft.Json.Tests/Converters/XmlNodeConverterTest.cs b/Src/Newtonsoft.Json.Tests/Converters/XmlNodeConverterTest.cs index 7fe895d2..4dc32b2b 100644 --- a/Src/Newtonsoft.Json.Tests/Converters/XmlNodeConverterTest.cs +++ b/Src/Newtonsoft.Json.Tests/Converters/XmlNodeConverterTest.cs @@ -1,4 +1,4 @@ -#region License +#region License // Copyright (c) 2007 James Newton-King // // Permission is hereby granted, free of charge, to any person @@ -1257,6 +1257,50 @@ namespace Newtonsoft.Json.Tests.Converters } }", json); } + + [Test] + public void DeserializeNonInt64IntegerValues() + { + var dict = new Dictionary { { "Int16", (short)1 }, { "Float", 2f }, { "Int32", 3 } }; + var obj = JObject.FromObject(dict); + var serializer = JsonSerializer.Create(new JsonSerializerSettings { Converters = { new XmlNodeConverter() { DeserializeRootElementName = "root" } } }); + using (var reader = obj.CreateReader()) + { + var value = (XmlDocument)serializer.Deserialize(reader, typeof(XmlDocument)); + + Assert.AreEqual(@"123", value.InnerXml); + } + } + + [Test] + public void DeserializingBooleanValues() + { + MemoryStream ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(@"{root:{""@booleanType"":true}}")); + MemoryStream xml = new MemoryStream(); + + JsonBodyToSoapXml(ms, xml); + + string xmlString = System.Text.Encoding.UTF8.GetString(xml.ToArray()); + + Assert.AreEqual(@"", xmlString); + } + + static void JsonBodyToSoapXml(Stream json, Stream xml) + { + Newtonsoft.Json.JsonSerializerSettings settings = new Newtonsoft.Json.JsonSerializerSettings(); + settings.Converters.Add(new Newtonsoft.Json.Converters.XmlNodeConverter()); + Newtonsoft.Json.JsonSerializer serializer = Newtonsoft.Json.JsonSerializer.Create(settings); + using (Newtonsoft.Json.JsonTextReader reader = new Newtonsoft.Json.JsonTextReader(new System.IO.StreamReader(json))) + { + XmlDocument doc = (XmlDocument)serializer.Deserialize(reader, typeof(XmlDocument)); + if (reader.Read() && reader.TokenType != JsonToken.Comment) + throw new JsonSerializationException("Additional text found in JSON string after finishing deserializing object."); + using (XmlWriter writer = XmlWriter.Create(xml)) + { + doc.Save(writer); + } + } + } } } #endif \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs index ebf9c958..ff612b56 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs @@ -436,5 +436,17 @@ Parameter name: index")] { JArray.Parse("[1"); } + + [Test] + public void InsertAddEnd() + { + JArray array = new JArray(); + array.Insert(0, 123); + array.Insert(1, 456); + + Assert.AreEqual(2, array.Count); + Assert.AreEqual(123, (int)array[0]); + Assert.AreEqual(456, (int)array[1]); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs index c7cf491f..5b2e05b3 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs @@ -209,6 +209,9 @@ namespace Newtonsoft.Json.Tests.Linq [Test] public void Casting() { + Assert.AreEqual(1L, (long)(new JValue(1))); + Assert.AreEqual(2L, (long) new JArray(1, 2, 3)[1]); + Assert.AreEqual(new DateTime(2000, 12, 20), (DateTime)new JValue(new DateTime(2000, 12, 20))); #if !PocketPC && !NET20 Assert.AreEqual(new DateTimeOffset(2000, 12, 20, 23, 50, 10, TimeSpan.Zero), (DateTimeOffset)new JValue(new DateTimeOffset(2000, 12, 20, 23, 50, 10, TimeSpan.Zero))); diff --git a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs index 23536a5d..89fcdcdc 100644 --- a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs +++ b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs @@ -126,14 +126,14 @@ namespace Newtonsoft.Json.Tests public void BuildJObject() { JObject o = new JObject(); - for (int i = 0; i < 200; i++) + for (int i = 0; i < 50; i++) { o[i.ToString()] = i; } string jsonText = o.ToString(); // this is extremely slow with 5000 interations - int interations = 100; + int interations = 1000; TimeOperation(() => { diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index d9aade55..d3883b4a 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -66,5 +66,5 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: [assembly: AssemblyVersion("4.0.3.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.0.3.14202")] +[assembly: AssemblyFileVersion("4.0.3.14301")] #endif diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 3e0b170d..c7386956 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -24,11 +24,15 @@ #endregion using System; +#if !(NET35 || NET20 || SILVERLIGHT || WINDOWS_PHONE) +using System.Collections.Concurrent; +#endif using System.Collections.Generic; #if !SILVERLIGHT && !PocketPC && !NET20 using System.ComponentModel.DataAnnotations; using System.Configuration; using System.Runtime.CompilerServices; +using System.Runtime.Serialization.Formatters; using System.Threading; using System.Web.Script.Serialization; #endif @@ -4910,6 +4914,107 @@ keyword such as type of business."" Assert.AreEqual(@"{""p"":1}", result); } } + +#if !(NET35 || NET20 || SILVERLIGHT || WINDOWS_PHONE) + [Test] + public void DeserializeConcurrentDictionary() + { + IDictionary components = new Dictionary + { + {"Key!", new Component()} + }; + GameObject go = new GameObject + { + Components = new ConcurrentDictionary(components), + Id = "Id!", + Name = "Name!" + }; + + string originalJson = JsonConvert.SerializeObject(go, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Components"": { + ""Key!"": {} + }, + ""Id"": ""Id!"", + ""Name"": ""Name!"" +}", originalJson); + + GameObject newObject = JsonConvert.DeserializeObject(originalJson); + + Assert.AreEqual(1, newObject.Components.Count); + Assert.AreEqual("Id!", newObject.Id); + Assert.AreEqual("Name!", newObject.Name); + } +#endif + + [Test] + public void DeserializeKeyValuePairArray() + { + string json = @"[ { ""Value"": [ ""1"", ""2"" ], ""Key"": ""aaa"", ""BadContent"": [ 0 ] }, { ""Value"": [ ""3"", ""4"" ], ""Key"": ""bbb"" } ]"; + + IList>> values = JsonConvert.DeserializeObject>>>(json); + + Assert.AreEqual(2, values.Count); + Assert.AreEqual("aaa", values[0].Key); + Assert.AreEqual(2, values[0].Value.Count); + Assert.AreEqual("1", values[0].Value[0]); + Assert.AreEqual("2", values[0].Value[1]); + Assert.AreEqual("bbb", values[1].Key); + Assert.AreEqual(2, values[1].Value.Count); + Assert.AreEqual("3", values[1].Value[0]); + Assert.AreEqual("4", values[1].Value[1]); + } + + [Test] + public void DeserializeNullableKeyValuePairArray() + { + string json = @"[ { ""Value"": [ ""1"", ""2"" ], ""Key"": ""aaa"", ""BadContent"": [ 0 ] }, null, { ""Value"": [ ""3"", ""4"" ], ""Key"": ""bbb"" } ]"; + + IList>?> values = JsonConvert.DeserializeObject>?>>(json); + + Assert.AreEqual(3, values.Count); + Assert.AreEqual("aaa", values[0].Value.Key); + Assert.AreEqual(2, values[0].Value.Value.Count); + Assert.AreEqual("1", values[0].Value.Value[0]); + Assert.AreEqual("2", values[0].Value.Value[1]); + Assert.AreEqual(null, values[1]); + Assert.AreEqual("bbb", values[2].Value.Key); + Assert.AreEqual(2, values[2].Value.Value.Count); + Assert.AreEqual("3", values[2].Value.Value[0]); + Assert.AreEqual("4", values[2].Value.Value[1]); + } + + [Test] + [ExpectedException(typeof (Exception), ExpectedMessage = "Could not deserialize Null to KeyValuePair.")] + public void DeserializeNullToNonNullableKeyValuePairArray() + { + string json = @"[ null ]"; + + JsonConvert.DeserializeObject>>>(json); + } + + [Test] + public void SerializeUriWithQuotes() + { + string input = "http://test.com/%22foo+bar%22"; + Uri uri = new Uri(input); + string json = JsonConvert.SerializeObject(uri); + Uri output = JsonConvert.DeserializeObject(json); + + Assert.AreEqual(uri, output); + } + + [Test] + public void SerializeUriWithSlashes() + { + string input = @"http://tes/?a=b\\c&d=e\"; + Uri uri = new Uri(input); + string json = JsonConvert.SerializeObject(uri); + Uri output = JsonConvert.DeserializeObject(json); + + Assert.AreEqual(uri, output); + } } public class UriGuidTimeSpanTestClass @@ -4920,4 +5025,47 @@ keyword such as type of business."" public TimeSpan? NullableTimeSpan { get; set; } public Uri Uri { get; set; } } + +#if !(NET35 || NET20 || SILVERLIGHT || WINDOWS_PHONE) + [JsonObject(MemberSerialization.OptIn)] + public class GameObject + { + [JsonProperty] + public string Id { get; set; } + + [JsonProperty] + public string Name { get; set; } + + [JsonProperty] + public ConcurrentDictionary Components; + + public GameObject() + { + Components = new ConcurrentDictionary(); + } + + } + + [JsonObject(MemberSerialization.OptIn)] + public class Component + { + [JsonIgnore] // Ignore circular reference + public GameObject GameObject { get; set; } + + public Component() + { + } + } + + [JsonObject(MemberSerialization.OptIn)] + public class TestComponent : Component + { + [JsonProperty] + public int MyProperty { get; set; } + + public TestComponent() + { + } + } +#endif } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Converters/KeyValuePairConverter.cs b/Src/Newtonsoft.Json/Converters/KeyValuePairConverter.cs index e546bdbc..791da1ee 100644 --- a/Src/Newtonsoft.Json/Converters/KeyValuePairConverter.cs +++ b/Src/Newtonsoft.Json/Converters/KeyValuePairConverter.cs @@ -42,19 +42,50 @@ namespace Newtonsoft.Json.Converters /// The object value. public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { - IList genericArguments = objectType.GetGenericArguments(); + bool isNullable = ReflectionUtils.IsNullableType(objectType); + + if (reader.TokenType == JsonToken.Null) + { + if (!isNullable) + throw new Exception("Could not deserialize Null to KeyValuePair."); + + return null; + } + + Type t = (isNullable) + ? Nullable.GetUnderlyingType(objectType) + : objectType; + + IList genericArguments = t.GetGenericArguments(); Type keyType = genericArguments[0]; Type valueType = genericArguments[1]; - reader.Read(); - reader.Read(); - object key = serializer.Deserialize(reader, keyType); - reader.Read(); - reader.Read(); - object value = serializer.Deserialize(reader, valueType); + object key = null; + object value = null; + reader.Read(); - return ReflectionUtils.CreateInstance(objectType, key, value); + while (reader.TokenType == JsonToken.PropertyName) + { + switch (reader.Value.ToString()) + { + case "Key": + reader.Read(); + key = serializer.Deserialize(reader, keyType); + break; + case "Value": + reader.Read(); + value = serializer.Deserialize(reader, valueType); + break; + default: + reader.Skip(); + break; + } + + reader.Read(); + } + + return ReflectionUtils.CreateInstance(t, key, value); } /// @@ -66,8 +97,12 @@ namespace Newtonsoft.Json.Converters /// public override bool CanConvert(Type objectType) { - if (objectType.IsValueType && objectType.IsGenericType) - return (objectType.GetGenericTypeDefinition() == typeof (KeyValuePair<,>)); + Type t = (ReflectionUtils.IsNullableType(objectType)) + ? Nullable.GetUnderlyingType(objectType) + : objectType; + + if (t.IsValueType && t.IsGenericType) + return (t.GetGenericTypeDefinition() == typeof(KeyValuePair<,>)); return false; } diff --git a/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs b/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs index bef5eb6d..d7469e6d 100644 --- a/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs +++ b/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs @@ -26,6 +26,7 @@ #if !SILVERLIGHT || WINDOWS_PHONE using System; using System.Collections.Generic; +using System.Globalization; using System.Xml; #if !NET20 using System.Xml.Linq; @@ -1194,26 +1195,13 @@ namespace Newtonsoft.Json.Converters element.SetAttributeNode(attribute); } - if (reader.TokenType == JsonToken.String) + if (reader.TokenType == JsonToken.String + || reader.TokenType == JsonToken.Integer + || reader.TokenType == JsonToken.Float + || reader.TokenType == JsonToken.Boolean + || reader.TokenType == JsonToken.Date) { - element.AppendChild(document.CreateTextNode(reader.Value.ToString())); - } - else if (reader.TokenType == JsonToken.Integer) - { - element.AppendChild(document.CreateTextNode(XmlConvert.ToString((long)reader.Value))); - } - else if (reader.TokenType == JsonToken.Float) - { - element.AppendChild(document.CreateTextNode(XmlConvert.ToString((double)reader.Value))); - } - else if (reader.TokenType == JsonToken.Boolean) - { - element.AppendChild(document.CreateTextNode(XmlConvert.ToString((bool)reader.Value))); - } - else if (reader.TokenType == JsonToken.Date) - { - DateTime d = (DateTime)reader.Value; - element.AppendChild(document.CreateTextNode(XmlConvert.ToString(d, DateTimeUtils.ToSerializationMode(d.Kind)))); + element.AppendChild(document.CreateTextNode(ConvertTokenToXmlValue(reader))); } else if (reader.TokenType == JsonToken.Null) { @@ -1233,6 +1221,39 @@ namespace Newtonsoft.Json.Converters } } + private string ConvertTokenToXmlValue(JsonReader reader) + { + if (reader.TokenType == JsonToken.String) + { + return reader.Value.ToString(); + } + else if (reader.TokenType == JsonToken.Integer) + { + return XmlConvert.ToString(Convert.ToInt64(reader.Value, CultureInfo.InvariantCulture)); + } + else if (reader.TokenType == JsonToken.Float) + { + return XmlConvert.ToString(Convert.ToDouble(reader.Value, CultureInfo.InvariantCulture)); + } + else if (reader.TokenType == JsonToken.Boolean) + { + return XmlConvert.ToString(Convert.ToBoolean(reader.Value, CultureInfo.InvariantCulture)); + } + else if (reader.TokenType == JsonToken.Date) + { + DateTime d = Convert.ToDateTime(reader.Value, CultureInfo.InvariantCulture); + return XmlConvert.ToString(d, DateTimeUtils.ToSerializationMode(d.Kind)); + } + else if (reader.TokenType == JsonToken.Null) + { + return null; + } + else + { + throw new Exception("Cannot get an XML string value from token type '{0}'.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + } + } + private void ReadArrayElements(JsonReader reader, IXmlDocument document, string propertyName, IXmlNode currentNode, XmlNamespaceManager manager) { string elementPrefix = MiscellaneousUtils.GetPrefix(propertyName); @@ -1309,7 +1330,7 @@ namespace Newtonsoft.Json.Converters case '@': attributeName = attributeName.Substring(1); reader.Read(); - attributeValue = reader.Value.ToString(); + attributeValue = ConvertTokenToXmlValue(reader); attributeNameValues.Add(attributeName, attributeValue); string namespacePrefix; diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs index 86d20d54..736d6dad 100644 --- a/Src/Newtonsoft.Json/JsonConvert.cs +++ b/Src/Newtonsoft.Json/JsonConvert.cs @@ -392,7 +392,10 @@ namespace Newtonsoft.Json /// A JSON string representation of the . public static string ToString(Uri value) { - return '"' + value.ToString() + '"'; + if (value == null) + return Null; + + return ToString(value.ToString()); } /// diff --git a/Src/Newtonsoft.Json/Linq/JToken.cs b/Src/Newtonsoft.Json/Linq/JToken.cs index 6d74db13..7bc84f30 100644 --- a/Src/Newtonsoft.Json/Linq/JToken.cs +++ b/Src/Newtonsoft.Json/Linq/JToken.cs @@ -418,7 +418,7 @@ namespace Newtonsoft.Json.Linq if (v == null || !ValidateBoolean(v, false)) throw new ArgumentException("Can not convert {0} to Boolean.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); - return (bool)v.Value; + return Convert.ToBoolean(v.Value, CultureInfo.InvariantCulture); } #if !PocketPC && !NET20 @@ -451,7 +451,7 @@ namespace Newtonsoft.Json.Linq if (v == null || !ValidateBoolean(v, true)) throw new ArgumentException("Can not convert {0} to Boolean.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); - return (bool?)v.Value; + return (v.Value != null) ? (bool?)Convert.ToBoolean(v.Value, CultureInfo.InvariantCulture) : null; } /// @@ -465,7 +465,7 @@ namespace Newtonsoft.Json.Linq if (v == null || !ValidateInteger(v, false)) throw new ArgumentException("Can not convert {0} to Int64.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); - return (long)v.Value; + return Convert.ToInt64(v.Value, CultureInfo.InvariantCulture); } /// @@ -482,7 +482,7 @@ namespace Newtonsoft.Json.Linq if (v == null || !ValidateDate(v, true)) throw new ArgumentException("Can not convert {0} to DateTime.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); - return (DateTime?)v.Value; + return (v.Value != null) ? (DateTime?)Convert.ToDateTime(v.Value, CultureInfo.InvariantCulture) : null; } #if !PocketPC && !NET20 @@ -644,7 +644,7 @@ namespace Newtonsoft.Json.Linq if (v == null || !ValidateDate(v, false)) throw new ArgumentException("Can not convert {0} to DateTime.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); - return (DateTime)v.Value; + return Convert.ToDateTime(v.Value, CultureInfo.InvariantCulture); } /// @@ -661,7 +661,7 @@ namespace Newtonsoft.Json.Linq if (v == null || !ValidateInteger(v, true)) throw new ArgumentException("Can not convert {0} to Int64.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); - return (long?)v.Value; + return (v.Value != null) ? (long?)Convert.ToInt64(v.Value, CultureInfo.InvariantCulture) : null; } /// @@ -710,7 +710,7 @@ namespace Newtonsoft.Json.Linq if (v == null || !ValidateInteger(v, true)) throw new ArgumentException("Can not convert {0} to UInt32.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); - return (uint?)v.Value; + return (v.Value != null) ? (uint?)Convert.ToUInt32(v.Value, CultureInfo.InvariantCulture) : null; } /// @@ -728,7 +728,7 @@ namespace Newtonsoft.Json.Linq if (v == null || !ValidateInteger(v, true)) throw new ArgumentException("Can not convert {0} to UInt64.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); - return (ulong?)v.Value; + return (v.Value != null) ? (ulong?)Convert.ToUInt64(v.Value, CultureInfo.InvariantCulture) : null; } /// @@ -742,7 +742,7 @@ namespace Newtonsoft.Json.Linq if (v == null || !ValidateFloat(v, false)) throw new ArgumentException("Can not convert {0} to Double.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); - return (double)v.Value; + return Convert.ToDouble(v.Value, CultureInfo.InvariantCulture); } /// @@ -773,7 +773,7 @@ namespace Newtonsoft.Json.Linq if (v == null || !ValidateString(v)) throw new ArgumentException("Can not convert {0} to String.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); - return (string)v.Value; + return (v.Value != null) ? Convert.ToString(v.Value) : null; } /// diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index ce577bd6..72dfcaa2 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -85,7 +85,7 @@ using System.Security; // by using the '*' as shown below: [assembly: AssemblyVersion("4.0.3.0")] #if !PocketPC -[assembly: AssemblyFileVersion("4.0.3.14202")] +[assembly: AssemblyFileVersion("4.0.3.14301")] #endif [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 516ad93f..97150f57 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -25,6 +25,9 @@ using System; using System.Collections; +#if !(NET35 || NET20 || SILVERLIGHT || WINDOWS_PHONE) +using System.Collections.Concurrent; +#endif using System.Collections.Generic; using System.ComponentModel; #if !(NET35 || NET20 || WINDOWS_PHONE) @@ -463,12 +466,24 @@ namespace Newtonsoft.Json.Serialization if (onSerializing != null) contract.OnSerializing = onSerializing; + if (onSerialized != null) contract.OnSerialized = onSerialized; + if (onDeserializing != null) contract.OnDeserializing = onDeserializing; + if (onDeserialized != null) + { + // ConcurrentDictionary throws an error here so don't use its OnDeserialized - http://json.codeplex.com/discussions/257093 +#if !(NET35 || NET20 || SILVERLIGHT || WINDOWS_PHONE) + if (!t.IsGenericType || (t.GetGenericTypeDefinition() != typeof(ConcurrentDictionary<,>))) + contract.OnDeserialized = onDeserialized; +#else contract.OnDeserialized = onDeserialized; +#endif + } + if (onError != null) contract.OnError = onError; }