From 107d15a38dd4ff9476d580b09f4a9358f0848d30 Mon Sep 17 00:00:00 2001 From: James Newton-King Date: Fri, 4 Oct 2013 22:29:32 +1300 Subject: [PATCH] -Fixed not parsing some ISO date timezones -Fixed float schema validation of integer values -Changed writing of IConvertible values to convert objects to strings -Fixed serializing proxy objects with no default constructor --- Doc/readme.txt | 2 +- Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs | 18 +++++ .../Properties/AssemblyInfo.cs | 2 +- .../Schema/JsonSchemaTests.cs | 25 +++++++ .../Serialization/JsonSerializerTest.cs | 70 +++++++++++++++++++ Src/Newtonsoft.Json/JsonWriter.cs | 12 +++- .../Properties/AssemblyInfo.cs | 2 +- .../Schema/JsonSchemaGenerator.cs | 2 +- .../Serialization/DefaultContractResolver.cs | 13 ++-- .../Utilities/DateTimeParser.cs | 35 ++++++++-- 10 files changed, 163 insertions(+), 18 deletions(-) diff --git a/Doc/readme.txt b/Doc/readme.txt index 29dea6c8..fcf654c3 100644 --- a/Doc/readme.txt +++ b/Doc/readme.txt @@ -24,7 +24,7 @@ http://james.newtonking.com/projects/json/help/ Versions: -Json.NET has different libaries for the various .NET Framework versions. +Json.NET has different libraries for the various .NET Framework versions. -Net45: .NET latest (4.5) diff --git a/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs b/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs index 8e8beb81..e99e4223 100644 --- a/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs +++ b/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs @@ -664,5 +664,23 @@ namespace Newtonsoft.Json.Tests.Linq ]", a.ToString()); } #endif + +#if !NET20 + [Test] + public void ParseIsoTimeZones() + { + DateTimeOffset expectedDate = new DateTimeOffset(2013, 08, 14, 4, 38, 31, TimeSpan.FromHours(12).Add(TimeSpan.FromMinutes(30))); + JsonTextReader reader = new JsonTextReader(new StringReader("'2013-08-14T04:38:31.000+1230'")); + reader.DateParseHandling = DateParseHandling.DateTimeOffset; + JValue date = (JValue)JToken.ReadFrom(reader); + Assert.AreEqual(expectedDate, date.Value); + + DateTimeOffset expectedDate2 = new DateTimeOffset(2013, 08, 14, 4, 38, 31, TimeSpan.FromHours(12)); + JsonTextReader reader2 = new JsonTextReader(new StringReader("'2013-08-14T04:38:31.000+12'")); + reader2.DateParseHandling = DateParseHandling.DateTimeOffset; + JValue date2 = (JValue)JToken.ReadFrom(reader2); + Assert.AreEqual(expectedDate2, date2.Value); + } +#endif } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs index 8fdd97c9..f8db56aa 100644 --- a/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -75,4 +75,4 @@ using System.Security; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("5.0.6.16530")] +[assembly: AssemblyFileVersion("5.0.6.16604")] diff --git a/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaTests.cs b/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaTests.cs index 8827a02d..90fa9522 100644 --- a/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaTests.cs +++ b/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaTests.cs @@ -555,5 +555,30 @@ namespace Newtonsoft.Json.Tests.Schema } }", json); } + + [Test] + public void IntegerValidatesAgainstFloatFlags() + { + JsonSchema schema = JsonSchema.Parse(@"{ + ""type"": ""object"", + ""$schema"": ""http://json-schema.org/draft-03/schema"", + ""required"": false, + ""properties"": { + ""NumberProperty"": { + ""required"": false, + ""type"": [ + ""number"", + ""null"" + ] + } + } +}"); + + JObject json = JObject.Parse(@"{ + ""NumberProperty"": 23 + }"); + + Assert.IsTrue(json.IsValid(schema)); + } } } diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 59911cf9..3b886dfb 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -8559,6 +8559,25 @@ Parameter name: value", ]", json); } +#if !(NET20 || NET35) + [Test] + public void SerializeDeserializeTuple() + { + Tuple tuple = Tuple.Create(500, 20); + string json = JsonConvert.SerializeObject(tuple); + Assert.AreEqual(@"{""Item1"":500,""Item2"":20}", json); + + Tuple tuple2 = JsonConvert.DeserializeObject>(json); + Assert.AreEqual(500, tuple2.Item1); + Assert.AreEqual(20, tuple2.Item2); + } +#endif + + public class MessageWithIsoDate + { + public String IsoDate { get; set; } + } + [Test] public void JsonSerializerStringEscapeHandling() { @@ -9393,6 +9412,57 @@ Parameter name: value", set { _properties = value; } } } + +#if !(PORTABLE || NETFX_CORE) + public class ConvertibleId : IConvertible + { + public int Value; + + TypeCode IConvertible.GetTypeCode() { return TypeCode.Object; } + + object IConvertible.ToType(Type conversionType, IFormatProvider provider) + { + if (conversionType == typeof(object)) { return this; } + if (conversionType == typeof(int)) { return (int)Value; } + if (conversionType == typeof(long)) { return (long)Value; } + if (conversionType == typeof(string)) { return Value.ToString(CultureInfo.InvariantCulture); } + throw new InvalidCastException(); + } + + bool IConvertible.ToBoolean(IFormatProvider provider) { throw new InvalidCastException(); } + byte IConvertible.ToByte(IFormatProvider provider) { throw new InvalidCastException(); } + char IConvertible.ToChar(IFormatProvider provider) { throw new InvalidCastException(); } + DateTime IConvertible.ToDateTime(IFormatProvider provider) { throw new InvalidCastException(); } + decimal IConvertible.ToDecimal(IFormatProvider provider) { throw new InvalidCastException(); } + double IConvertible.ToDouble(IFormatProvider provider) { throw new InvalidCastException(); } + short IConvertible.ToInt16(IFormatProvider provider) { return (short)Value; } + int IConvertible.ToInt32(IFormatProvider provider) { return Value; } + long IConvertible.ToInt64(IFormatProvider provider) { return (long)Value; } + sbyte IConvertible.ToSByte(IFormatProvider provider) { throw new InvalidCastException(); } + float IConvertible.ToSingle(IFormatProvider provider) { throw new InvalidCastException(); } + string IConvertible.ToString(IFormatProvider provider) { throw new InvalidCastException(); } + ushort IConvertible.ToUInt16(IFormatProvider provider) { throw new InvalidCastException(); } + uint IConvertible.ToUInt32(IFormatProvider provider) { throw new InvalidCastException(); } + ulong IConvertible.ToUInt64(IFormatProvider provider) { throw new InvalidCastException(); } + } + + public class TestClassConvertable + { + public ConvertibleId Id; + public int X; + } + + [Test] + public void ConvertibleIdTest() + { + var c = new TestClassConvertable { Id = new ConvertibleId { Value = 1 }, X = 2 }; + var s = JsonConvert.SerializeObject(c, Formatting.Indented); + Assert.AreEqual(@"{ + ""Id"": ""1"", + ""X"": 2 +}", s); + } +#endif } public class PersonReference diff --git a/Src/Newtonsoft.Json/JsonWriter.cs b/Src/Newtonsoft.Json/JsonWriter.cs index ec2e96dc..0bf09e94 100644 --- a/Src/Newtonsoft.Json/JsonWriter.cs +++ b/Src/Newtonsoft.Json/JsonWriter.cs @@ -1398,10 +1398,16 @@ namespace Newtonsoft.Json // the value is a non-standard IConvertible // convert to the underlying value and retry IConvertible convertable = (IConvertible)value; - TypeInformation typeInformation = ConvertUtils.GetTypeInformation(convertable); - object convertedValue = convertable.ToType(typeInformation.Type, CultureInfo.InvariantCulture); - WriteValue(writer, typeInformation.TypeCode, convertedValue); + TypeInformation typeInformation = ConvertUtils.GetTypeInformation(convertable); + + // if convertable has an underlying typecode of Object then attempt to convert it to a string + PrimitiveTypeCode resolvedTypeCode = (typeInformation.TypeCode == PrimitiveTypeCode.Object) ? PrimitiveTypeCode.String : typeInformation.TypeCode; + Type resolvedType = (typeInformation.TypeCode == PrimitiveTypeCode.Object) ? typeof(string) : typeInformation.Type; + + object convertedValue = convertable.ToType(resolvedType, CultureInfo.InvariantCulture); + + WriteValue(writer, resolvedTypeCode, convertedValue); } else #endif diff --git a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs index 2a3af9d4..e449c1c7 100644 --- a/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs +++ b/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -92,6 +92,6 @@ using System.Security; // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: [assembly: AssemblyVersion("4.5.0.0")] -[assembly: AssemblyFileVersion("5.0.6.16530")] +[assembly: AssemblyFileVersion("5.0.6.16604")] [assembly: CLSCompliant(true)] diff --git a/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs b/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs index e462462d..beb78b1e 100644 --- a/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs +++ b/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs @@ -404,7 +404,7 @@ namespace Newtonsoft.Json.Schema return true; // integer is a subset of float - if (value == JsonSchemaType.Float && flag == JsonSchemaType.Integer) + if (flag == JsonSchemaType.Integer && (value & JsonSchemaType.Float) == JsonSchemaType.Float) return true; return false; diff --git a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs index 89eabdd7..6f440145 100644 --- a/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs +++ b/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -507,11 +507,16 @@ namespace Newtonsoft.Json.Serialization if (matchingMemberProperty != null && matchingMemberProperty.PropertyType != parameterInfo.ParameterType) matchingMemberProperty = null; - JsonProperty property = CreatePropertyFromConstructorParameter(matchingMemberProperty, parameterInfo); - - if (property != null) + // ensure that property will have a name from matching property or from parameterinfo + // parameterinfo could have no name if generated by a proxy (I'm looking at you Castle) + if (matchingMemberProperty != null || parameterInfo.Name != null) { - parameterCollection.AddProperty(property); + JsonProperty property = CreatePropertyFromConstructorParameter(matchingMemberProperty, parameterInfo); + + if (property != null) + { + parameterCollection.AddProperty(property); + } } } diff --git a/Src/Newtonsoft.Json/Utilities/DateTimeParser.cs b/Src/Newtonsoft.Json/Utilities/DateTimeParser.cs index c7d395f9..8ae6fa91 100644 --- a/Src/Newtonsoft.Json/Utilities/DateTimeParser.cs +++ b/Src/Newtonsoft.Json/Utilities/DateTimeParser.cs @@ -176,26 +176,47 @@ namespace Newtonsoft.Json.Utilities } else { - if (start + 5 < _length + if (start + 2 < _length && Parse2Digit(start + Lz_, out ZoneHour) - && ZoneHour <= 99 - && ParseChar(start + Lz_zz, ':') - && Parse2Digit(start + Lz_zz_, out ZoneMinute) - && ZoneMinute <= 99) + && ZoneHour <= 99) { switch (ch) { case '-': Zone = ParserTimeZone.LocalWestOfUtc; - start += Lz_zz_zz; + start += Lz_zz; break; case '+': Zone = ParserTimeZone.LocalEastOfUtc; - start += Lz_zz_zz; + start += Lz_zz; break; } } + + if (start < _length) + { + if (ParseChar(start, ':')) + { + start += 1; + + if (start + 1 < _length + && Parse2Digit(start, out ZoneMinute) + && ZoneMinute <= 99) + { + start += 2; + } + } + else + { + if (start + 1 < _length + && Parse2Digit(start, out ZoneMinute) + && ZoneMinute <= 99) + { + start += 2; + } + } + } } }