From 58d51cda5c3ee6e3fd5c3b826ae15e65423d9c31 Mon Sep 17 00:00:00 2001 From: JamesNK Date: Sun, 22 Jan 2012 01:51:01 +0000 Subject: [PATCH] -Added ReadAsInt32 to JsonReader -Improved performance by using an enum rather than a type check to test contract type --- .../JsonTextReaderTest.cs | 30 ++++ Src/Newtonsoft.Json.Tests/PerformanceTests.cs | 15 +- .../Serialization/JsonSerializerTest.cs | 2 +- Src/Newtonsoft.Json/Bson/BsonReader.cs | 26 ++- .../Converters/DataTableConverter.cs | 2 +- .../Converters/RegexConverter.cs | 4 +- Src/Newtonsoft.Json/JsonConvert.cs | 26 --- Src/Newtonsoft.Json/JsonReader.cs | 14 +- Src/Newtonsoft.Json/JsonTextReader.cs | 69 +++++++- Src/Newtonsoft.Json/JsonToken.cs | 2 +- Src/Newtonsoft.Json/JsonValidatingReader.cs | 12 ++ Src/Newtonsoft.Json/Linq/JTokenReader.cs | 22 +++ .../Schema/JsonSchemaGenerator.cs | 156 +++++++++--------- .../Serialization/JsonArrayContract.cs | 2 + .../Serialization/JsonContract.cs | 51 ++++-- .../Serialization/JsonDictionaryContract.cs | 2 + .../Serialization/JsonDynamicContract.cs | 2 + .../JsonISerializableContract.cs | 1 + .../Serialization/JsonLinqContract.cs | 1 + .../Serialization/JsonObjectContract.cs | 14 +- .../Serialization/JsonPrimitiveContract.cs | 1 + .../JsonSerializerInternalReader.cs | 100 ++++++----- .../JsonSerializerInternalWriter.cs | 81 +++++---- .../Serialization/JsonStringContract.cs | 1 + 24 files changed, 398 insertions(+), 238 deletions(-) diff --git a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs index b07921f9..6ffcf8a8 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs @@ -1075,6 +1075,36 @@ bye", reader.Value); Assert.IsFalse(jsonReader.Read()); } + [Test] + public void ReadOctalNumberAsInt32() + { + StringReader s = new StringReader(@"[0372, 0xFA, 0XFA]"); + JsonTextReader jsonReader = new JsonTextReader(s); + + Assert.IsTrue(jsonReader.Read()); + Assert.AreEqual(JsonToken.StartArray, jsonReader.TokenType); + + jsonReader.ReadAsInt32(); + Assert.AreEqual(JsonToken.Integer, jsonReader.TokenType); + Assert.AreEqual(typeof(int), jsonReader.ValueType); + Assert.AreEqual(250, jsonReader.Value); + + jsonReader.ReadAsInt32(); + Assert.AreEqual(JsonToken.Integer, jsonReader.TokenType); + Assert.AreEqual(typeof(int), jsonReader.ValueType); + Assert.AreEqual(250, jsonReader.Value); + + jsonReader.ReadAsInt32(); + Assert.AreEqual(JsonToken.Integer, jsonReader.TokenType); + Assert.AreEqual(typeof(int), jsonReader.ValueType); + Assert.AreEqual(250, jsonReader.Value); + + Assert.IsTrue(jsonReader.Read()); + Assert.AreEqual(JsonToken.EndArray, jsonReader.TokenType); + + Assert.IsFalse(jsonReader.Read()); + } + [Test] [ExpectedException(typeof(JsonReaderException), ExpectedMessage = "Unexpected character encountered while parsing value: }. Line 1, position 1.")] public void ReadBadCharInArray() diff --git a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs index 11e0dc1d..160cbd09 100644 --- a/Src/Newtonsoft.Json.Tests/PerformanceTests.cs +++ b/Src/Newtonsoft.Json.Tests/PerformanceTests.cs @@ -36,8 +36,8 @@ namespace Newtonsoft.Json.Tests public class PerformanceTests : TestFixtureBase { - private const int Iterations = 100; - //private const int Iterations = 5000; + //private const int Iterations = 100; + private const int Iterations = 5000; private static SimpleObject CreateSimpleObject() { @@ -101,12 +101,12 @@ namespace Newtonsoft.Json.Tests private void SerializeTests(object value) { - //BenchmarkSerializeMethod(SerializeMethod.DataContractSerializer, value); - ////BenchmarkSerializeMethod(SerializeMethod.BinaryFormatter, value); - //BenchmarkSerializeMethod(SerializeMethod.JavaScriptSerializer, value); - //BenchmarkSerializeMethod(SerializeMethod.DataContractJsonSerializer, value); + BenchmarkSerializeMethod(SerializeMethod.DataContractSerializer, value); + BenchmarkSerializeMethod(SerializeMethod.BinaryFormatter, value); + BenchmarkSerializeMethod(SerializeMethod.JavaScriptSerializer, value); + BenchmarkSerializeMethod(SerializeMethod.DataContractJsonSerializer, value); BenchmarkSerializeMethod(SerializeMethod.JsonNet, value); - //BenchmarkSerializeMethod(SerializeMethod.JsonNetBinary, value); + BenchmarkSerializeMethod(SerializeMethod.JsonNetBinary, value); } [Test] @@ -763,6 +763,7 @@ namespace Newtonsoft.Json.Tests } [DataContract] + [Serializable] public class SimpleObject { [DataMember] diff --git a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs index 13b87831..b0fe5629 100644 --- a/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -1931,7 +1931,7 @@ keyword such as type of business."" } [Test] - [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Error converting value """" to type 'System.Int32'. Line 1, position 15.")] + [ExpectedException(typeof(JsonReaderException), ExpectedMessage = @"Could not convert string to integer: . Line 1, position 15.")] public void DeserializeEnsureTypeEmptyStringToIntError() { JsonConvert.DeserializeObject("{ReadTimeout:''}"); diff --git a/Src/Newtonsoft.Json/Bson/BsonReader.cs b/Src/Newtonsoft.Json/Bson/BsonReader.cs index a36bb92d..0d178852 100644 --- a/Src/Newtonsoft.Json/Bson/BsonReader.cs +++ b/Src/Newtonsoft.Json/Bson/BsonReader.cs @@ -193,7 +193,7 @@ namespace Newtonsoft.Json.Bson if (TokenType == JsonToken.Null) return null; if (TokenType == JsonToken.Bytes) - return (byte[]) Value; + return (byte[])Value; if (ReaderIsSerializerInArray()) return null; @@ -238,7 +238,7 @@ namespace Newtonsoft.Json.Bson if (TokenType == JsonToken.Integer || TokenType == JsonToken.Float) { SetToken(JsonToken.Float, Convert.ToDecimal(Value, CultureInfo.InvariantCulture)); - return (decimal) Value; + return (decimal)Value; } if (ReaderIsSerializerInArray()) @@ -247,6 +247,28 @@ namespace Newtonsoft.Json.Bson throw new JsonReaderException("Error reading decimal. Expected a number but got {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType)); } + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . + public override int? ReadAsInt32() + { + Read(); + + if (TokenType == JsonToken.Integer || TokenType == JsonToken.Float) + { + SetToken(JsonToken.Float, Convert.ToInt32(Value, CultureInfo.InvariantCulture)); + return (int)Value; + } + if (TokenType == JsonToken.Null) + return null; + + if (ReaderIsSerializerInArray()) + return null; + + throw new JsonReaderException("Error reading integer. Expected a number but got {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType)); + } + #if !NET20 /// /// Reads the next JSON token from the stream as a . diff --git a/Src/Newtonsoft.Json/Converters/DataTableConverter.cs b/Src/Newtonsoft.Json/Converters/DataTableConverter.cs index c08324c4..ec0de77d 100644 --- a/Src/Newtonsoft.Json/Converters/DataTableConverter.cs +++ b/Src/Newtonsoft.Json/Converters/DataTableConverter.cs @@ -96,7 +96,7 @@ namespace Newtonsoft.Json.Converters while (reader.TokenType == JsonToken.PropertyName) { - string columnName = (string) reader.Value; + string columnName = (string)reader.Value; reader.Read(); diff --git a/Src/Newtonsoft.Json/Converters/RegexConverter.cs b/Src/Newtonsoft.Json/Converters/RegexConverter.cs index bd589bc3..02ef524d 100644 --- a/Src/Newtonsoft.Json/Converters/RegexConverter.cs +++ b/Src/Newtonsoft.Json/Converters/RegexConverter.cs @@ -93,7 +93,7 @@ namespace Newtonsoft.Json.Converters private object ReadBson(BsonReader reader) { - string regexText = (string) reader.Value; + string regexText = (string)reader.Value; int patternOptionDelimiterIndex = regexText.LastIndexOf('/'); string patternText = regexText.Substring(1, patternOptionDelimiterIndex - 1); @@ -126,7 +126,7 @@ namespace Newtonsoft.Json.Converters { reader.Read(); reader.Read(); - string pattern = (string) reader.Value; + string pattern = (string)reader.Value; reader.Read(); reader.Read(); diff --git a/Src/Newtonsoft.Json/JsonConvert.cs b/Src/Newtonsoft.Json/JsonConvert.cs index 4f9b89a6..556a7f87 100644 --- a/Src/Newtonsoft.Json/JsonConvert.cs +++ b/Src/Newtonsoft.Json/JsonConvert.cs @@ -529,32 +529,6 @@ namespace Newtonsoft.Json return IsJsonPrimitiveTypeCode(Type.GetTypeCode(type)); } - internal static bool IsJsonPrimitive(object value) - { - if (value == null) - return true; - - IConvertible convertible = value as IConvertible; - - if (convertible != null) - return IsJsonPrimitiveTypeCode(convertible.GetTypeCode()); - -#if !PocketPC && !NET20 - if (value is DateTimeOffset) - return true; -#endif - if (value is byte[]) - return true; - if (value is Uri) - return true; - if (value is TimeSpan) - return true; - if (value is Guid) - return true; - - return false; - } - /// /// Serializes the specified object to a JSON string. /// diff --git a/Src/Newtonsoft.Json/JsonReader.cs b/Src/Newtonsoft.Json/JsonReader.cs index 0e919e7c..b22fba32 100644 --- a/Src/Newtonsoft.Json/JsonReader.cs +++ b/Src/Newtonsoft.Json/JsonReader.cs @@ -97,7 +97,7 @@ namespace Newtonsoft.Json } // current Token data - private JsonToken _token; + private JsonToken _tokenType; private object _value; private char _quoteChar; internal State _currentState; @@ -146,7 +146,7 @@ namespace Newtonsoft.Json /// public virtual JsonToken TokenType { - get { return _token; } + get { return _tokenType; } } /// @@ -222,6 +222,12 @@ namespace Newtonsoft.Json /// true if the next token was read successfully; false if there are no more tokens to read. public abstract bool Read(); + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . + public abstract int? ReadAsInt32(); + /// /// Reads the next JSON token from the stream as a . /// @@ -276,7 +282,7 @@ namespace Newtonsoft.Json /// The value. protected void SetToken(JsonToken newToken, object value) { - _token = newToken; + _tokenType = newToken; switch (newToken) { @@ -447,7 +453,7 @@ namespace Newtonsoft.Json public virtual void Close() { _currentState = State.Closed; - _token = JsonToken.None; + _tokenType = JsonToken.None; _value = null; } diff --git a/Src/Newtonsoft.Json/JsonTextReader.cs b/Src/Newtonsoft.Json/JsonTextReader.cs index 6455bd02..65987770 100644 --- a/Src/Newtonsoft.Json/JsonTextReader.cs +++ b/Src/Newtonsoft.Json/JsonTextReader.cs @@ -42,6 +42,7 @@ namespace Newtonsoft.Json private enum ReadType { Read, + ReadAsInt32, ReadAsBytes, ReadAsDecimal, #if !NET20 @@ -459,6 +460,45 @@ namespace Newtonsoft.Json throw CreateReaderException(this, "Unexpected token when reading decimal: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType)); } + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . + public override int? ReadAsInt32() + { + _readType = ReadType.ReadAsInt32; + + do + { + if (!ReadInternal()) + throw CreateReaderException(this, "Unexpected end when reading integer."); + } while (TokenType == JsonToken.Comment); + + if (TokenType == JsonToken.Integer) + return (int?)Value; + if (TokenType == JsonToken.Null) + return null; + + int i; + if (TokenType == JsonToken.String) + { + if (int.TryParse((string)Value, NumberStyles.Integer, Culture, out i)) + { + SetToken(JsonToken.Integer, i); + return i; + } + else + { + throw CreateReaderException(this, "Could not convert string to integer: {0}.".FormatWith(CultureInfo.InvariantCulture, Value)); + } + } + + if (ReaderIsSerializerInArray()) + return null; + + throw CreateReaderException(this, "Unexpected token when reading integer: {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType)); + } + #if !NET20 /// /// Reads the next JSON token from the stream as a . @@ -1223,7 +1263,34 @@ namespace Newtonsoft.Json bool singleDigit = (char.IsDigit(firstChar) && _stringReference.Length == 1); bool nonBase10 = (firstChar == '0' && _stringReference.Length > 1 && _stringReference.Chars[_stringReference.StartIndex + 1] != '.'); - if (_readType == ReadType.ReadAsDecimal) + if (_readType == ReadType.ReadAsInt32) + { + if (singleDigit) + { + // digit char values start at 48 + numberValue = firstChar - 48; + } + else if (nonBase10) + { + string number = _stringReference.ToString(); + + // decimal.Parse doesn't support parsing hexadecimal values + int integer = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) + ? Convert.ToInt32(number, 16) + : Convert.ToInt32(number, 8); + + numberValue = integer; + } + else + { + string number = _stringReference.ToString(); + + numberValue = Convert.ToInt32(number, CultureInfo.InvariantCulture); + } + + numberType = JsonToken.Integer; + } + else if (_readType == ReadType.ReadAsDecimal) { if (singleDigit) { diff --git a/Src/Newtonsoft.Json/JsonToken.cs b/Src/Newtonsoft.Json/JsonToken.cs index 5870c420..ca05bcdd 100644 --- a/Src/Newtonsoft.Json/JsonToken.cs +++ b/Src/Newtonsoft.Json/JsonToken.cs @@ -63,7 +63,7 @@ namespace Newtonsoft.Json /// Raw, /// - /// An interger. + /// An integer. /// Integer, /// diff --git a/Src/Newtonsoft.Json/JsonValidatingReader.cs b/Src/Newtonsoft.Json/JsonValidatingReader.cs index a6e199b9..4e2f691f 100644 --- a/Src/Newtonsoft.Json/JsonValidatingReader.cs +++ b/Src/Newtonsoft.Json/JsonValidatingReader.cs @@ -344,6 +344,18 @@ namespace Newtonsoft.Json } } + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . + public override int? ReadAsInt32() + { + int? i = _reader.ReadAsInt32(); + + ValidateCurrentToken(); + return i; + } + /// /// Reads the next JSON token from the stream as a . /// diff --git a/Src/Newtonsoft.Json/Linq/JTokenReader.cs b/Src/Newtonsoft.Json/Linq/JTokenReader.cs index 807f044d..e52fc75b 100644 --- a/Src/Newtonsoft.Json/Linq/JTokenReader.cs +++ b/Src/Newtonsoft.Json/Linq/JTokenReader.cs @@ -111,6 +111,28 @@ namespace Newtonsoft.Json.Linq throw new JsonReaderException("Error reading decimal. Expected a number but got {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType)); } + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . + public override int? ReadAsInt32() + { + Read(); + + if (TokenType == JsonToken.Integer || TokenType == JsonToken.Float) + { + SetToken(JsonToken.Integer, Convert.ToInt32(Value, CultureInfo.InvariantCulture)); + return (int)Value; + } + if (TokenType == JsonToken.Null) + return null; + + if (ReaderIsSerializerInArray()) + return null; + + throw new JsonReaderException("Error reading integer. Expected a number but got {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType)); + } + #if !NET20 /// /// Reads the next JSON token from the stream as a . diff --git a/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs b/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs index 4ab26140..f30643b9 100644 --- a/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs +++ b/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs @@ -258,87 +258,85 @@ namespace Newtonsoft.Json.Schema // todo: Add GetSchema to JsonConverter and use here? CurrentSchema.Type = JsonSchemaType.Any; } - else if (contract is JsonDictionaryContract) - { - CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired); - - Type keyType; - Type valueType; - ReflectionUtils.GetDictionaryKeyValueTypes(type, out keyType, out valueType); - - if (keyType != null) - { - // can be converted to a string - if (typeof (IConvertible).IsAssignableFrom(keyType)) - { - CurrentSchema.AdditionalProperties = GenerateInternal(valueType, Required.Default, false); - } - } - } - else if (contract is JsonArrayContract) - { - CurrentSchema.Type = AddNullType(JsonSchemaType.Array, valueRequired); - - CurrentSchema.Id = GetTypeId(type, false); - - JsonArrayAttribute arrayAttribute = JsonTypeReflector.GetJsonContainerAttribute(type) as JsonArrayAttribute; - bool allowNullItem = (arrayAttribute == null || arrayAttribute.AllowNullItems); - - Type collectionItemType = ReflectionUtils.GetCollectionItemType(type); - if (collectionItemType != null) - { - CurrentSchema.Items = new List(); - CurrentSchema.Items.Add(GenerateInternal(collectionItemType, (!allowNullItem) ? Required.Always : Required.Default, false)); - } - } - else if (contract is JsonStringContract) - { - JsonSchemaType schemaType = (!ReflectionUtils.IsNullable(contract.UnderlyingType)) - ? JsonSchemaType.String - : AddNullType(JsonSchemaType.String, valueRequired); - - CurrentSchema.Type = schemaType; - } - else if (contract is JsonPrimitiveContract) - { - CurrentSchema.Type = GetJsonSchemaType(type, valueRequired); - - if (CurrentSchema.Type == JsonSchemaType.Integer && type.IsEnum && !type.IsDefined(typeof(FlagsAttribute), true)) - { - CurrentSchema.Enum = new List(); - CurrentSchema.Options = new Dictionary(); - - EnumValues enumValues = EnumUtils.GetNamesAndValues(type); - foreach (EnumValue enumValue in enumValues) - { - JToken value = JToken.FromObject(enumValue.Value); - - CurrentSchema.Enum.Add(value); - CurrentSchema.Options.Add(value, enumValue.Name); - } - } - } - else if (contract is JsonObjectContract) - { - CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired); - CurrentSchema.Id = GetTypeId(type, false); - GenerateObjectSchema(type, (JsonObjectContract)contract); - } -#if !SILVERLIGHT && !PocketPC - else if (contract is JsonISerializableContract) - { - CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired); - CurrentSchema.Id = GetTypeId(type, false); - GenerateISerializableContract(type, (JsonISerializableContract) contract); - } -#endif - else if (contract is JsonLinqContract) - { - CurrentSchema.Type = JsonSchemaType.Any; - } else { - throw new Exception("Unexpected contract type: {0}".FormatWith(CultureInfo.InvariantCulture, contract)); + switch (contract.ContractType) + { + case JsonContractType.Object: + CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired); + CurrentSchema.Id = GetTypeId(type, false); + GenerateObjectSchema(type, (JsonObjectContract) contract); + break; + case JsonContractType.Array: + CurrentSchema.Type = AddNullType(JsonSchemaType.Array, valueRequired); + + CurrentSchema.Id = GetTypeId(type, false); + + JsonArrayAttribute arrayAttribute = JsonTypeReflector.GetJsonContainerAttribute(type) as JsonArrayAttribute; + bool allowNullItem = (arrayAttribute == null || arrayAttribute.AllowNullItems); + + Type collectionItemType = ReflectionUtils.GetCollectionItemType(type); + if (collectionItemType != null) + { + CurrentSchema.Items = new List(); + CurrentSchema.Items.Add(GenerateInternal(collectionItemType, (!allowNullItem) ? Required.Always : Required.Default, false)); + } + break; + case JsonContractType.Primitive: + CurrentSchema.Type = GetJsonSchemaType(type, valueRequired); + + if (CurrentSchema.Type == JsonSchemaType.Integer && type.IsEnum && !type.IsDefined(typeof (FlagsAttribute), true)) + { + CurrentSchema.Enum = new List(); + CurrentSchema.Options = new Dictionary(); + + EnumValues enumValues = EnumUtils.GetNamesAndValues(type); + foreach (EnumValue enumValue in enumValues) + { + JToken value = JToken.FromObject(enumValue.Value); + + CurrentSchema.Enum.Add(value); + CurrentSchema.Options.Add(value, enumValue.Name); + } + } + break; + case JsonContractType.String: + JsonSchemaType schemaType = (!ReflectionUtils.IsNullable(contract.UnderlyingType)) + ? JsonSchemaType.String + : AddNullType(JsonSchemaType.String, valueRequired); + + CurrentSchema.Type = schemaType; + break; + case JsonContractType.Dictionary: + CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired); + + Type keyType; + Type valueType; + ReflectionUtils.GetDictionaryKeyValueTypes(type, out keyType, out valueType); + + if (keyType != null) + { + // can be converted to a string + if (typeof (IConvertible).IsAssignableFrom(keyType)) + { + CurrentSchema.AdditionalProperties = GenerateInternal(valueType, Required.Default, false); + } + } + break; +#if !SILVERLIGHT && !PocketPC + case JsonContractType.Serializable: + CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired); + CurrentSchema.Id = GetTypeId(type, false); + GenerateISerializableContract(type, (JsonISerializableContract) contract); + break; +#endif + case JsonContractType.Dynamic: + case JsonContractType.Linq: + CurrentSchema.Type = JsonSchemaType.Any; + break; + default: + throw new Exception("Unexpected contract type: {0}".FormatWith(CultureInfo.InvariantCulture, contract)); + } } return Pop().Schema; diff --git a/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs b/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs index 23db0c8f..7fd8b741 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs @@ -52,6 +52,8 @@ namespace Newtonsoft.Json.Serialization public JsonArrayContract(Type underlyingType) : base(underlyingType) { + ContractType = JsonContractType.Array; + if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(ICollection<>), out _genericCollectionDefinitionType)) { CollectionItemType = _genericCollectionDefinitionType.GetGenericArguments()[0]; diff --git a/Src/Newtonsoft.Json/Serialization/JsonContract.cs b/Src/Newtonsoft.Json/Serialization/JsonContract.cs index f8b1a4ea..ef450b62 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonContract.cs @@ -30,20 +30,44 @@ using Newtonsoft.Json.Utilities; namespace Newtonsoft.Json.Serialization { + internal enum JsonContractType + { + None, + Object, + Array, + Primitive, + String, + Dictionary, +#if !(NET35 || NET20 || WINDOWS_PHONE) + Dynamic, +#endif +#if !SILVERLIGHT && !PocketPC + Serializable, +#endif + Linq + } + + internal enum ReadType + { + Read, + ReadAsInt32, + ReadAsDecimal, + ReadAsBytes, +#if !NET20 + ReadAsDateTimeOffset +#endif + } + /// /// Contract details for a used by the . /// public abstract class JsonContract { - internal enum ReadType - { - Read, - ReadAsDecimal, - ReadAsBytes, -#if !NET20 - ReadAsDateTimeOffset -#endif - } + internal bool IsNullable; + internal bool IsConvertable; + internal Type NonNullableUnderlyingType; + internal ReadType InternalReadType; + internal JsonContractType ContractType; /// /// Gets the underlying type for the contract. @@ -73,11 +97,6 @@ namespace Newtonsoft.Json.Serialization // checked for after passed in converters and attribute specified converters internal JsonConverter InternalConverter { get; set; } - internal ReadType InternalReadType; - internal bool IsNullable; - internal bool IsConvertable; - internal Type NonNullableUnderlyingType; - #if !PocketPC /// /// Gets or sets the method called immediately after deserialization of the object. @@ -177,6 +196,10 @@ namespace Newtonsoft.Json.Serialization { InternalReadType = ReadType.ReadAsBytes; } + else if (NonNullableUnderlyingType == typeof(int)) + { + InternalReadType = ReadType.ReadAsInt32; + } else if (NonNullableUnderlyingType == typeof(decimal)) { InternalReadType = ReadType.ReadAsDecimal; diff --git a/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs b/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs index 16c67fe7..975c9371 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs @@ -60,6 +60,8 @@ namespace Newtonsoft.Json.Serialization public JsonDictionaryContract(Type underlyingType) : base(underlyingType) { + ContractType = JsonContractType.Dictionary; + Type keyType; Type valueType; if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(IDictionary<,>), out _genericCollectionDefinitionType)) diff --git a/Src/Newtonsoft.Json/Serialization/JsonDynamicContract.cs b/Src/Newtonsoft.Json/Serialization/JsonDynamicContract.cs index 75f08bea..2645c3ae 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonDynamicContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonDynamicContract.cs @@ -56,6 +56,8 @@ namespace Newtonsoft.Json.Serialization public JsonDynamicContract(Type underlyingType) : base(underlyingType) { + ContractType = JsonContractType.Dynamic; + Properties = new JsonPropertyCollection(UnderlyingType); } } diff --git a/Src/Newtonsoft.Json/Serialization/JsonISerializableContract.cs b/Src/Newtonsoft.Json/Serialization/JsonISerializableContract.cs index ee1eadb1..c033f766 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonISerializableContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonISerializableContract.cs @@ -50,6 +50,7 @@ namespace Newtonsoft.Json.Serialization public JsonISerializableContract(Type underlyingType) : base(underlyingType) { + ContractType = JsonContractType.Serializable; } } } diff --git a/Src/Newtonsoft.Json/Serialization/JsonLinqContract.cs b/Src/Newtonsoft.Json/Serialization/JsonLinqContract.cs index bc3526ff..1f100884 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonLinqContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonLinqContract.cs @@ -17,6 +17,7 @@ namespace Newtonsoft.Json.Serialization public JsonLinqContract(Type underlyingType) : base(underlyingType) { + ContractType = JsonContractType.Linq; } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs index 426a469f..ef249c5b 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs @@ -44,10 +44,10 @@ namespace Newtonsoft.Json.Serialization /// /// The object's properties. public JsonPropertyCollection Properties { get; private set; } - - /// - /// Gets the constructor parameters required for any non-default constructor - /// + + /// + /// Gets the constructor parameters required for any non-default constructor + /// public JsonPropertyCollection ConstructorParameters { get; private set; } /// @@ -71,8 +71,10 @@ namespace Newtonsoft.Json.Serialization public JsonObjectContract(Type underlyingType) : base(underlyingType) { - Properties = new JsonPropertyCollection(UnderlyingType); - ConstructorParameters = new JsonPropertyCollection(UnderlyingType); + ContractType = JsonContractType.Object; + + Properties = new JsonPropertyCollection(UnderlyingType); + ConstructorParameters = new JsonPropertyCollection(UnderlyingType); } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonPrimitiveContract.cs b/Src/Newtonsoft.Json/Serialization/JsonPrimitiveContract.cs index 60bc83ad..040613ab 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonPrimitiveContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonPrimitiveContract.cs @@ -39,6 +39,7 @@ namespace Newtonsoft.Json.Serialization public JsonPrimitiveContract(Type underlyingType) : base(underlyingType) { + ContractType = JsonContractType.Primitive; } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs index cc1953b2..a0f1d2f4 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -64,7 +64,7 @@ namespace Newtonsoft.Json.Serialization if (reader.TokenType == JsonToken.StartArray) { - if (contract is JsonArrayContract) + if (contract.ContractType == JsonContractType.Array) PopulateList(CollectionUtils.CreateCollectionWrapper(target), reader, null, (JsonArrayContract) contract); else throw CreateSerializationException(reader, "Cannot populate JSON array onto type '{0}'.".FormatWith(CultureInfo.InvariantCulture, objectType)); @@ -81,9 +81,9 @@ namespace Newtonsoft.Json.Serialization CheckedRead(reader); } - if (contract is JsonDictionaryContract) + if (contract.ContractType == JsonContractType.Dictionary) PopulateDictionary(CollectionUtils.CreateDictionaryWrapper(target), reader, (JsonDictionaryContract) contract, id); - else if (contract is JsonObjectContract) + else if (contract.ContractType == JsonContractType.Object) PopulateObject(target, reader, (JsonObjectContract) contract, id); else throw CreateSerializationException(reader, "Cannot populate JSON object onto type '{0}'.".FormatWith(CultureInfo.InvariantCulture, objectType)); @@ -223,7 +223,7 @@ namespace Newtonsoft.Json.Serialization private object CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, object existingValue) { - if (contract is JsonLinqContract) + if (contract != null && contract.ContractType == JsonContractType.Linq) return CreateJToken(reader, contract); do @@ -425,53 +425,43 @@ namespace Newtonsoft.Json.Serialization if (contract == null) throw CreateSerializationException(reader, "Could not resolve type '{0}' to a JsonContract.".FormatWith(CultureInfo.InvariantCulture, objectType)); - JsonDictionaryContract dictionaryContract = contract as JsonDictionaryContract; - if (dictionaryContract != null) + switch (contract.ContractType) { - if (existingValue == null) - return CreateAndPopulateDictionary(reader, dictionaryContract, id); + case JsonContractType.Object: + JsonObjectContract objectContract = (JsonObjectContract) contract; + if (existingValue == null) + return CreateAndPopulateObject(reader, objectContract, id); - return PopulateDictionary(dictionaryContract.CreateWrapper(existingValue), reader, dictionaryContract, id); - } + return PopulateObject(existingValue, reader, objectContract, id); + case JsonContractType.Primitive: + JsonPrimitiveContract primitiveContract = (JsonPrimitiveContract) contract; + // if the content is inside $value then read past it + if (reader.TokenType == JsonToken.PropertyName && string.Equals(reader.Value.ToString(), JsonTypeReflector.ValuePropertyName, StringComparison.Ordinal)) + { + CheckedRead(reader); + object value = CreateValueInternal(reader, objectType, primitiveContract, member, existingValue); - JsonObjectContract objectContract = contract as JsonObjectContract; - if (objectContract != null) - { - if (existingValue == null) - return CreateAndPopulateObject(reader, objectContract, id); - - return PopulateObject(existingValue, reader, objectContract, id); - } - - JsonPrimitiveContract primitiveContract = contract as JsonPrimitiveContract; - if (primitiveContract != null) - { - // if the content is inside $value then read past it - if (reader.TokenType == JsonToken.PropertyName && string.Equals(reader.Value.ToString(), JsonTypeReflector.ValuePropertyName, StringComparison.Ordinal)) - { - CheckedRead(reader); - object value = CreateValueInternal(reader, objectType, primitiveContract, member, existingValue); - - CheckedRead(reader); - return value; - } - } - -#if !SILVERLIGHT && !PocketPC - JsonISerializableContract serializableContract = contract as JsonISerializableContract; - if (serializableContract != null) - { - return CreateISerializable(reader, serializableContract, id); - } -#endif + CheckedRead(reader); + return value; + } + break; + case JsonContractType.Dictionary: + JsonDictionaryContract dictionaryContract = (JsonDictionaryContract) contract; + if (existingValue == null) + return CreateAndPopulateDictionary(reader, dictionaryContract, id); + return PopulateDictionary(dictionaryContract.CreateWrapper(existingValue), reader, dictionaryContract, id); #if !(NET35 || NET20 || WINDOWS_PHONE) - JsonDynamicContract dynamicContract = contract as JsonDynamicContract; - if (dynamicContract != null) - { - return CreateDynamic(reader, dynamicContract, id); - } + case JsonContractType.Dynamic: + JsonDynamicContract dynamicContract = (JsonDynamicContract) contract; + return CreateDynamic(reader, dynamicContract, id); #endif +#if !SILVERLIGHT && !PocketPC + case JsonContractType.Serializable: + JsonISerializableContract serializableContract = (JsonISerializableContract) contract; + return CreateISerializable(reader, serializableContract, id); +#endif + } throw CreateSerializationException(reader, "Cannot deserialize JSON object into type '{0}'.".FormatWith(CultureInfo.InvariantCulture, objectType)); } @@ -1009,7 +999,7 @@ namespace Newtonsoft.Json.Serialization // handle readonly collection/dictionary properties JsonContract propertyContract = Serializer.ContractResolver.ResolveContract(property.PropertyType); - if (propertyContract is JsonArrayContract) + if (propertyContract.ContractType == JsonContractType.Array) { JsonArrayContract propertyArrayContract = propertyContract as JsonArrayContract; @@ -1025,7 +1015,7 @@ namespace Newtonsoft.Json.Serialization } } } - else if (propertyContract is JsonDictionaryContract) + else if (propertyContract.ContractType == JsonContractType.Dictionary) { JsonDictionaryContract jsonDictionaryContract = propertyContract as JsonDictionaryContract; @@ -1116,11 +1106,11 @@ namespace Newtonsoft.Json.Serialization if (hasConverter) return reader.Read(); - JsonContract.ReadType t = (contract != null) ? contract.InternalReadType : JsonContract.ReadType.Read; + ReadType t = (contract != null) ? contract.InternalReadType : ReadType.Read; switch (t) { - case JsonContract.ReadType.Read: + case ReadType.Read: do { if (!reader.Read()) @@ -1128,20 +1118,26 @@ namespace Newtonsoft.Json.Serialization } while (reader.TokenType == JsonToken.Comment); return true; - case JsonContract.ReadType.ReadAsDecimal: + case ReadType.ReadAsInt32: + SetSerializeInArray(reader, inArray, true); + reader.ReadAsInt32(); + SetSerializeInArray(reader, inArray, false); + + return true; + case ReadType.ReadAsDecimal: SetSerializeInArray(reader, inArray, true); reader.ReadAsDecimal(); SetSerializeInArray(reader, inArray, false); return true; - case JsonContract.ReadType.ReadAsBytes: + case ReadType.ReadAsBytes: SetSerializeInArray(reader, inArray, true); reader.ReadAsBytes(); SetSerializeInArray(reader, inArray, false); return true; #if !NET20 - case JsonContract.ReadType.ReadAsDateTimeOffset: + case ReadType.ReadAsDateTimeOffset: SetSerializeInArray(reader, inArray, true); reader.ReadAsDateTimeOffset(); SetSerializeInArray(reader, inArray, false); diff --git a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs index 5ac8dcd4..a77f83c2 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -105,58 +105,55 @@ namespace Newtonsoft.Json.Serialization } if ((converter != null - || ((converter = valueContract.Converter) != null) - || ((converter = Serializer.GetMatchingConverter(valueContract.UnderlyingType)) != null) - || ((converter = valueContract.InternalConverter) != null)) - && converter.CanWrite) + || ((converter = valueContract.Converter) != null) + || ((converter = Serializer.GetMatchingConverter(valueContract.UnderlyingType)) != null) + || ((converter = valueContract.InternalConverter) != null)) + && converter.CanWrite) { SerializeConvertable(writer, converter, value, valueContract); + return; } - else if (valueContract is JsonStringContract) + + switch (valueContract.ContractType) { - SerializeString(writer, value, (JsonStringContract)valueContract); - } - else if (valueContract is JsonPrimitiveContract) - { - SerializePrimitive(writer, value, (JsonPrimitiveContract)valueContract, member, collectionValueContract); - } - else if (valueContract is JsonObjectContract) - { - SerializeObject(writer, value, (JsonObjectContract)valueContract, member, collectionValueContract); - } - else if (valueContract is JsonDictionaryContract) - { - JsonDictionaryContract dictionaryContract = (JsonDictionaryContract)valueContract; - SerializeDictionary(writer, dictionaryContract.CreateWrapper(value), dictionaryContract, member, collectionValueContract); - } - else if (valueContract is JsonArrayContract) - { - JsonArrayContract arrayContract = (JsonArrayContract)valueContract; - SerializeList(writer, arrayContract.CreateWrapper(value), arrayContract, member, collectionValueContract); - } - else if (valueContract is JsonLinqContract) - { - ((JToken)value).WriteTo(writer, (Serializer.Converters != null) ? Serializer.Converters.ToArray() : null); - } -#if !SILVERLIGHT && !PocketPC - else if (valueContract is JsonISerializableContract) - { - SerializeISerializable(writer, (ISerializable)value, (JsonISerializableContract)valueContract, member, collectionValueContract); - } -#endif + case JsonContractType.Object: + SerializeObject(writer, value, (JsonObjectContract) valueContract, member, collectionValueContract); + break; + case JsonContractType.Array: + JsonArrayContract arrayContract = (JsonArrayContract) valueContract; + SerializeList(writer, arrayContract.CreateWrapper(value), arrayContract, member, collectionValueContract); + break; + case JsonContractType.Primitive: + SerializePrimitive(writer, value, (JsonPrimitiveContract) valueContract, member, collectionValueContract); + break; + case JsonContractType.String: + SerializeString(writer, value, (JsonStringContract) valueContract); + break; + case JsonContractType.Dictionary: + JsonDictionaryContract dictionaryContract = (JsonDictionaryContract) valueContract; + SerializeDictionary(writer, dictionaryContract.CreateWrapper(value), dictionaryContract, member, collectionValueContract); + break; #if !(NET35 || NET20 || WINDOWS_PHONE) - else if (valueContract is JsonDynamicContract) - { - SerializeDynamic(writer, (IDynamicMetaObjectProvider)value, (JsonDynamicContract)valueContract); - } + case JsonContractType.Dynamic: + SerializeDynamic(writer, (IDynamicMetaObjectProvider) value, (JsonDynamicContract) valueContract); + break; #endif +#if !SILVERLIGHT && !PocketPC + case JsonContractType.Serializable: + SerializeISerializable(writer, (ISerializable) value, (JsonISerializableContract) valueContract, member, collectionValueContract); + break; +#endif + case JsonContractType.Linq: + ((JToken) value).WriteTo(writer, (Serializer.Converters != null) ? Serializer.Converters.ToArray() : null); + break; + } } private bool ShouldWriteReference(object value, JsonProperty property, JsonContract contract) { if (value == null) return false; - if (contract is JsonPrimitiveContract) + if (contract.ContractType == JsonContractType.Primitive || contract.ContractType == JsonContractType.String) return false; bool? isReference = null; @@ -170,7 +167,7 @@ namespace Newtonsoft.Json.Serialization if (isReference == null) { - if (contract is JsonArrayContract) + if (contract.ContractType == JsonContractType.Array) isReference = HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Arrays); else isReference = HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Objects); @@ -214,7 +211,7 @@ namespace Newtonsoft.Json.Serialization private bool CheckForCircularReference(object value, ReferenceLoopHandling? referenceLoopHandling, JsonContract contract) { - if (value == null || contract is JsonPrimitiveContract) + if (value == null || contract.ContractType == JsonContractType.Primitive || contract.ContractType == JsonContractType.String) return true; if (_serializeStack.IndexOf(value) != -1) diff --git a/Src/Newtonsoft.Json/Serialization/JsonStringContract.cs b/Src/Newtonsoft.Json/Serialization/JsonStringContract.cs index c608d9ea..b76925ec 100644 --- a/Src/Newtonsoft.Json/Serialization/JsonStringContract.cs +++ b/Src/Newtonsoft.Json/Serialization/JsonStringContract.cs @@ -42,6 +42,7 @@ namespace Newtonsoft.Json.Serialization public JsonStringContract(Type underlyingType) : base(underlyingType) { + ContractType = JsonContractType.String; } } } \ No newline at end of file