-Added support for MS format JSON dates to ReadAsDateTime

-Fixed parsing certain JSON with comments into a JObject
-Tests
This commit is contained in:
James Newton-King
2014-03-01 18:31:01 +13:00
parent 0f77cd1b03
commit b43afdcc0a
12 changed files with 328 additions and 57 deletions
@@ -57,6 +57,25 @@ namespace Newtonsoft.Json.Tests.Linq
[TestFixture]
public class JObjectTests : TestFixtureBase
{
[Test]
public void JObjectWithComments()
{
string json = @"{ /*comment2*/
""Name"": /*comment3*/ ""Apple"" /*comment4*/, /*comment5*/
""ExpiryDate"": ""\/Date(1230422400000)\/"",
""Price"": 3.99,
""Sizes"": /*comment6*/ [ /*comment7*/
""Small"", /*comment8*/
""Medium"" /*comment9*/,
/*comment10*/ ""Large""
/*comment11*/ ] /*comment12*/
} /*comment13*/";
JToken o = JToken.Parse(json);
Assert.AreEqual("Apple", (string) o["Name"]);
}
[Test]
public void WritePropertyWithNoValue()
{
@@ -75,4 +75,4 @@ using System.Security;
// by using the '*' as shown below:
[assembly: AssemblyVersion("6.0.0.0")]
[assembly: AssemblyFileVersion("6.0.1.17012")]
[assembly: AssemblyFileVersion("6.0.1.17101")]
@@ -2099,15 +2099,13 @@ keyword such as type of business.""
});
Assert.AreEqual(@"{""DefaultConverter"":""\/Date(0)\/"",""MemberConverter"":""1970-01-01T00:00:00Z""}", json);
ExceptionAssert.Throws<JsonReaderException>(
"Could not convert string to DateTime: /Date(0)/. Path 'DefaultConverter', line 1, position 33.",
() =>
{
JsonConvert.DeserializeObject<MemberConverterClass>(json, new JsonSerializerSettings
{
DateParseHandling = DateParseHandling.None
});
});
var m2 = JsonConvert.DeserializeObject<MemberConverterClass>(json, new JsonSerializerSettings
{
DateParseHandling = DateParseHandling.None
});
Assert.AreEqual(new DateTime(1970, 1, 1), m2.DefaultConverter);
Assert.AreEqual(new DateTime(1970, 1, 1), m2.MemberConverter);
}
[Test]
@@ -2941,16 +2939,16 @@ keyword such as type of business.""
[Test]
public void TypedObjectDeserializationWithComments()
{
string json = @"/*comment*/ { /*comment*/
""Name"": /*comment*/ ""Apple"" /*comment*/, /*comment*/
string json = @"/*comment1*/ { /*comment2*/
""Name"": /*comment3*/ ""Apple"" /*comment4*/, /*comment5*/
""ExpiryDate"": ""\/Date(1230422400000)\/"",
""Price"": 3.99,
""Sizes"": /*comment*/ [ /*comment*/
""Small"", /*comment*/
""Medium"" /*comment*/,
/*comment*/ ""Large""
/*comment*/ ] /*comment*/
} /*comment*/";
""Sizes"": /*comment6*/ [ /*comment7*/
""Small"", /*comment8*/
""Medium"" /*comment9*/,
/*comment10*/ ""Large""
/*comment11*/ ] /*comment12*/
} /*comment13*/";
Product deserializedProduct = (Product)JsonConvert.DeserializeObject(json, typeof(Product));
@@ -3881,7 +3879,11 @@ Path ''"));
string json = "{'$id':'1',key1:'value1',key2:'value2',key3:'value3'}";
ExceptionAssert.Throws<JsonSerializationException>("Cannot preserve reference to readonly dictionary, or dictionary created from a non-default constructor: Newtonsoft.Json.Tests.Serialization.JsonSerializerTest+DictionaryWithNoDefaultConstructor. Path 'key1', line 1, position 16.",
() => JsonConvert.DeserializeObject<DictionaryWithNoDefaultConstructor>(json, new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.All }));
() => JsonConvert.DeserializeObject<DictionaryWithNoDefaultConstructor>(json, new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.All,
SpecialPropertyHandling = SpecialPropertyHandling.Default
}));
}
public class DictionaryWithNoDefaultConstructor : Dictionary<string, string>
@@ -5505,6 +5507,7 @@ To fix this error either change the environment to be fully trusted, change the
JsonTextReader reader = new JsonTextReader(new StringReader(serializeObject));
JsonSerializer serializer = new JsonSerializer();
serializer.DateParseHandling = DateParseHandling.None;
var deserializeObject = serializer.Deserialize<TimeZoneOffsetObject>(reader);
@@ -5847,7 +5850,7 @@ To fix this error either change the environment to be fully trusted, change the
{
var data = new DecimalTest(decimal.MinValue);
var json = JsonConvert.SerializeObject(data);
var obj = JsonConvert.DeserializeObject<DecimalTest>(json);
var obj = JsonConvert.DeserializeObject<DecimalTest>(json, new JsonSerializerSettings { SpecialPropertyHandling = SpecialPropertyHandling.Default });
Assert.AreEqual(decimal.MinValue, obj.Value);
}
@@ -295,7 +295,11 @@ namespace Newtonsoft.Json.Tests.Serialization
() =>
{
JsonConvert.DeserializeObject<string[][]>(json,
new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.All });
new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.All,
SpecialPropertyHandling = SpecialPropertyHandling.Default
});
});
}
@@ -1063,7 +1067,10 @@ namespace Newtonsoft.Json.Tests.Serialization
ExceptionAssert.Throws<JsonSerializationException>(
"Error reading object reference '1'. Path 'Data.Prop2.MyProperty', line 9, position 20.",
() => JsonConvert.DeserializeObject<PropertyItemIsReferenceObject>(json));
() => JsonConvert.DeserializeObject<PropertyItemIsReferenceObject>(json, new JsonSerializerSettings
{
SpecialPropertyHandling = SpecialPropertyHandling.Default
}));
}
}
@@ -407,6 +407,7 @@ namespace Newtonsoft.Json.Tests.Serialization
string json = "{}";
List<string> errors = new List<string>();
JsonSerializer serializer = new JsonSerializer();
serializer.SpecialPropertyHandling = SpecialPropertyHandling.Default;
serializer.Error += delegate(object sender, ErrorEventArgs args)
{
errors.Add(args.ErrorContext.Path + " - " + args.ErrorContext.Member + " - " + args.ErrorContext.Error.Message);
@@ -539,6 +540,7 @@ namespace Newtonsoft.Json.Tests.Serialization
"{'badarray':[0,x,2],'goodarray':[0,1,2]}",
new JsonSerializerSettings
{
SpecialPropertyHandling = SpecialPropertyHandling.Default,
Error = (sender, arg) =>
{
errors.Add(arg.ErrorContext.Error.Message);
@@ -565,7 +567,11 @@ namespace Newtonsoft.Json.Tests.Serialization
const int maxDepth = 256;
using (var jsonTextReader = new JsonTextReader(new StringReader(input)) { MaxDepth = maxDepth })
{
JsonSerializer jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings { MaxDepth = maxDepth });
JsonSerializer jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings
{
MaxDepth = maxDepth,
SpecialPropertyHandling = SpecialPropertyHandling.Default
});
jsonSerializer.Error += (sender, e) =>
{
errors.Add(e.ErrorContext.Error.Message);
@@ -595,7 +601,7 @@ namespace Newtonsoft.Json.Tests.Serialization
const int maxDepth = 256;
using (var jsonTextReader = new JsonTextReader(new StringReader(input)) { MaxDepth = maxDepth })
{
JsonSerializer jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings { MaxDepth = maxDepth });
JsonSerializer jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings { MaxDepth = maxDepth, SpecialPropertyHandling = SpecialPropertyHandling.Default });
jsonSerializer.Error += (sender, e) =>
{
errors.Add(e.ErrorContext.Error.Message);
@@ -634,7 +640,8 @@ namespace Newtonsoft.Json.Tests.Serialization
{
errors.Add(e.ErrorContext.Error.Message);
e.ErrorContext.Handled = true;
}
},
SpecialPropertyHandling = SpecialPropertyHandling.Default
});
Assert.AreEqual(true, newDynamicObject.Explicit);
@@ -54,6 +54,136 @@ namespace Newtonsoft.Json.Tests.Serialization
});
}
#if !NETFX_CORE
[Test]
public void SerializeDeserialize_DictionaryContextContainsGuid_DeserializesItemAsGuid()
{
const string contextKey = "k1";
var someValue = Guid.NewGuid();
Dictionary<string, Guid> inputContext = new Dictionary<string, Guid>();
inputContext.Add(contextKey, someValue);
JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings()
{
Formatting = Formatting.Indented,
TypeNameHandling = TypeNameHandling.All,
SpecialPropertyHandling = SpecialPropertyHandling.ReadAhead
};
string serializedString = JsonConvert.SerializeObject(inputContext, jsonSerializerSettings);
Console.WriteLine(serializedString);
var deserializedObject = (Dictionary<string, Guid>)JsonConvert.DeserializeObject(serializedString, jsonSerializerSettings);
Assert.AreEqual(someValue, deserializedObject[contextKey]);
}
#endif
[Test]
public void DeserializeGuid()
{
Item expected = new Item()
{
SourceTypeID = new Guid("d8220a4b-75b1-4b7a-8112-b7bdae956a45"),
BrokerID = new Guid("951663c4-924e-4c86-a57a-7ed737501dbd"),
Latitude = 33.657145,
Longitude = -117.766684,
TimeStamp = new DateTime(2000, 3, 1, 23, 59, 59, DateTimeKind.Utc),
Payload = new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }
};
string jsonString = JsonConvert.SerializeObject(expected, Formatting.Indented);
Assert.AreEqual(@"{
""SourceTypeID"": ""d8220a4b-75b1-4b7a-8112-b7bdae956a45"",
""BrokerID"": ""951663c4-924e-4c86-a57a-7ed737501dbd"",
""Latitude"": 33.657145,
""Longitude"": -117.766684,
""TimeStamp"": ""2000-03-01T23:59:59Z"",
""Payload"": {
""$type"": ""System.Byte[], mscorlib"",
""$value"": ""AAECAwQFBgcICQ==""
}
}", jsonString);
Item actual = JsonConvert.DeserializeObject<Item>(jsonString, new JsonSerializerSettings
{
SpecialPropertyHandling = SpecialPropertyHandling.ReadAhead
});
Assert.AreEqual(new Guid("d8220a4b-75b1-4b7a-8112-b7bdae956a45"), actual.SourceTypeID);
Assert.AreEqual(new Guid("951663c4-924e-4c86-a57a-7ed737501dbd"), actual.BrokerID);
byte[] bytes = (byte[])actual.Payload;
CollectionAssert.AreEquivalent(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }, bytes);
}
[Test]
public void DeserializeListsWithPreserveObjectReferences()
{
string json = @"{
""$id"": ""1"",
""$values"": [
null,
{
""$id"": ""2"",
""$values"": [
null
]
},
{
""$id"": ""3"",
""$values"": [
{
""$id"": ""4"",
""$values"": [
{
""$ref"": ""1""
}
]
}
]
}
]
}";
PreserveReferencesHandlingTests.CircularList circularList = JsonConvert.DeserializeObject<PreserveReferencesHandlingTests.CircularList>(json,
new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.All,
SpecialPropertyHandling = SpecialPropertyHandling.ReadAhead
});
Assert.AreEqual(3, circularList.Count);
Assert.AreEqual(null, circularList[0]);
Assert.AreEqual(1, circularList[1].Count);
Assert.AreEqual(1, circularList[2].Count);
Assert.AreEqual(1, circularList[2][0].Count);
Assert.IsTrue(ReferenceEquals(circularList, circularList[2][0][0]));
}
[Test]
public void DeserializeTypeNameOnly()
{
string json = @"{
""$id"": ""1"",
""$type"": ""Newtonsoft.Json.Tests.TestObjects.Employee"",
""Name"": ""Name!"",
""Manager"": null
}";
ExceptionAssert.Throws<JsonSerializationException>(
"Type specified in JSON 'Newtonsoft.Json.Tests.TestObjects.Employee' was not resolved. Path '$type', line 3, position 56.",
() =>
{
JsonConvert.DeserializeObject(json, null, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Objects,
SpecialPropertyHandling = SpecialPropertyHandling.ReadAhead
});
});
}
[Test]
public void SerializeRefNull()
{
@@ -67,7 +197,7 @@ namespace Newtonsoft.Json.Tests.Serialization
child.Add("Name", "Isabell");
child.Add("Father", reference);
string json = JsonConvert.SerializeObject(child);
string json = JsonConvert.SerializeObject(child, Formatting.Indented);
Console.WriteLine(json);
@@ -110,6 +240,40 @@ namespace Newtonsoft.Json.Tests.Serialization
Assert.AreEqual(employees[0], employees[1].Manager);
}
[Test]
public void DeserializeFromJToken()
{
string json = @"[
{
""Name"": ""Mike Manager"",
""$id"": ""1"",
""Manager"": null
},
{
""Name"": ""Joe User"",
""$id"": ""2"",
""Manager"": {
""$ref"": ""1""
}
}
]";
JToken t1 = JToken.Parse(json);
JToken t2 = t1.CloneToken();
List<EmployeeReference> employees = t1.ToObject<List<EmployeeReference>>(JsonSerializer.Create(new JsonSerializerSettings
{
SpecialPropertyHandling = SpecialPropertyHandling.ReadAhead
}));
Assert.AreEqual(2, employees.Count);
Assert.AreEqual("Mike Manager", employees[0].Name);
Assert.AreEqual("Joe User", employees[1].Name);
Assert.AreEqual(employees[0], employees[1].Manager);
Assert.IsTrue(JToken.DeepEquals(t1, t2));
}
#if !(PORTABLE || PORTABLE40)
[Test]
public void DeserializeGenericObjectListWithTypeName()
@@ -136,7 +136,12 @@ Newtonsoft.Json Error: 0 : Error!
JsonConvert.DeserializeObject<Staff>(
json,
new JsonSerializerSettings { TraceWriter = traceWriter, Converters = { new JavaScriptDateTimeConverter() } });
new JsonSerializerSettings
{
TraceWriter = traceWriter,
Converters = { new JavaScriptDateTimeConverter() },
SpecialPropertyHandling = SpecialPropertyHandling.Default
});
Console.WriteLine(traceWriter);
// 2012-11-11T12:08:42.761 Info Started serializing Newtonsoft.Json.Tests.Serialization.Staff. Path ''.
@@ -398,6 +403,7 @@ Newtonsoft.Json Error: 0 : Error!
new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.All,
SpecialPropertyHandling = SpecialPropertyHandling.Default,
TraceWriter = traceWriter
});
@@ -517,6 +523,7 @@ Newtonsoft.Json Error: 0 : Error!
JsonConvert.DeserializeObject(json, null, new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.All,
SpecialPropertyHandling = SpecialPropertyHandling.Default,
TraceWriter = traceWriter
});
@@ -122,6 +122,7 @@ namespace Newtonsoft.Json.Tests.Serialization
{
var reader = new JsonTextReader(new StringReader(sb.ToString()));
var ser = new JsonSerializer();
ser.SpecialPropertyHandling = SpecialPropertyHandling.Default;
ser.Deserialize<bool>(reader);
});
}
+21 -8
View File
@@ -438,15 +438,21 @@ namespace Newtonsoft.Json
return null;
}
object temp;
if (DateTimeUtils.TryParseDateTime(s, DateParseHandling.DateTimeOffset, DateTimeZoneHandling, out temp))
{
dt = (DateTimeOffset)temp;
SetToken(JsonToken.Date, dt, false);
return dt;
}
if (DateTimeOffset.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt))
{
SetToken(JsonToken.Date, dt, false);
return dt;
}
else
{
throw JsonReaderException.Create(this, "Could not convert string to DateTimeOffset: {0}.".FormatWith(CultureInfo.InvariantCulture, Value));
}
throw JsonReaderException.Create(this, "Could not convert string to DateTimeOffset: {0}.".FormatWith(CultureInfo.InvariantCulture, Value));
}
if (t == JsonToken.EndArray)
@@ -720,16 +726,23 @@ namespace Newtonsoft.Json
return null;
}
object temp;
if (DateTimeUtils.TryParseDateTime(s, DateParseHandling.DateTime, DateTimeZoneHandling, out temp))
{
dt = (DateTime)temp;
dt = DateTimeUtils.EnsureDateTime(dt, DateTimeZoneHandling);
SetToken(JsonToken.Date, dt, false);
return dt;
}
if (DateTime.TryParse(s, Culture, DateTimeStyles.RoundtripKind, out dt))
{
dt = DateTimeUtils.EnsureDateTime(dt, DateTimeZoneHandling);
SetToken(JsonToken.Date, dt, false);
return dt;
}
else
{
throw JsonReaderException.Create(this, "Could not convert string to DateTime: {0}.".FormatWith(CultureInfo.InvariantCulture, Value));
}
throw JsonReaderException.Create(this, "Could not convert string to DateTime: {0}.".FormatWith(CultureInfo.InvariantCulture, Value));
}
if (TokenType == JsonToken.EndArray)
+4
View File
@@ -130,6 +130,10 @@ namespace Newtonsoft.Json.Linq
internal override void InsertItem(int index, JToken item, bool skipParentCheck)
{
// don't add comments to JProperty
if (item != null && item.Type == JTokenType.Comment)
return;
if (Value != null)
throw new JsonException("{0} cannot have multiple values.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty)));
@@ -91,5 +91,5 @@ using System.Security;
// by using the '*' as shown below:
[assembly: AssemblyVersion("6.0.0.0")]
[assembly: AssemblyFileVersion("6.0.1.17012")]
[assembly: AssemblyFileVersion("6.0.1.17101")]
[assembly: CLSCompliant(true)]
@@ -227,20 +227,33 @@ namespace Newtonsoft.Json.Serialization
ValidationUtils.ArgumentNotNull(reader, "reader");
// this is needed because we've already read inside the object, looking for special properties
JToken token;
using (JTokenWriter writer = new JTokenWriter())
{
writer.WriteStartObject();
if (reader.TokenType == JsonToken.PropertyName)
writer.WriteToken(reader, reader.Depth - 1, true, true);
else
writer.WriteEndObject();
do
{
if (reader.TokenType == JsonToken.PropertyName)
{
string propertyName = (string)reader.Value;
if (!reader.Read())
break;
token = writer.Token;
if (CheckPropertyName(reader, propertyName))
continue;
writer.WritePropertyName(propertyName);
writer.WriteToken(reader, true, true);
}
else
{
writer.WriteEndObject();
return writer.Token;
}
} while (reader.Read());
throw JsonSerializationException.Create(reader, "Unexpected end when deserializing object.");
}
return token;
}
private object CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, object existingValue)
@@ -252,8 +265,8 @@ namespace Newtonsoft.Json.Serialization
{
switch (reader.TokenType)
{
// populate a typed object or generic dictionary/array
// depending upon whether an objectType was supplied
// populate a typed object or generic dictionary/array
// depending upon whether an objectType was supplied
case JsonToken.StartObject:
return CreateObject(reader, objectType, contract, member, containerContract, containerMember, existingValue);
case JsonToken.StartArray:
@@ -369,10 +382,11 @@ namespace Newtonsoft.Json.Serialization
{
JToken t = JToken.ReadFrom(reader);
tokenReader = (JTokenReader)t.CreateReader();
reader = tokenReader;
// start
CheckedRead(reader);
CheckedRead(tokenReader);
reader = tokenReader;
}
if (ReadSpecialPropertiesToken(tokenReader, ref objectType, ref contract, member, containerContract, containerMember, existingValue, out newValue, out id))
@@ -516,8 +530,6 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
string reference = (string)refToken;
refToken.Parent.Remove();
if (reference != null)
{
if (additionalContent != null)
@@ -528,7 +540,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
if (TraceWriter != null && TraceWriter.LevelFilter >= TraceLevel.Info)
TraceWriter.Trace(TraceLevel.Info, JsonPosition.FormatMessage(reader as IJsonLineInfo, reader.Path, "Resolved object reference '{0}' to {1}.".FormatWith(CultureInfo.InvariantCulture, reference, newValue.GetType())), null);
CheckedRead(reader);
reader.Skip();
return true;
}
}
@@ -540,14 +552,27 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
CheckedRead(typeTokenReader);
ResolveTypeName(typeTokenReader, ref objectType, ref contract, member, containerContract, containerMember, qualifiedTypeName);
typeToken.Parent.Remove();
JToken valueToken = current[JsonTypeReflector.ValuePropertyName];
if (valueToken != null)
{
while (true)
{
CheckedRead(reader);
if (reader.TokenType == JsonToken.PropertyName)
{
if ((string)reader.Value == JsonTypeReflector.ValuePropertyName)
return false;
}
CheckedRead(reader);
reader.Skip();
}
}
}
JToken idToken = current[JsonTypeReflector.IdPropertyName];
if (idToken != null)
{
id = (string)idToken;
idToken.Parent.Remove();
}
JToken valuesToken = current[JsonTypeReflector.ArrayValuesPropertyName];
if (valuesToken != null)
@@ -556,8 +581,7 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
CheckedRead(listReader);
newValue = CreateList(listReader, objectType, contract, member, existingValue, id);
valuesToken.Parent.Remove();
CheckedRead(reader);
reader.Skip();
return true;
}
}
@@ -1095,6 +1119,8 @@ To fix this error either change the JSON to a {1} or change the deserialized typ
{
case JsonToken.PropertyName:
object keyValue = reader.Value;
if (CheckPropertyName(reader, keyValue.ToString()))
continue;
try
{
try
@@ -1849,6 +1875,9 @@ To fix this error either change the environment to be fully trusted, change the
{
string memberName = reader.Value.ToString();
if (CheckPropertyName(reader, memberName))
continue;
try
{
// attempt exact case match first
@@ -1891,8 +1920,8 @@ To fix this error either change the environment to be fully trusted, change the
else
throw;
}
}
break;
}
case JsonToken.EndObject:
finished = true;
break;
@@ -1913,6 +1942,23 @@ To fix this error either change the environment to be fully trusted, change the
return newObject;
}
private bool CheckPropertyName(JsonReader reader, string memberName)
{
if (Serializer.SpecialPropertyHandling == SpecialPropertyHandling.ReadAhead)
{
switch (memberName)
{
case "$type":
case "$id":
case "$ref":
case "$values":
reader.Skip();
return true;
}
}
return false;
}
private void SetExtensionData(JsonObjectContract contract, JsonProperty member, JsonReader reader, string memberName, object o)
{
if (contract.ExtensionDataSetter != null)