New features:

Added Skip() method to JsonReader. Skips the current JSON token and any child tokens.
JsonSerializer now has the ability to populate existing members on an object. This behaviour is controlled by the ObjectCreationHandling property.

Changes:
Improved behaviour of JsonReader.Depth property.

Fixes:
Floating point numbers now use ToString("r") to avoid an overflow error when serializing and deserializing a boundary value.
JsonSerializer now correctly serializes and deserializes DBNull.
JsonSerializer no longer errors when skipping a multi-token JSON structure.
This commit is contained in:
JamesNK
2008-03-15 22:31:36 +00:00
parent e2203d8f63
commit 4ee5fc3578
13 changed files with 944 additions and 116 deletions
+135 -1
View File
@@ -118,6 +118,30 @@ namespace Newtonsoft.Json.Tests
White
}
public class ClassWithArray
{
private readonly IList<long> bar;
private string foo;
public ClassWithArray()
{
bar = new List<Int64>() { int.MaxValue };
}
[JsonProperty("foo")]
public string Foo
{
get { return foo; }
set { foo = value; }
}
[JsonProperty("bar")]
public IList<long> 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<JsonIgnoreAttributeTestClass>(@"{""Field"":99,""Property"":-1,""IgnoredField"":-1}");
JsonIgnoreAttributeTestClass c = JavaScriptConvert.DeserializeObject<JsonIgnoreAttributeTestClass>(@"{""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<ClassWithArray>(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<ClassWithArray>(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<ClassWithArray>(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<ConverableMembers>(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);
}
}
}
@@ -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);
}
}
+2 -2
View File
@@ -252,7 +252,7 @@ namespace Newtonsoft.Json
/// <returns>A Json string representation of the <see cref="Single"/>.</returns>
public static string ToString(float value)
{
return value.ToString(null, CultureInfo.InvariantCulture);
return value.ToString("R", CultureInfo.InvariantCulture);
}
/// <summary>
@@ -262,7 +262,7 @@ namespace Newtonsoft.Json
/// <returns>A Json string representation of the <see cref="Double"/>.</returns>
public static string ToString(double value)
{
return value.ToString(null, CultureInfo.InvariantCulture);
return value.ToString("R", CultureInfo.InvariantCulture);
}
/// <summary>
+53 -2
View File
@@ -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;
}
}
/// <summary>
@@ -148,6 +155,18 @@ namespace Newtonsoft.Json
/// <returns></returns>
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)
+159 -94
View File
@@ -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<object>().ToList());
}
private void SerializeList(JsonWriter writer, IList values)
+1 -1
View File
@@ -789,7 +789,7 @@ namespace Newtonsoft.Json
/// <summary>
/// Changes the <see cref="State"/> to Closed.
/// </summary>
public void Close()
public override void Close()
{
base.Close();
+1 -1
View File
@@ -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)
@@ -73,6 +73,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ObjectCreationHandling.cs" />
<Compile Include="Converters\IsoDateTimeConverter.cs" />
<Compile Include="Converters\JavaScriptDateTimeConverter.cs" />
<Compile Include="Converters\JsonDateTimeSerializationMode.cs" />
@@ -109,6 +110,7 @@
<Compile Include="NullValueHandling.cs" />
<Compile Include="ReferenceLoopHandling.cs" />
<Compile Include="Utilities\DateTimeUtils.cs" />
<Compile Include="Utilities\DictionaryWrapper.cs" />
<Compile Include="Utilities\JavaScriptUtils.cs" />
<Compile Include="JsonToken.cs" />
<Compile Include="JsonWriter.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
}
}
@@ -191,7 +191,7 @@ namespace Newtonsoft.Json.Utilities
/// </summary>
/// <param name="initial">The list to add to.</param>
/// <param name="collection">The collection of elements to add.</param>
public static void AddRange<T>(IList<T> initial, IEnumerable<T> collection)
public static void AddRange<T>(this IList<T> initial, IEnumerable<T> 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<object> wrapper = new ListWrapper<object>(initial);
wrapper.AddRange(collection.Cast<object>());
}
public static List<T> Distinct<T>(List<T> collection)
{
List<T> distinctList = new List<T>();
@@ -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<Type, IList<object>, 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<object>((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<Type, IList<object>, 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<object, object>((IDictionary)dictionary);
}
else
{
throw new Exception(string.Format("Can not create DictionaryWrapper for type {0}.", dictionary.GetType()));
}
}
public static IList CreateAndPopulateList(Type listType, Action<IList> 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<T> 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<object>();
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
@@ -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<TKey, TValue> : IDictionary<TKey, TValue>, IWrappedDictionary
{
private readonly IDictionary _dictionary;
private readonly IDictionary<TKey, TValue> _genericDictionary;
private object _syncRoot;
public DictionaryWrapper(IDictionary dictionary)
{
ValidationUtils.ArgumentNotNull(dictionary, "dictionary");
_dictionary = dictionary;
}
public DictionaryWrapper(IDictionary<TKey, TValue> 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<TKey> Keys
{
get
{
if (_genericDictionary != null)
return _genericDictionary.Keys;
else
return _dictionary.Keys.Cast<TKey>().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<TValue> Values
{
get
{
if (_genericDictionary != null)
return _genericDictionary.Values;
else
return _dictionary.Values.Cast<TValue>().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<TKey, TValue> 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<TKey, TValue> item)
{
if (_genericDictionary != null)
return _genericDictionary.Contains(item);
else
return ((IList)_dictionary).Contains(item);
}
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
if (_genericDictionary != null)
{
_genericDictionary.CopyTo(array, arrayIndex);
}
else
{
foreach (DictionaryEntry item in _dictionary)
{
array[arrayIndex++] = new KeyValuePair<TKey, TValue>((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<TKey, TValue> 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<KeyValuePair<TKey, TValue>> GetEnumerator()
{
if (_genericDictionary != null)
return _genericDictionary.GetEnumerator();
else
return _dictionary.Cast<DictionaryEntry>().Select(de => new KeyValuePair<TKey, TValue>((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<TKey, TValue>[])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;
}
}
}
}
@@ -7,11 +7,7 @@ using System.Text;
namespace Newtonsoft.Json.Utilities
{
internal delegate void Action();
internal delegate T Creator<T>();
internal delegate T Func<A0, T>(A0 arg0);
internal delegate T Func<A0, A1, T>(A0 arg0, A1 arg1);
internal delegate T Func<A0, A1, A2, T>(A0 arg0, A1 arg1, A2 arg2);
internal static class MiscellaneousUtils
{
@@ -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<Type> innerTypes, params object[] args)
{
return CreateGeneric(genericTypeDefinition, innerTypes, (t, a) => Activator.CreateInstance(t, a.ToArray()), args);
}
public static object CreateGeneric(Type genericTypeDefinition, IList<Type> innerTypes, Func<Type, IList<object>, 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);
}
}
}