diff --git a/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs b/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs index 5680279b..6d03bb2b 100644 --- a/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonSerializerTest.cs @@ -118,6 +118,30 @@ namespace Newtonsoft.Json.Tests White } + public class ClassWithArray + { + private readonly IList bar; + private string foo; + + public ClassWithArray() + { + bar = new List() { int.MaxValue }; + } + + [JsonProperty("foo")] + public string Foo + { + get { return foo; } + set { foo = value; } + } + + [JsonProperty("bar")] + public IList Bar + { + get { return bar; } + } + } + public class JsonSerializerTest : TestFixtureBase { [Test] @@ -311,6 +335,9 @@ namespace Newtonsoft.Json.Tests { get { return _ignoredProperty; } } + + [JsonIgnore] + public Product IgnoredObject = new Product(); } [Test] @@ -320,7 +347,7 @@ namespace Newtonsoft.Json.Tests Assert.AreEqual(@"{""Field"":0,""Property"":21}", json); - JsonIgnoreAttributeTestClass c = JavaScriptConvert.DeserializeObject(@"{""Field"":99,""Property"":-1,""IgnoredField"":-1}"); + JsonIgnoreAttributeTestClass c = JavaScriptConvert.DeserializeObject(@"{""Field"":99,""Property"":-1,""IgnoredField"":-1,""IgnoredObject"":[1,2,3,4,5]}"); Assert.AreEqual(0, c.IgnoredField); Assert.AreEqual(99, c.Field); @@ -780,5 +807,112 @@ keyword such as type of business."" string json = JavaScriptConvert.SerializeObject(null); Assert.AreEqual("null", json); } + + [Test] + public void CanDeserializeIntArrayWhenNotFirstPropertyInJson() + { + string json = "{foo:'hello',bar:[1,2,3]}"; + ClassWithArray wibble = JavaScriptConvert.DeserializeObject(json); + Assert.AreEqual("hello", wibble.Foo); + + Assert.AreEqual(4, wibble.Bar.Count); + Assert.AreEqual(int.MaxValue, wibble.Bar[0]); + Assert.AreEqual(1, wibble.Bar[1]); + Assert.AreEqual(2, wibble.Bar[2]); + Assert.AreEqual(3, wibble.Bar[3]); + } + + [Test] + public void CanDeserializeIntArray_WhenArrayIsFirstPropertyInJson() + { + string json = "{bar:[1,2,3], foo:'hello'}"; + ClassWithArray wibble = JavaScriptConvert.DeserializeObject(json); + Assert.AreEqual("hello", wibble.Foo); + + Assert.AreEqual(4, wibble.Bar.Count); + Assert.AreEqual(int.MaxValue, wibble.Bar[0]); + Assert.AreEqual(1, wibble.Bar[1]); + Assert.AreEqual(2, wibble.Bar[2]); + Assert.AreEqual(3, wibble.Bar[3]); + } + + [Test] + public void ObjectCreationHandlingReplace() + { + string json = "{bar:[1,2,3], foo:'hello'}"; + + JsonSerializer s = new JsonSerializer(); + s.ObjectCreationHandling = ObjectCreationHandling.Replace; + + ClassWithArray wibble = (ClassWithArray)s.Deserialize(new StringReader(json), typeof(ClassWithArray)); + + Assert.AreEqual("hello", wibble.Foo); + + Assert.AreEqual(1, wibble.Bar.Count); + } + + [Test] + public void CanDeserializeSerializedJson() + { + ClassWithArray wibble = new ClassWithArray(); + wibble.Foo = "hello"; + wibble.Bar.Add(1); + wibble.Bar.Add(2); + wibble.Bar.Add(3); + string json = JavaScriptConvert.SerializeObject(wibble); + + ClassWithArray wibbleOut = JavaScriptConvert.DeserializeObject(json); + Assert.AreEqual("hello", wibbleOut.Foo); + + Assert.AreEqual(5, wibbleOut.Bar.Count); + Assert.AreEqual(int.MaxValue, wibbleOut.Bar[0]); + Assert.AreEqual(int.MaxValue, wibbleOut.Bar[1]); + Assert.AreEqual(1, wibbleOut.Bar[2]); + Assert.AreEqual(2, wibbleOut.Bar[3]); + Assert.AreEqual(3, wibbleOut.Bar[4]); + } + + public class ConverableMembers + { + public string String = "string"; + public int Int32 = int.MaxValue; + public uint UInt32 = uint.MaxValue; + public byte Byte = byte.MaxValue; + public sbyte SByte = sbyte.MaxValue; + public short Short = short.MaxValue; + public ushort UShort = ushort.MaxValue; + public long Long = long.MaxValue; + public ulong ULong = long.MaxValue; + public double Double = double.MaxValue; + public float Float = float.MaxValue; + public DBNull DBNull = DBNull.Value; + public bool Bool = true; + public char Char = '\0'; + } + + [Test] + public void SerializeConverableObjects() + { + string json = JavaScriptConvert.SerializeObject(new ConverableMembers()); + + Assert.AreEqual(@"{""String"":""string"",""Int32"":2147483647,""UInt32"":4294967295,""Byte"":255,""SByte"":127,""Short"":32767,""UShort"":65535,""Long"":9223372036854775807,""ULong"":9223372036854775807,""Double"":1.7976931348623157E+308,""Float"":3.40282347E+38,""DBNull"":null,""Bool"":true,""Char"":""\u0000""}", json); + + ConverableMembers c = JavaScriptConvert.DeserializeObject(json); + Assert.AreEqual("string", c.String); + Assert.AreEqual(double.MaxValue, c.Double); + Assert.AreEqual(DBNull.Value, c.DBNull); + } + + [Test] + public void SerializeStack() + { + Stack s = new Stack(); + s.Push(1); + s.Push(2); + s.Push(3); + + string json = JavaScriptConvert.SerializeObject(s); + Assert.AreEqual("[3,2,1]", json); + } } } \ No newline at end of file diff --git a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs index 05c9f302..0d5b9a60 100644 --- a/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs +++ b/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs @@ -120,6 +120,57 @@ namespace Newtonsoft.Json.Tests } } + [Test] + public void Depth() + { + string input = "{value:'Purple\\r \\n monkey\\'s:\\tdishwasher',array:[1,2]}"; + + StringReader sr = new StringReader(input); + + using (JsonReader jsonReader = new JsonTextReader(sr)) + { + Assert.AreEqual(0, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.StartObject); + Assert.AreEqual(0, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName); + Assert.AreEqual(1, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.String); + Assert.AreEqual(jsonReader.Value, "Purple\r \n monkey's:\tdishwasher"); + Assert.AreEqual(jsonReader.QuoteChar, '\''); + Assert.AreEqual(1, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName); + Assert.AreEqual(1, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.StartArray); + Assert.AreEqual(2, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.Integer); + Assert.AreEqual(3, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.Integer); + Assert.AreEqual(3, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.EndArray); + Assert.AreEqual(1, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.EndObject); + Assert.AreEqual(0, jsonReader.Depth); + } + } + [Test] public void ReadingEscapedStrings() { @@ -129,19 +180,25 @@ namespace Newtonsoft.Json.Tests using (JsonReader jsonReader = new JsonTextReader(sr)) { + Assert.AreEqual(0, jsonReader.Depth); + jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.StartObject); + Assert.AreEqual(0, jsonReader.Depth); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName); + Assert.AreEqual(1, jsonReader.Depth); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.String); Assert.AreEqual(jsonReader.Value, "Purple\r \n monkey's:\tdishwasher"); Assert.AreEqual(jsonReader.QuoteChar, '\''); + Assert.AreEqual(1, jsonReader.Depth); jsonReader.Read(); Assert.AreEqual(jsonReader.TokenType, JsonToken.EndObject); + Assert.AreEqual(0, jsonReader.Depth); } } diff --git a/Src/Newtonsoft.Json/JavaScriptConvert.cs b/Src/Newtonsoft.Json/JavaScriptConvert.cs index b9bb53f0..a40281fc 100644 --- a/Src/Newtonsoft.Json/JavaScriptConvert.cs +++ b/Src/Newtonsoft.Json/JavaScriptConvert.cs @@ -252,7 +252,7 @@ namespace Newtonsoft.Json /// A Json string representation of the . public static string ToString(float value) { - return value.ToString(null, CultureInfo.InvariantCulture); + return value.ToString("R", CultureInfo.InvariantCulture); } /// @@ -262,7 +262,7 @@ namespace Newtonsoft.Json /// A Json string representation of the . public static string ToString(double value) { - return value.ToString(null, CultureInfo.InvariantCulture); + return value.ToString("R", CultureInfo.InvariantCulture); } /// diff --git a/Src/Newtonsoft.Json/JsonReader.cs b/Src/Newtonsoft.Json/JsonReader.cs index 7dea5e65..b50027dc 100644 --- a/Src/Newtonsoft.Json/JsonReader.cs +++ b/Src/Newtonsoft.Json/JsonReader.cs @@ -107,7 +107,14 @@ namespace Newtonsoft.Json public int Depth { - get { return _top - 1; } + get + { + int depth = _top - 1; + if (IsStartToken(TokenType)) + return depth - 1; + else + return depth; + } } /// @@ -148,6 +155,18 @@ namespace Newtonsoft.Json /// public abstract bool Read(); + public void Skip() + { + if (IsStartToken(TokenType)) + { + int depth = Depth; + + while (Read() && (depth < Depth)) + { + } + } + } + protected void SetToken(JsonToken newToken) { SetToken(newToken, null); @@ -185,6 +204,7 @@ namespace Newtonsoft.Json break; case JsonToken.PropertyName: _currentState = State.Property; + Push(JsonTokenType.Property); break; case JsonToken.Undefined: case JsonToken.Integer: @@ -197,6 +217,10 @@ namespace Newtonsoft.Json break; } + JsonTokenType current = Peek(); + if (current == JsonTokenType.Property && _currentState == State.PostValue) + Pop(); + if (value != null) { _value = value; @@ -216,7 +240,7 @@ namespace Newtonsoft.Json if (GetTypeForCloseToken(endToken) != currentObject) throw new JsonReaderException(string.Format("JsonToken {0} is not valid for closing JsonType {1}.", endToken, currentObject)); } - + protected void SetStateBasedOnCurrent() { JsonTokenType currentObject = Peek(); @@ -240,6 +264,33 @@ namespace Newtonsoft.Json } } + private bool IsStartToken(JsonToken token) + { + switch (token) + { + case JsonToken.StartObject: + case JsonToken.StartArray: + case JsonToken.StartConstructor: + case JsonToken.PropertyName: + return true; + case JsonToken.None: + case JsonToken.Comment: + case JsonToken.Integer: + case JsonToken.Float: + case JsonToken.String: + case JsonToken.Boolean: + case JsonToken.Null: + case JsonToken.Undefined: + case JsonToken.EndObject: + case JsonToken.EndArray: + case JsonToken.EndConstructor: + case JsonToken.Date: + return false; + default: + throw new ArgumentOutOfRangeException("token", token, "Unexpected JsonToken value."); + } + } + private JsonTokenType GetTypeForCloseToken(JsonToken token) { switch (token) diff --git a/Src/Newtonsoft.Json/JsonSerializer.cs b/Src/Newtonsoft.Json/JsonSerializer.cs index 633d00c6..b529adeb 100644 --- a/Src/Newtonsoft.Json/JsonSerializer.cs +++ b/Src/Newtonsoft.Json/JsonSerializer.cs @@ -34,6 +34,7 @@ using Newtonsoft.Json.Utilities; using System.Globalization; using System.Linq; using Newtonsoft.Json.Linq; +using System.Collections.ObjectModel; namespace Newtonsoft.Json { @@ -45,6 +46,7 @@ namespace Newtonsoft.Json { private ReferenceLoopHandling _referenceLoopHandling; private MissingMemberHandling _missingMemberHandling; + private ObjectCreationHandling _objectCreationHandling; private NullValueHandling _nullValueHandling; private int _level; private JsonConverterCollection _converters; @@ -95,6 +97,18 @@ namespace Newtonsoft.Json } } + public ObjectCreationHandling ObjectCreationHandling + { + get { return _objectCreationHandling; } + set + { + if (value < ObjectCreationHandling.Auto || value > ObjectCreationHandling.Replace) + throw new ArgumentOutOfRangeException("value"); + + _objectCreationHandling = value; + } + } + public JsonConverterCollection Converters { get @@ -113,6 +127,8 @@ namespace Newtonsoft.Json { _referenceLoopHandling = ReferenceLoopHandling.Error; _missingMemberHandling = MissingMemberHandling.Error; + _nullValueHandling = NullValueHandling.Include; + _objectCreationHandling = ObjectCreationHandling.Auto; } #region Deserialize @@ -142,11 +158,16 @@ namespace Newtonsoft.Json return null; if (objectType != null) - return GetObject(reader, objectType); + return CreateObject(reader, objectType, null); else return CreateJToken(reader); } + public object Deserialize(StringReader reader, Type objectType) + { + return Deserialize(new JsonTextReader(reader), objectType); + } + private JToken CreateJToken(JsonReader reader) { JToken token; @@ -159,7 +180,7 @@ namespace Newtonsoft.Json return token; } - private object GetObject(JsonReader reader, Type objectType) + private object CreateObject(JsonReader reader, Type objectType, object existingValue) { _level++; @@ -177,10 +198,37 @@ namespace Newtonsoft.Json // populate a typed object or generic dictionary/array // depending upon whether an objectType was supplied case JsonToken.StartObject: - value = (objectType != null) ? PopulateObject(reader, objectType) : CreateJToken(reader); + if (objectType == null) + { + value = CreateJToken(reader); + } + else if (typeof(IDictionary).IsAssignableFrom(objectType) || ReflectionUtils.IsSubClass(objectType, typeof(IDictionary<,>))) + { + if (existingValue == null) + value = CreateAndPopulateDictionary(reader, objectType); + else + value = PopulateDictionary(CollectionUtils.CreateDictionaryWrapper(existingValue), reader); + } + else + { + if (existingValue == null) + value = CreateAndPopulateObject(reader, objectType); + else + value = PopulateObject(existingValue, reader, objectType); + } break; case JsonToken.StartArray: - value = (objectType != null) ? PopulateList(reader, objectType) : CreateJToken(reader); + if (objectType != null) + { + if (existingValue == null) + value = CreateAndPopulateList(reader, objectType); + else + value = PopulateList(CollectionUtils.CreateListWrapper(existingValue), ReflectionUtils.GetListItemType(objectType), reader); + } + else + { + value = CreateJToken(reader); + } break; case JsonToken.Integer: case JsonToken.Float: @@ -197,7 +245,10 @@ namespace Newtonsoft.Json break; case JsonToken.Null: case JsonToken.Undefined: - value = null; + if (objectType == typeof(DBNull)) + value = DBNull.Value; + else + value = null; break; default: throw new JsonSerializationException("Unexpected token while deserializing object: " + reader.TokenType); @@ -209,25 +260,6 @@ namespace Newtonsoft.Json return value; } - private DateTime CreateDate(JsonReader reader) - { - // move to parameter - if (!reader.Read()) - throw new JsonSerializationException("Unexpected end while creating date."); - - //TODO: add support for Date constructor with multiple arguments - long ticks = Convert.ToInt64(reader.Value); - DateTime date = JavaScriptConvert.ConvertJavaScriptTicksToDateTime(ticks); - - // move to constructor end - if (!reader.Read()) - throw new JsonSerializationException("Unexpected end while creating date."); - if (reader.TokenType != JsonToken.EndConstructor) - throw new JsonSerializationException("Unexpected token while creating date: " + reader.TokenType); - - return date; - } - private object EnsureType(object value, Type targetType) { // do something about null value when the targetType is a valuetype? @@ -329,32 +361,33 @@ namespace Newtonsoft.Json MemberMapping memberMapping = memberMappings[memberName]; if (memberMapping.Ignored) + { + reader.Skip(); return; - - // ignore member if it is readonly - if (!memberMapping.Writable) - return; + } // get the member's underlying type memberType = ReflectionUtils.GetMemberUnderlyingType(memberMapping.Member); + object currentValue = ReflectionUtils.GetMemberValue(memberMapping.Member, target); - value = GetObject(reader, memberType); + bool useExistingValue = (currentValue != null + && (_objectCreationHandling == ObjectCreationHandling.Auto || _objectCreationHandling == ObjectCreationHandling.Reuse) + && (reader.TokenType == JsonToken.StartArray || reader.TokenType == JsonToken.StartObject) + && (!memberType.IsArray && !ReflectionUtils.IsSubClass(memberType, typeof(ReadOnlyCollection<>)))); + + if (!memberMapping.Writable && !useExistingValue) + { + reader.Skip(); + return; + } + + value = CreateObject(reader, memberType, (useExistingValue) ? currentValue : null); if (_nullValueHandling == NullValueHandling.Ignore && value == null) return; - ReflectionUtils.SetMemberValue(memberMapping.Member, target, value); - } - else if (typeof(IDictionary).IsAssignableFrom(targetType)) - { - // attempt to get the IDictionary's type - memberType = ReflectionUtils.GetDictionaryValueType(targetType); - value = GetObject(reader, memberType); - - Type keyType = ReflectionUtils.GetDictionaryKeyType(targetType); - object keyValue = EnsureType(memberName, keyType); - - ((IDictionary)target).Add(keyValue, value); + if (!useExistingValue) + ReflectionUtils.SetMemberValue(memberMapping.Member, target, value); } else { @@ -363,35 +396,67 @@ namespace Newtonsoft.Json } } - private object PopulateList(JsonReader reader, Type objectType) + private object CreateAndPopulateDictionary(JsonReader reader, Type objectType) { - Type elementType = ReflectionUtils.GetListItemType(objectType); + IWrappedDictionary dictionary = CollectionUtils.CreateDictionaryWrapper(Activator.CreateInstance(objectType)); + PopulateDictionary(dictionary, reader); - IList populatedList = CollectionUtils.CreateAndPopulateList(objectType, delegate(IList list) - { - while (reader.Read()) - { - switch (reader.TokenType) - { - case JsonToken.EndArray: - return; - case JsonToken.Comment: - break; - default: - object value = GetObject(reader, elementType); - - list.Add(value); - break; - } - } - - throw new JsonSerializationException("Unexpected end when deserializing array."); - }); - - return populatedList; + return dictionary.UnderlyingDictionary; } - private object PopulateObject(JsonReader reader, Type objectType) + private IDictionary PopulateDictionary(IWrappedDictionary dictionary, JsonReader reader) + { + Type dictionaryType = dictionary.UnderlyingDictionary.GetType(); + Type dictionaryKeyType = ReflectionUtils.GetDictionaryKeyType(dictionaryType); + Type dictionaryValueType = ReflectionUtils.GetDictionaryValueType(dictionaryType); + + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + object keyValue = EnsureType(reader.Value, dictionaryKeyType); + reader.Read(); + + dictionary.Add(keyValue, CreateObject(reader, dictionaryValueType, null)); + break; + case JsonToken.EndObject: + return dictionary; + default: + throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType); + } + } + + throw new JsonSerializationException("Unexpected end when deserializing object."); + } + + private object CreateAndPopulateList(JsonReader reader, Type objectType) + { + return CollectionUtils.CreateAndPopulateList(objectType, l => PopulateList(l, ReflectionUtils.GetListItemType(objectType), reader)); + } + + private IList PopulateList(IList list, Type listItemType, JsonReader reader) + { + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.EndArray: + return list; + case JsonToken.Comment: + break; + default: + object value = CreateObject(reader, listItemType, null); + + list.Add(value); + break; + } + } + + throw new JsonSerializationException("Unexpected end when deserializing array."); + } + + private object CreateAndPopulateObject(JsonReader reader, Type objectType) { object newObject; @@ -399,23 +464,8 @@ namespace Newtonsoft.Json { newObject = Activator.CreateInstance(objectType); - while (reader.Read()) - { - switch (reader.TokenType) - { - case JsonToken.PropertyName: - string memberName = reader.Value.ToString(); - - SetObjectMember(reader, newObject, objectType, memberName); - break; - case JsonToken.EndObject: - return newObject; - default: - throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType); - } - } - - throw new JsonSerializationException("Unexpected end when deserializing object."); + PopulateObject(newObject, reader, objectType); + return newObject; } else { @@ -442,15 +492,9 @@ namespace Newtonsoft.Json if (!reader.Read()) throw new JsonSerializationException(string.Format("Unexpected end when setting {0}'s value.", memberName)); - if (matchingConstructorParameter != null) - { - constructorParameters[matchingConstructorParameter] = GetObject(reader, matchingConstructorParameter.ParameterType); - } + constructorParameters[matchingConstructorParameter] = CreateObject(reader, matchingConstructorParameter.ParameterType, null); - - - //SetObjectMember(reader, newObject, objectType, memberName); break; case JsonToken.EndObject: exit = true; @@ -465,6 +509,27 @@ namespace Newtonsoft.Json return newObject; } } + + private object PopulateObject(object newObject, JsonReader reader, Type objectType) + { + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + string memberName = reader.Value.ToString(); + + SetObjectMember(reader, newObject, objectType, memberName); + break; + case JsonToken.EndObject: + return newObject; + default: + throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType); + } + } + + throw new JsonSerializationException("Unexpected end when deserializing object."); + } #endregion #region Serialize @@ -560,6 +625,9 @@ namespace Newtonsoft.Json case TypeCode.Decimal: writer.WriteValue((decimal)convertible); break; + case TypeCode.DBNull: + writer.WriteNull(); + break; default: SerializeObject(writer, value); break; @@ -691,10 +759,7 @@ namespace Newtonsoft.Json private void SerializeCollection(JsonWriter writer, ICollection values) { - object[] collectionValues = new object[values.Count]; - values.CopyTo(collectionValues, 0); - - SerializeList(writer, collectionValues); + SerializeList(writer, values.Cast().ToList()); } private void SerializeList(JsonWriter writer, IList values) diff --git a/Src/Newtonsoft.Json/JsonTextReader.cs b/Src/Newtonsoft.Json/JsonTextReader.cs index d42afe9d..b9ece9e1 100644 --- a/Src/Newtonsoft.Json/JsonTextReader.cs +++ b/Src/Newtonsoft.Json/JsonTextReader.cs @@ -789,7 +789,7 @@ namespace Newtonsoft.Json /// /// Changes the to Closed. /// - public void Close() + public override void Close() { base.Close(); diff --git a/Src/Newtonsoft.Json/JsonWriter.cs b/Src/Newtonsoft.Json/JsonWriter.cs index 69e3f282..4651fea5 100644 --- a/Src/Newtonsoft.Json/JsonWriter.cs +++ b/Src/Newtonsoft.Json/JsonWriter.cs @@ -295,7 +295,7 @@ namespace Newtonsoft.Json ValidationUtils.ArgumentNotNull(reader, "reader"); int currentDepth = (reader.TokenType == JsonToken.None) ? -1 : reader.Depth; - bool continue1; + do { switch (reader.TokenType) diff --git a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj index 5ad7487f..0078628a 100644 --- a/Src/Newtonsoft.Json/Newtonsoft.Json.csproj +++ b/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -73,6 +73,7 @@ + @@ -109,6 +110,7 @@ + diff --git a/Src/Newtonsoft.Json/ObjectCreationHandling.cs b/Src/Newtonsoft.Json/ObjectCreationHandling.cs new file mode 100644 index 00000000..ec5048e2 --- /dev/null +++ b/Src/Newtonsoft.Json/ObjectCreationHandling.cs @@ -0,0 +1,39 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json +{ + public enum ObjectCreationHandling + { + Auto = 0, + Reuse = 1, + Replace = 2 + } +} \ No newline at end of file diff --git a/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs b/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs index 8c1d4a3a..79f1845f 100644 --- a/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs @@ -191,7 +191,7 @@ namespace Newtonsoft.Json.Utilities /// /// The list to add to. /// The collection of elements to add. - public static void AddRange(IList initial, IEnumerable collection) + public static void AddRange(this IList initial, IEnumerable collection) { if (initial == null) throw new ArgumentNullException("initial"); @@ -205,6 +205,14 @@ namespace Newtonsoft.Json.Utilities } } + public static void AddRange(this IList initial, IEnumerable collection) + { + ValidationUtils.ArgumentNotNull(initial, "initial"); + + ListWrapper wrapper = new ListWrapper(initial); + wrapper.AddRange(collection.Cast()); + } + public static List Distinct(List collection) { List distinctList = new List(); @@ -336,11 +344,11 @@ namespace Newtonsoft.Json.Utilities return tempList.ToArray(); } - public static object CreateGenericList(Type listType) + public static IList CreateGenericList(Type listType) { ValidationUtils.ArgumentNotNull(listType, "listType"); - return ReflectionUtils.CreateGeneric(typeof(List<>), listType); + return (IList)ReflectionUtils.CreateGeneric(typeof(List<>), listType); } public static bool IsListType(Type type) @@ -357,13 +365,70 @@ namespace Newtonsoft.Json.Utilities return false; } + public static IWrappedList CreateListWrapper(object list) + { + ValidationUtils.ArgumentNotNull(list, "list"); + + Type listDefinition; + if (ReflectionUtils.IsSubClass(list.GetType(), typeof(IList<>), out listDefinition)) + { + Type listItemType = ReflectionUtils.GetListItemType(listDefinition); + + // Activator.CreateInstance throws AmbiguousMatchException. Manually invoke constructor + Func, object> instanceCreator = (t, a) => + { + ConstructorInfo c = t.GetConstructor(new[] {listDefinition}); + return c.Invoke(new[] { list }); + }; + + return (IWrappedList)ReflectionUtils.CreateGeneric(typeof(ListWrapper<>), new[] { listItemType }, instanceCreator, list); + } + else if (list is IList) + { + return new ListWrapper((IList)list); + } + else + { + throw new Exception(string.Format("Can not create ListWrapper for type {0}.", list.GetType())); + } + } + + public static IWrappedDictionary CreateDictionaryWrapper(object dictionary) + { + ValidationUtils.ArgumentNotNull(dictionary, "dictionary"); + + Type dictionaryDefinition; + if (ReflectionUtils.IsSubClass(dictionary.GetType(), typeof(IDictionary<,>), out dictionaryDefinition)) + { + Type dictionaryKeyType = ReflectionUtils.GetDictionaryKeyType(dictionaryDefinition); + Type dictionaryValueType = ReflectionUtils.GetDictionaryValueType(dictionaryDefinition); + + // Activator.CreateInstance throws AmbiguousMatchException. Manually invoke constructor + Func, object> instanceCreator = (t, a) => + { + ConstructorInfo c = t.GetConstructor(new[] { dictionaryDefinition }); + return c.Invoke(new[] { dictionary }); + }; + + return (IWrappedDictionary)ReflectionUtils.CreateGeneric(typeof(DictionaryWrapper<,>), new[] { dictionaryKeyType, dictionaryValueType }, instanceCreator, dictionary); + } + else if (dictionary is IDictionary) + { + return new DictionaryWrapper((IDictionary)dictionary); + } + else + { + throw new Exception(string.Format("Can not create DictionaryWrapper for type {0}.", dictionary.GetType())); + } + } + public static IList CreateAndPopulateList(Type listType, Action populateList) { ValidationUtils.ArgumentNotNull(listType, "listType"); ValidationUtils.ArgumentNotNull(populateList, "populateList"); IList list; - Type readOnlyCollectionType; + Type collectionType; bool isReadOnlyOrFixedSize = false; if (listType.IsArray) @@ -373,9 +438,9 @@ namespace Newtonsoft.Json.Utilities list = new ArrayList(); isReadOnlyOrFixedSize = true; } - else if (ReflectionUtils.IsSubClass(listType, typeof(ReadOnlyCollection<>), out readOnlyCollectionType)) + else if (ReflectionUtils.IsSubClass(listType, typeof(ReadOnlyCollection<>), out collectionType)) { - Type readOnlyCollectionContentsType = readOnlyCollectionType.GetGenericArguments()[0]; + Type readOnlyCollectionContentsType = collectionType.GetGenericArguments()[0]; Type genericEnumerable = ReflectionUtils.MakeGenericType(typeof(IEnumerable<>), readOnlyCollectionContentsType); bool suitableConstructor = false; @@ -394,22 +459,34 @@ namespace Newtonsoft.Json.Utilities } if (!suitableConstructor) - throw new Exception(string.Format("Readonly type {0} does not have a public constructor that takes a type that implements {1}.", listType, genericEnumerable)); + throw new Exception(string.Format("Read-only type {0} does not have a public constructor that takes a type that implements {1}.", listType, genericEnumerable)); // can't add or modify a readonly list // use List and convert once populated list = (IList)CreateGenericList(readOnlyCollectionContentsType); isReadOnlyOrFixedSize = true; } - else if (typeof(IList).IsAssignableFrom(listType) && ReflectionUtils.IsInstantiatableType(listType)) + else if (typeof(IList).IsAssignableFrom(listType)) { - list = (IList)Activator.CreateInstance(listType); + if (ReflectionUtils.IsInstantiatableType(listType)) + list = (IList)Activator.CreateInstance(listType); + else if (listType == typeof(IList)) + list = new List(); + else + list = null; + } + else if (listType.IsGenericType && listType.GetGenericTypeDefinition() == typeof(IList<>)) + { + list = CollectionUtils.CreateGenericList(ReflectionUtils.GetListItemType(listType)); } else { - throw new Exception(string.Format("Cannot create and populate list type {0}.", listType)); + list = null; } + if (list == null) + throw new Exception(string.Format("Cannot create and populate list type {0}.", listType)); + populateList(list); // create readonly and fixed sized collections using the temporary list diff --git a/Src/Newtonsoft.Json/Utilities/DictionaryWrapper.cs b/Src/Newtonsoft.Json/Utilities/DictionaryWrapper.cs new file mode 100644 index 00000000..d2db5637 --- /dev/null +++ b/Src/Newtonsoft.Json/Utilities/DictionaryWrapper.cs @@ -0,0 +1,400 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Collections; +using System.Threading; + +namespace Newtonsoft.Json.Utilities +{ + public interface IWrappedDictionary : IDictionary + { + object UnderlyingDictionary { get; } + } + + public class DictionaryWrapper : IDictionary, IWrappedDictionary + { + private readonly IDictionary _dictionary; + private readonly IDictionary _genericDictionary; + private object _syncRoot; + + public DictionaryWrapper(IDictionary dictionary) + { + ValidationUtils.ArgumentNotNull(dictionary, "dictionary"); + + _dictionary = dictionary; + } + + public DictionaryWrapper(IDictionary dictionary) + { + ValidationUtils.ArgumentNotNull(dictionary, "dictionary"); + + _genericDictionary = dictionary; + } + + public void Add(TKey key, TValue value) + { + if (_genericDictionary != null) + _genericDictionary.Add(key, value); + else + _dictionary.Add(key, value); + } + + public bool ContainsKey(TKey key) + { + if (_genericDictionary != null) + return _genericDictionary.ContainsKey(key); + else + return _dictionary.Contains(key); + } + + public ICollection Keys + { + get + { + if (_genericDictionary != null) + return _genericDictionary.Keys; + else + return _dictionary.Keys.Cast().ToList(); + } + } + + public bool Remove(TKey key) + { + if (_genericDictionary != null) + { + return _genericDictionary.Remove(key); + } + else + { + if (_dictionary.Contains(key)) + { + _dictionary.Remove(key); + return true; + } + else + { + return false; + } + } + } + + public bool TryGetValue(TKey key, out TValue value) + { + if (_genericDictionary != null) + { + return _genericDictionary.TryGetValue(key, out value); + } + else + { + if (!_dictionary.Contains(key)) + { + value = default(TValue); + return false; + } + else + { + value = (TValue)_dictionary[key]; + return true; + } + } + } + + public ICollection Values + { + get + { + if (_genericDictionary != null) + return _genericDictionary.Values; + else + return _dictionary.Values.Cast().ToList(); + } + } + + public TValue this[TKey key] + { + get + { + if (_genericDictionary != null) + return _genericDictionary[key]; + else + return (TValue)_dictionary[key]; + } + set + { + if (_genericDictionary != null) + _genericDictionary[key] = value; + else + _dictionary[key] = value; + } + } + + public void Add(KeyValuePair item) + { + if (_genericDictionary != null) + _genericDictionary.Add(item); + else + ((IList)_dictionary).Add(item); + } + + public void Clear() + { + if (_genericDictionary != null) + _genericDictionary.Clear(); + else + _dictionary.Clear(); + } + + public bool Contains(KeyValuePair item) + { + if (_genericDictionary != null) + return _genericDictionary.Contains(item); + else + return ((IList)_dictionary).Contains(item); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (_genericDictionary != null) + { + _genericDictionary.CopyTo(array, arrayIndex); + } + else + { + foreach (DictionaryEntry item in _dictionary) + { + array[arrayIndex++] = new KeyValuePair((TKey)item.Key, (TValue)item.Value); + } + } + } + + public int Count + { + get + { + if (_genericDictionary != null) + return _genericDictionary.Count; + else + return _dictionary.Count; + } + } + + public bool IsReadOnly + { + get + { + if (_genericDictionary != null) + return _genericDictionary.IsReadOnly; + else + return _dictionary.IsReadOnly; + } + } + + public bool Remove(KeyValuePair item) + { + if (_genericDictionary != null) + { + return _genericDictionary.Remove(item); + } + else + { + if (_dictionary.Contains(item.Key)) + { + object value = _dictionary[item.Key]; + + if (object.Equals(value, item.Value)) + { + _dictionary.Remove(item.Key); + return true; + } + else + { + return false; + } + } + else + { + return true; + } + } + } + + public IEnumerator> GetEnumerator() + { + if (_genericDictionary != null) + return _genericDictionary.GetEnumerator(); + else + return _dictionary.Cast().Select(de => new KeyValuePair((TKey)de.Key, (TValue)de.Value)).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + void IDictionary.Add(object key, object value) + { + if (_genericDictionary != null) + _genericDictionary.Add((TKey)key, (TValue)value); + else + _dictionary.Add(key, value); + } + + bool IDictionary.Contains(object key) + { + if (_genericDictionary != null) + return _genericDictionary.ContainsKey((TKey)key); + else + return _dictionary.Contains(key); + } + + private struct DictionaryEnumerator : IDictionaryEnumerator + { + private IEnumerator _e; + + public DictionaryEnumerator(IEnumerator e) + { + ValidationUtils.ArgumentNotNull(e, "e"); + _e = e; + } + + public DictionaryEntry Entry + { + get { return (DictionaryEntry)Current; } + } + + public object Key + { + get { return Entry.Key; } + } + + public object Value + { + get { return Entry.Value; } + } + + public object Current + { + get { return _e.Current; } + } + + public bool MoveNext() + { + return _e.MoveNext(); + } + + public void Reset() + { + _e.Reset(); + } + } + + IDictionaryEnumerator IDictionary.GetEnumerator() + { + if (_genericDictionary != null) + return new DictionaryEnumerator(_genericDictionary.GetEnumerator()); + else + return _dictionary.GetEnumerator(); + } + + bool IDictionary.IsFixedSize + { + get + { + if (_genericDictionary != null) + return false; + else + return _dictionary.IsFixedSize; + } + } + + ICollection IDictionary.Keys + { + get + { + if (_genericDictionary != null) + return _genericDictionary.Keys.ToList(); + else + return _dictionary.Keys; + } + } + + public void Remove(object key) + { + if (_genericDictionary != null) + _genericDictionary.Remove((TKey)key); + else + _dictionary.Remove(key); + } + + ICollection IDictionary.Values + { + get + { + if (_genericDictionary != null) + return _genericDictionary.Values.ToList(); + else + return _dictionary.Values; + } + } + + object IDictionary.this[object key] + { + get + { + if (_genericDictionary != null) + return _genericDictionary[(TKey)key]; + else + return _dictionary[key]; + } + set + { + if (_genericDictionary != null) + _genericDictionary[(TKey)key] = (TValue)value; + else + _dictionary[key] = value; + } + } + + void ICollection.CopyTo(Array array, int index) + { + if (_genericDictionary != null) + _genericDictionary.CopyTo((KeyValuePair[])array, index); + else + _dictionary.CopyTo(array, index); + } + + bool ICollection.IsSynchronized + { + get + { + if (_genericDictionary != null) + return false; + else + return _dictionary.IsSynchronized; + } + } + + object ICollection.SyncRoot + { + get + { + if (_syncRoot == null) + Interlocked.CompareExchange(ref _syncRoot, new object(), null); + + return _syncRoot; + } + } + + public object UnderlyingDictionary + { + get + { + if (_genericDictionary != null) + return _genericDictionary; + else + return _dictionary; + } + } + } +} diff --git a/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs b/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs index f4fbdc37..5ead88ff 100644 --- a/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs @@ -7,11 +7,7 @@ using System.Text; namespace Newtonsoft.Json.Utilities { - internal delegate void Action(); internal delegate T Creator(); - internal delegate T Func(A0 arg0); - internal delegate T Func(A0 arg0, A1 arg1); - internal delegate T Func(A0 arg0, A1 arg1, A2 arg2); internal static class MiscellaneousUtils { diff --git a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs index 79a21707..28b4d847 100644 --- a/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs +++ b/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs @@ -29,6 +29,7 @@ using System.Text; using System.Reflection; using System.Collections; using System.ComponentModel; +using System.Linq; namespace Newtonsoft.Json.Utilities { @@ -536,13 +537,19 @@ namespace Newtonsoft.Json.Utilities } public static object CreateGeneric(Type genericTypeDefinition, IList innerTypes, params object[] args) + { + return CreateGeneric(genericTypeDefinition, innerTypes, (t, a) => Activator.CreateInstance(t, a.ToArray()), args); + } + + public static object CreateGeneric(Type genericTypeDefinition, IList innerTypes, Func, object> instanceCreator, params object[] args) { ValidationUtils.ArgumentNotNull(genericTypeDefinition, "genericTypeDefinition"); ValidationUtils.ArgumentNotNullOrEmpty(innerTypes, "innerTypes"); + ValidationUtils.ArgumentNotNull(instanceCreator, "createInstance"); Type specificType = MakeGenericType(genericTypeDefinition, CollectionUtils.CreateArray(innerTypes)); - return Activator.CreateInstance(specificType, args); + return instanceCreator(specificType, args); } } } \ No newline at end of file