-Added Path to JsonReader/JsonWriter/ErrorContext with the JSON path of the current position
-Changed collection type on JsonArrayContract to public -Changed dictionary key type and dictionary value type on JsonDictionaryContract to public -Fixed error handling when failing to parse array content -Fixed error handling when there are missing required properties
This commit is contained in:
@@ -272,75 +272,127 @@ namespace Newtonsoft.Json.Tests
|
||||
{
|
||||
string input = @"{
|
||||
value:'Purple',
|
||||
array:[1,2],
|
||||
subobject:{prop:1}
|
||||
array:[1,2,new Date(1)],
|
||||
subobject:{prop:1,proparray:[1]}
|
||||
}";
|
||||
|
||||
StringReader sr = new StringReader(input);
|
||||
|
||||
using (JsonReader jsonReader = new JsonTextReader(sr))
|
||||
using (JsonReader reader = new JsonTextReader(sr))
|
||||
{
|
||||
Assert.AreEqual(0, jsonReader.Depth);
|
||||
Assert.AreEqual(0, reader.Depth);
|
||||
|
||||
jsonReader.Read();
|
||||
Assert.AreEqual(jsonReader.TokenType, JsonToken.StartObject);
|
||||
Assert.AreEqual(0, jsonReader.Depth);
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.StartObject);
|
||||
Assert.AreEqual(0, reader.Depth);
|
||||
Assert.AreEqual("", reader.Path);
|
||||
|
||||
jsonReader.Read();
|
||||
Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName);
|
||||
Assert.AreEqual(1, jsonReader.Depth);
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.PropertyName);
|
||||
Assert.AreEqual(1, reader.Depth);
|
||||
Assert.AreEqual("value", reader.Path);
|
||||
|
||||
jsonReader.Read();
|
||||
Assert.AreEqual(jsonReader.TokenType, JsonToken.String);
|
||||
Assert.AreEqual(jsonReader.Value, @"Purple");
|
||||
Assert.AreEqual(jsonReader.QuoteChar, '\'');
|
||||
Assert.AreEqual(1, jsonReader.Depth);
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.String);
|
||||
Assert.AreEqual(reader.Value, @"Purple");
|
||||
Assert.AreEqual(reader.QuoteChar, '\'');
|
||||
Assert.AreEqual(1, reader.Depth);
|
||||
Assert.AreEqual("value", reader.Path);
|
||||
|
||||
jsonReader.Read();
|
||||
Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName);
|
||||
Assert.AreEqual(1, jsonReader.Depth);
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.PropertyName);
|
||||
Assert.AreEqual(1, reader.Depth);
|
||||
Assert.AreEqual("array", reader.Path);
|
||||
|
||||
jsonReader.Read();
|
||||
Assert.AreEqual(jsonReader.TokenType, JsonToken.StartArray);
|
||||
Assert.AreEqual(1, jsonReader.Depth);
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.StartArray);
|
||||
Assert.AreEqual(1, reader.Depth);
|
||||
Assert.AreEqual("array", reader.Path);
|
||||
|
||||
jsonReader.Read();
|
||||
Assert.AreEqual(jsonReader.TokenType, JsonToken.Integer);
|
||||
Assert.AreEqual(1, jsonReader.Value);
|
||||
Assert.AreEqual(2, jsonReader.Depth);
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.Integer);
|
||||
Assert.AreEqual(1, reader.Value);
|
||||
Assert.AreEqual(2, reader.Depth);
|
||||
Assert.AreEqual("array[0]", reader.Path);
|
||||
|
||||
jsonReader.Read();
|
||||
Assert.AreEqual(jsonReader.TokenType, JsonToken.Integer);
|
||||
Assert.AreEqual(2, jsonReader.Value);
|
||||
Assert.AreEqual(2, jsonReader.Depth);
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.Integer);
|
||||
Assert.AreEqual(2, reader.Value);
|
||||
Assert.AreEqual(2, reader.Depth);
|
||||
Assert.AreEqual("array[1]", reader.Path);
|
||||
|
||||
jsonReader.Read();
|
||||
Assert.AreEqual(jsonReader.TokenType, JsonToken.EndArray);
|
||||
Assert.AreEqual(1, jsonReader.Depth);
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.StartConstructor);
|
||||
Assert.AreEqual("Date", reader.Value);
|
||||
Assert.AreEqual(2, reader.Depth);
|
||||
Assert.AreEqual("array[2]", reader.Path);
|
||||
|
||||
jsonReader.Read();
|
||||
Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName);
|
||||
Assert.AreEqual(1, jsonReader.Depth);
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.Integer);
|
||||
Assert.AreEqual(1, reader.Value);
|
||||
Assert.AreEqual(3, reader.Depth);
|
||||
Assert.AreEqual("array[2][0]", reader.Path);
|
||||
|
||||
jsonReader.Read();
|
||||
Assert.AreEqual(jsonReader.TokenType, JsonToken.StartObject);
|
||||
Assert.AreEqual(1, jsonReader.Depth);
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.EndConstructor);
|
||||
Assert.AreEqual(null, reader.Value);
|
||||
Assert.AreEqual(2, reader.Depth);
|
||||
Assert.AreEqual("array[2]", reader.Path);
|
||||
|
||||
jsonReader.Read();
|
||||
Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName);
|
||||
Assert.AreEqual(2, jsonReader.Depth);
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.EndArray);
|
||||
Assert.AreEqual(1, reader.Depth);
|
||||
Assert.AreEqual("array", reader.Path);
|
||||
|
||||
jsonReader.Read();
|
||||
Assert.AreEqual(jsonReader.TokenType, JsonToken.Integer);
|
||||
Assert.AreEqual(2, jsonReader.Depth);
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.PropertyName);
|
||||
Assert.AreEqual(1, reader.Depth);
|
||||
Assert.AreEqual("subobject", reader.Path);
|
||||
|
||||
jsonReader.Read();
|
||||
Assert.AreEqual(jsonReader.TokenType, JsonToken.EndObject);
|
||||
Assert.AreEqual(1, jsonReader.Depth);
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.StartObject);
|
||||
Assert.AreEqual(1, reader.Depth);
|
||||
Assert.AreEqual("subobject", reader.Path);
|
||||
|
||||
jsonReader.Read();
|
||||
Assert.AreEqual(jsonReader.TokenType, JsonToken.EndObject);
|
||||
Assert.AreEqual(0, jsonReader.Depth);
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.PropertyName);
|
||||
Assert.AreEqual(2, reader.Depth);
|
||||
Assert.AreEqual("subobject.prop", reader.Path);
|
||||
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.Integer);
|
||||
Assert.AreEqual(2, reader.Depth);
|
||||
Assert.AreEqual("subobject.prop", reader.Path);
|
||||
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.PropertyName);
|
||||
Assert.AreEqual(2, reader.Depth);
|
||||
Assert.AreEqual("subobject.proparray", reader.Path);
|
||||
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.StartArray);
|
||||
Assert.AreEqual(2, reader.Depth);
|
||||
Assert.AreEqual("subobject.proparray", reader.Path);
|
||||
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.Integer);
|
||||
Assert.AreEqual(3, reader.Depth);
|
||||
Assert.AreEqual("subobject.proparray[0]", reader.Path);
|
||||
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.EndArray);
|
||||
Assert.AreEqual(2, reader.Depth);
|
||||
Assert.AreEqual("subobject.proparray", reader.Path);
|
||||
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.EndObject);
|
||||
Assert.AreEqual(1, reader.Depth);
|
||||
Assert.AreEqual("subobject", reader.Path);
|
||||
|
||||
reader.Read();
|
||||
Assert.AreEqual(reader.TokenType, JsonToken.EndObject);
|
||||
Assert.AreEqual(0, reader.Depth);
|
||||
Assert.AreEqual("", reader.Path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -199,6 +199,85 @@ namespace Newtonsoft.Json.Tests
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void WriteEnd()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
StringWriter sw = new StringWriter(sb);
|
||||
|
||||
using (JsonWriter jsonWriter = new JsonTextWriter(sw))
|
||||
{
|
||||
jsonWriter.Formatting = Formatting.Indented;
|
||||
|
||||
jsonWriter.WriteStartObject();
|
||||
jsonWriter.WritePropertyName("CPU");
|
||||
jsonWriter.WriteValue("Intel");
|
||||
jsonWriter.WritePropertyName("PSU");
|
||||
jsonWriter.WriteValue("500W");
|
||||
jsonWriter.WritePropertyName("Drives");
|
||||
jsonWriter.WriteStartArray();
|
||||
jsonWriter.WriteValue("DVD read/writer");
|
||||
jsonWriter.WriteComment("(broken)");
|
||||
jsonWriter.WriteValue("500 gigabyte hard drive");
|
||||
jsonWriter.WriteValue("200 gigabype hard drive");
|
||||
jsonWriter.WriteEndObject();
|
||||
Assert.AreEqual(WriteState.Start, jsonWriter.WriteState);
|
||||
}
|
||||
|
||||
string expected = @"{
|
||||
""CPU"": ""Intel"",
|
||||
""PSU"": ""500W"",
|
||||
""Drives"": [
|
||||
""DVD read/writer""
|
||||
/*(broken)*/,
|
||||
""500 gigabyte hard drive"",
|
||||
""200 gigabype hard drive""
|
||||
]
|
||||
}";
|
||||
string result = sb.ToString();
|
||||
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CloseWithRemainingContent()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
StringWriter sw = new StringWriter(sb);
|
||||
|
||||
using (JsonWriter jsonWriter = new JsonTextWriter(sw))
|
||||
{
|
||||
jsonWriter.Formatting = Formatting.Indented;
|
||||
|
||||
jsonWriter.WriteStartObject();
|
||||
jsonWriter.WritePropertyName("CPU");
|
||||
jsonWriter.WriteValue("Intel");
|
||||
jsonWriter.WritePropertyName("PSU");
|
||||
jsonWriter.WriteValue("500W");
|
||||
jsonWriter.WritePropertyName("Drives");
|
||||
jsonWriter.WriteStartArray();
|
||||
jsonWriter.WriteValue("DVD read/writer");
|
||||
jsonWriter.WriteComment("(broken)");
|
||||
jsonWriter.WriteValue("500 gigabyte hard drive");
|
||||
jsonWriter.WriteValue("200 gigabype hard drive");
|
||||
jsonWriter.Close();
|
||||
}
|
||||
|
||||
string expected = @"{
|
||||
""CPU"": ""Intel"",
|
||||
""PSU"": ""500W"",
|
||||
""Drives"": [
|
||||
""DVD read/writer""
|
||||
/*(broken)*/,
|
||||
""500 gigabyte hard drive"",
|
||||
""200 gigabype hard drive""
|
||||
]
|
||||
}";
|
||||
string result = sb.ToString();
|
||||
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Indenting()
|
||||
{
|
||||
@@ -222,6 +301,7 @@ namespace Newtonsoft.Json.Tests
|
||||
jsonWriter.WriteValue("200 gigabype hard drive");
|
||||
jsonWriter.WriteEnd();
|
||||
jsonWriter.WriteEndObject();
|
||||
Assert.AreEqual(WriteState.Start, jsonWriter.WriteState);
|
||||
}
|
||||
|
||||
// {
|
||||
@@ -247,9 +327,6 @@ namespace Newtonsoft.Json.Tests
|
||||
}";
|
||||
string result = sb.ToString();
|
||||
|
||||
Console.WriteLine("Indenting");
|
||||
Console.WriteLine(result);
|
||||
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
@@ -265,27 +342,34 @@ namespace Newtonsoft.Json.Tests
|
||||
|
||||
jsonWriter.WriteStartObject();
|
||||
Assert.AreEqual(WriteState.Object, jsonWriter.WriteState);
|
||||
Assert.AreEqual("", jsonWriter.Path);
|
||||
|
||||
jsonWriter.WritePropertyName("CPU");
|
||||
Assert.AreEqual(WriteState.Property, jsonWriter.WriteState);
|
||||
Assert.AreEqual("CPU", jsonWriter.Path);
|
||||
|
||||
jsonWriter.WriteValue("Intel");
|
||||
Assert.AreEqual(WriteState.Object, jsonWriter.WriteState);
|
||||
Assert.AreEqual("CPU", jsonWriter.Path);
|
||||
|
||||
jsonWriter.WritePropertyName("Drives");
|
||||
Assert.AreEqual(WriteState.Property, jsonWriter.WriteState);
|
||||
Assert.AreEqual("Drives", jsonWriter.Path);
|
||||
|
||||
jsonWriter.WriteStartArray();
|
||||
Assert.AreEqual(WriteState.Array, jsonWriter.WriteState);
|
||||
|
||||
jsonWriter.WriteValue("DVD read/writer");
|
||||
Assert.AreEqual(WriteState.Array, jsonWriter.WriteState);
|
||||
Assert.AreEqual("Drives[0]", jsonWriter.Path);
|
||||
|
||||
jsonWriter.WriteEnd();
|
||||
Assert.AreEqual(WriteState.Object, jsonWriter.WriteState);
|
||||
Assert.AreEqual("Drives", jsonWriter.Path);
|
||||
|
||||
jsonWriter.WriteEndObject();
|
||||
Assert.AreEqual(WriteState.Start, jsonWriter.WriteState);
|
||||
Assert.AreEqual("", jsonWriter.Path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -620,6 +704,79 @@ _____'propertyName': NaN
|
||||
Assert.AreEqual(expected, result);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Path()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
StringWriter sw = new StringWriter(sb);
|
||||
|
||||
string text = "Hello world.";
|
||||
byte[] data = Encoding.UTF8.GetBytes(text);
|
||||
|
||||
using (JsonTextWriter writer = new JsonTextWriter(sw))
|
||||
{
|
||||
writer.Formatting = Formatting.Indented;
|
||||
|
||||
writer.WriteStartArray();
|
||||
Assert.AreEqual("", writer.Path);
|
||||
writer.WriteStartObject();
|
||||
Assert.AreEqual("[0]", writer.Path);
|
||||
writer.WritePropertyName("Property1");
|
||||
Assert.AreEqual("[0].Property1", writer.Path);
|
||||
writer.WriteStartArray();
|
||||
Assert.AreEqual("[0].Property1", writer.Path);
|
||||
writer.WriteValue(1);
|
||||
Assert.AreEqual("[0].Property1[0]", writer.Path);
|
||||
writer.WriteStartArray();
|
||||
Assert.AreEqual("[0].Property1[1]", writer.Path);
|
||||
writer.WriteStartArray();
|
||||
Assert.AreEqual("[0].Property1[1][0]", writer.Path);
|
||||
writer.WriteStartArray();
|
||||
Assert.AreEqual("[0].Property1[1][0][0]", writer.Path);
|
||||
writer.WriteEndObject();
|
||||
Assert.AreEqual("[0]", writer.Path);
|
||||
writer.WriteStartObject();
|
||||
Assert.AreEqual("[1]", writer.Path);
|
||||
writer.WritePropertyName("Property2");
|
||||
Assert.AreEqual("[1].Property2", writer.Path);
|
||||
writer.WriteStartConstructor("Constructor1");
|
||||
Assert.AreEqual("[1].Property2", writer.Path);
|
||||
writer.WriteNull();
|
||||
Assert.AreEqual("[1].Property2[0]", writer.Path);
|
||||
writer.WriteStartArray();
|
||||
Assert.AreEqual("[1].Property2[1]", writer.Path);
|
||||
writer.WriteValue(1);
|
||||
Assert.AreEqual("[1].Property2[1][0]", writer.Path);
|
||||
writer.WriteEnd();
|
||||
Assert.AreEqual("[1].Property2[1]", writer.Path);
|
||||
writer.WriteEndObject();
|
||||
Assert.AreEqual("[1]", writer.Path);
|
||||
writer.WriteEndArray();
|
||||
Assert.AreEqual("", writer.Path);
|
||||
}
|
||||
|
||||
Assert.AreEqual(@"[
|
||||
{
|
||||
""Property1"": [
|
||||
1,
|
||||
[
|
||||
[
|
||||
[]
|
||||
]
|
||||
]
|
||||
]
|
||||
},
|
||||
{
|
||||
""Property2"": new Constructor1(
|
||||
null,
|
||||
[
|
||||
1
|
||||
]
|
||||
)
|
||||
}
|
||||
]", sb.ToString());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void BuildStateArray()
|
||||
{
|
||||
|
||||
@@ -72,5 +72,5 @@ using System.Security;
|
||||
// by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("4.0.8.0")]
|
||||
#if !PocketPC
|
||||
[assembly: AssemblyFileVersion("4.0.8.14618")]
|
||||
[assembly: AssemblyFileVersion("4.0.8.14623")]
|
||||
#endif
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
VersionKeyedCollection c = JsonConvert.DeserializeObject<VersionKeyedCollection>(json);
|
||||
Assert.AreEqual(1, c.Count);
|
||||
Assert.AreEqual(1, c.Messages.Count);
|
||||
Assert.AreEqual("Error message for member 1 = An item with the same key has already been added.", c.Messages[0]);
|
||||
Assert.AreEqual("[1] - Error message for member 1 = An item with the same key has already been added.", c.Messages[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -161,7 +161,8 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
{
|
||||
List<string> errors = new List<string>();
|
||||
|
||||
List<DateTime> c = JsonConvert.DeserializeObject<List<DateTime>>(@"[
|
||||
List<DateTime> c = JsonConvert.DeserializeObject<List<DateTime>>(
|
||||
@"[
|
||||
""2009-09-09T00:00:00Z"",
|
||||
""I am not a date and will error!"",
|
||||
[
|
||||
@@ -175,10 +176,10 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
{
|
||||
Error = delegate(object sender, ErrorEventArgs args)
|
||||
{
|
||||
errors.Add(args.ErrorContext.Error.Message);
|
||||
errors.Add(args.ErrorContext.Path + " - " + args.ErrorContext.Member + " - " + args.ErrorContext.Error.Message);
|
||||
args.ErrorContext.Handled = true;
|
||||
},
|
||||
Converters = { new IsoDateTimeConverter() }
|
||||
Converters = {new IsoDateTimeConverter()}
|
||||
});
|
||||
|
||||
// 2009-09-09T00:00:00Z
|
||||
@@ -196,12 +197,12 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
|
||||
Assert.AreEqual(3, errors.Count);
|
||||
#if !(NET20 || NET35 || WINDOWS_PHONE)
|
||||
Assert.AreEqual("The string was not recognized as a valid DateTime. There is an unknown word starting at index 0.", errors[0]);
|
||||
Assert.AreEqual("[1] - 1 - The string was not recognized as a valid DateTime. There is an unknown word starting at index 0.", errors[0]);
|
||||
#else
|
||||
Assert.AreEqual("The string was not recognized as a valid DateTime. There is a unknown word starting at index 0.", errors[0]);
|
||||
Assert.AreEqual("[1] - 1 - The string was not recognized as a valid DateTime. There is a unknown word starting at index 0.", errors[0]);
|
||||
#endif
|
||||
Assert.AreEqual("Unexpected token parsing date. Expected String, got StartArray.", errors[1]);
|
||||
Assert.AreEqual("Cannot convert null value to System.DateTime.", errors[2]);
|
||||
Assert.AreEqual("[2] - 2 - Unexpected token parsing date. Expected String, got StartArray.", errors[1]);
|
||||
Assert.AreEqual("[4] - 4 - Cannot convert null value to System.DateTime.", errors[2]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -209,7 +210,8 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
{
|
||||
bool eventErrorHandlerCalled = false;
|
||||
|
||||
DateTimeErrorObjectCollection c = JsonConvert.DeserializeObject<DateTimeErrorObjectCollection>(@"[
|
||||
DateTimeErrorObjectCollection c = JsonConvert.DeserializeObject<DateTimeErrorObjectCollection>(
|
||||
@"[
|
||||
""2009-09-09T00:00:00Z"",
|
||||
""kjhkjhkjhkjh"",
|
||||
[
|
||||
@@ -220,13 +222,13 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
""2000-12-01T00:00:00Z""
|
||||
]",
|
||||
new JsonSerializerSettings
|
||||
{
|
||||
Error = (s, a) => eventErrorHandlerCalled = true,
|
||||
Converters =
|
||||
{
|
||||
Error = (s, a) => eventErrorHandlerCalled = true,
|
||||
Converters =
|
||||
{
|
||||
new IsoDateTimeConverter()
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Assert.AreEqual(3, c.Count);
|
||||
Assert.AreEqual(new DateTime(2009, 9, 9, 0, 0, 0, DateTimeKind.Utc), c[0]);
|
||||
@@ -270,25 +272,76 @@ namespace Newtonsoft.Json.Tests.Serialization
|
||||
|
||||
string json = @"[[""kjhkjhkjhkjh""]]";
|
||||
|
||||
Exception e = null;
|
||||
try
|
||||
{
|
||||
JsonSerializer serializer = new JsonSerializer();
|
||||
serializer.Error += delegate(object sender, ErrorEventArgs args)
|
||||
{
|
||||
// only log an error once
|
||||
if (args.CurrentObject == args.ErrorContext.OriginalObject)
|
||||
errors.Add(args.ErrorContext.Error.Message);
|
||||
};
|
||||
{
|
||||
// only log an error once
|
||||
if (args.CurrentObject == args.ErrorContext.OriginalObject)
|
||||
errors.Add(args.ErrorContext.Path + " - " + args.ErrorContext.Member + " - " + args.ErrorContext.Error.Message);
|
||||
};
|
||||
|
||||
serializer.Deserialize(new StringReader(json), typeof(List<List<DateTime>>));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
e = ex;
|
||||
}
|
||||
|
||||
Assert.AreEqual(@"Error converting value ""kjhkjhkjhkjh"" to type 'System.DateTime'. Line 1, position 16.", e.Message);
|
||||
|
||||
Assert.AreEqual(1, errors.Count);
|
||||
Assert.AreEqual(@"Error converting value ""kjhkjhkjhkjh"" to type 'System.DateTime'. Line 1, position 16.", errors[0]);
|
||||
Assert.AreEqual(@"[0][0] - 0 - Error converting value ""kjhkjhkjhkjh"" to type 'System.DateTime'. Line 1, position 16.", errors[0]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void MultipleRequiredPropertyErrors()
|
||||
{
|
||||
string json = "{}";
|
||||
List<string> errors = new List<string>();
|
||||
JsonSerializer serializer = new JsonSerializer();
|
||||
serializer.Error += delegate(object sender, ErrorEventArgs args)
|
||||
{
|
||||
errors.Add(args.ErrorContext.Path + " - " + args.ErrorContext.Member + " - " + args.ErrorContext.Error.Message);
|
||||
args.ErrorContext.Handled = true;
|
||||
};
|
||||
serializer.Deserialize(new JsonTextReader(new StringReader(json)), typeof (MyTypeWithRequiredMembers));
|
||||
|
||||
Assert.AreEqual(2, errors.Count);
|
||||
Assert.AreEqual(" - Required1 - Required property 'Required1' not found in JSON. Line 1, position 2.", errors[0]);
|
||||
Assert.AreEqual(" - Required2 - Required property 'Required2' not found in JSON. Line 1, position 2.", errors[1]);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void HandlingArrayErrors()
|
||||
{
|
||||
string json = "[\"a\",\"b\",\"45\",34]";
|
||||
|
||||
List<string> errors = new List<string>();
|
||||
|
||||
JsonSerializer serializer = new JsonSerializer();
|
||||
serializer.Error += delegate(object sender, ErrorEventArgs args)
|
||||
{
|
||||
errors.Add(args.ErrorContext.Path + " - " + args.ErrorContext.Member + " - " + args.ErrorContext.Error.Message);
|
||||
args.ErrorContext.Handled = true;
|
||||
};
|
||||
|
||||
serializer.Deserialize(new JsonTextReader(new StringReader(json)), typeof(int[]));
|
||||
|
||||
Assert.AreEqual(2, errors.Count);
|
||||
Assert.AreEqual("[0] - 0 - Could not convert string to integer: a. Line 1, position 4.", errors[0]);
|
||||
Assert.AreEqual("[1] - 1 - Could not convert string to integer: b. Line 1, position 8.", errors[1]);
|
||||
}
|
||||
}
|
||||
|
||||
[JsonObject]
|
||||
public class MyTypeWithRequiredMembers
|
||||
{
|
||||
[JsonProperty(Required = Required.AllowNull)]
|
||||
public string Required1;
|
||||
[JsonProperty(Required = Required.AllowNull)]
|
||||
public string Required2;
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ namespace Newtonsoft.Json.Tests.TestObjects
|
||||
[OnError]
|
||||
internal void OnErrorMethod(StreamingContext context, ErrorContext errorContext)
|
||||
{
|
||||
Messages.Add("Error message for member " + errorContext.Member + " = " + errorContext.Error.Message);
|
||||
Messages.Add(errorContext.Path + " - Error message for member " + errorContext.Member + " = " + errorContext.Error.Message);
|
||||
errorContext.Handled = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,11 +27,78 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Utilities;
|
||||
|
||||
namespace Newtonsoft.Json
|
||||
{
|
||||
internal enum JsonContainerType
|
||||
{
|
||||
None,
|
||||
Object,
|
||||
Array,
|
||||
Constructor
|
||||
}
|
||||
|
||||
internal struct JsonPosition
|
||||
{
|
||||
internal JsonContainerType Type;
|
||||
internal int? Position;
|
||||
internal string PropertyName;
|
||||
|
||||
internal void WriteTo(StringBuilder sb)
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case JsonContainerType.Object:
|
||||
if (PropertyName != null)
|
||||
{
|
||||
if (sb.Length > 0)
|
||||
sb.Append(".");
|
||||
sb.Append(PropertyName);
|
||||
}
|
||||
break;
|
||||
case JsonContainerType.Array:
|
||||
case JsonContainerType.Constructor:
|
||||
if (Position != null)
|
||||
{
|
||||
sb.Append("[");
|
||||
sb.Append(Position);
|
||||
sb.Append("]");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool InsideContainer()
|
||||
{
|
||||
switch (Type)
|
||||
{
|
||||
case JsonContainerType.Object:
|
||||
return (PropertyName != null);
|
||||
case JsonContainerType.Array:
|
||||
case JsonContainerType.Constructor:
|
||||
return (Position != null);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static string BuildPath(IEnumerable<JsonPosition> positions)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
foreach (JsonPosition state in positions)
|
||||
{
|
||||
state.WriteTo(sb);
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data.
|
||||
/// </summary>
|
||||
@@ -101,7 +168,7 @@ namespace Newtonsoft.Json
|
||||
private object _value;
|
||||
private char _quoteChar;
|
||||
internal State _currentState;
|
||||
private JTokenType _currentTypeContext;
|
||||
private JsonPosition _currentPosition;
|
||||
private CultureInfo _culture;
|
||||
|
||||
/// <summary>
|
||||
@@ -113,9 +180,7 @@ namespace Newtonsoft.Json
|
||||
get { return _currentState; }
|
||||
}
|
||||
|
||||
private int _top;
|
||||
|
||||
private readonly List<JTokenType> _stack;
|
||||
private readonly List<JsonPosition> _stack;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the underlying stream or
|
||||
@@ -137,7 +202,7 @@ namespace Newtonsoft.Json
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the current Json token.
|
||||
/// Gets the type of the current JSON token.
|
||||
/// </summary>
|
||||
public virtual JsonToken TokenType
|
||||
{
|
||||
@@ -145,7 +210,7 @@ namespace Newtonsoft.Json
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text value of the current Json token.
|
||||
/// Gets the text value of the current JSON token.
|
||||
/// </summary>
|
||||
public virtual object Value
|
||||
{
|
||||
@@ -153,7 +218,7 @@ namespace Newtonsoft.Json
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets The Common Language Runtime (CLR) type for the current Json token.
|
||||
/// Gets The Common Language Runtime (CLR) type for the current JSON token.
|
||||
/// </summary>
|
||||
public virtual Type ValueType
|
||||
{
|
||||
@@ -168,11 +233,11 @@ namespace Newtonsoft.Json
|
||||
{
|
||||
get
|
||||
{
|
||||
int depth = _top - 1;
|
||||
if (IsStartToken(TokenType))
|
||||
return depth - 1;
|
||||
else
|
||||
int depth = _stack.Count;
|
||||
if (IsStartToken(TokenType) || _currentPosition.Type == JsonContainerType.None)
|
||||
return depth;
|
||||
else
|
||||
return depth + 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,39 +250,71 @@ namespace Newtonsoft.Json
|
||||
set { _culture = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path of the current JSON token.
|
||||
/// </summary>
|
||||
public string Path
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_currentPosition.Type == JsonContainerType.None)
|
||||
return string.Empty;
|
||||
|
||||
return JsonPosition.BuildPath(_stack.Concat(new[] { _currentPosition }));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JsonReader"/> class with the specified <see cref="TextReader"/>.
|
||||
/// </summary>
|
||||
protected JsonReader()
|
||||
{
|
||||
_currentState = State.Start;
|
||||
_stack = new List<JTokenType>();
|
||||
_stack = new List<JsonPosition>(4);
|
||||
|
||||
CloseInput = true;
|
||||
|
||||
Push(JTokenType.None);
|
||||
}
|
||||
|
||||
private void Push(JTokenType value)
|
||||
private void Push(JsonContainerType value)
|
||||
{
|
||||
_stack.Add(value);
|
||||
_top++;
|
||||
_currentTypeContext = value;
|
||||
UpdateScopeWithFinishedValue();
|
||||
|
||||
if (_currentPosition.Type == JsonContainerType.None)
|
||||
{
|
||||
_currentPosition.Type = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
_stack.Add(_currentPosition);
|
||||
var state = new JsonPosition
|
||||
{
|
||||
Type = value
|
||||
};
|
||||
_currentPosition = state;
|
||||
}
|
||||
}
|
||||
|
||||
private JTokenType Pop()
|
||||
private JsonContainerType Pop()
|
||||
{
|
||||
JTokenType value = Peek();
|
||||
_stack.RemoveAt(_stack.Count - 1);
|
||||
_top--;
|
||||
_currentTypeContext = _stack[_top - 1];
|
||||
JsonPosition oldPosition;
|
||||
if (_stack.Count > 0)
|
||||
{
|
||||
oldPosition = _currentPosition;
|
||||
_currentPosition = _stack[_stack.Count - 1];
|
||||
_stack.RemoveAt(_stack.Count - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
oldPosition = _currentPosition;
|
||||
_currentPosition = new JsonPosition();
|
||||
}
|
||||
|
||||
return value;
|
||||
return oldPosition.Type;
|
||||
}
|
||||
|
||||
private JTokenType Peek()
|
||||
private JsonContainerType Peek()
|
||||
{
|
||||
return _currentTypeContext;
|
||||
return _currentPosition.Type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -292,15 +389,15 @@ namespace Newtonsoft.Json
|
||||
{
|
||||
case JsonToken.StartObject:
|
||||
_currentState = State.ObjectStart;
|
||||
Push(JTokenType.Object);
|
||||
Push(JsonContainerType.Object);
|
||||
break;
|
||||
case JsonToken.StartArray:
|
||||
_currentState = State.ArrayStart;
|
||||
Push(JTokenType.Array);
|
||||
Push(JsonContainerType.Array);
|
||||
break;
|
||||
case JsonToken.StartConstructor:
|
||||
_currentState = State.ConstructorStart;
|
||||
Push(JTokenType.Constructor);
|
||||
Push(JsonContainerType.Constructor);
|
||||
break;
|
||||
case JsonToken.EndObject:
|
||||
ValidateEnd(JsonToken.EndObject);
|
||||
@@ -313,6 +410,8 @@ namespace Newtonsoft.Json
|
||||
break;
|
||||
case JsonToken.PropertyName:
|
||||
_currentState = State.Property;
|
||||
|
||||
_currentPosition.PropertyName = (string) value;
|
||||
break;
|
||||
case JsonToken.Undefined:
|
||||
case JsonToken.Integer:
|
||||
@@ -323,21 +422,35 @@ namespace Newtonsoft.Json
|
||||
case JsonToken.String:
|
||||
case JsonToken.Raw:
|
||||
case JsonToken.Bytes:
|
||||
_currentState = (Peek() != JTokenType.None) ? State.PostValue : State.Finished;
|
||||
_currentState = (Peek() != JsonContainerType.None) ? State.PostValue : State.Finished;
|
||||
|
||||
UpdateScopeWithFinishedValue();
|
||||
break;
|
||||
}
|
||||
|
||||
_value = value;
|
||||
}
|
||||
|
||||
private void UpdateScopeWithFinishedValue()
|
||||
{
|
||||
if (_currentPosition.Type == JsonContainerType.Array
|
||||
|| _currentPosition.Type == JsonContainerType.Constructor)
|
||||
{
|
||||
if (_currentPosition.Position == null)
|
||||
_currentPosition.Position = 0;
|
||||
else
|
||||
_currentPosition.Position++;
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateEnd(JsonToken endToken)
|
||||
{
|
||||
JTokenType currentObject = Pop();
|
||||
JsonContainerType currentObject = Pop();
|
||||
|
||||
if (GetTypeForCloseToken(endToken) != currentObject)
|
||||
throw new JsonReaderException("JsonToken {0} is not valid for closing JsonType {1}.".FormatWith(CultureInfo.InvariantCulture, endToken, currentObject));
|
||||
|
||||
_currentState = (Peek() != JTokenType.None) ? State.PostValue : State.Finished;
|
||||
_currentState = (Peek() != JsonContainerType.None) ? State.PostValue : State.Finished;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -345,20 +458,20 @@ namespace Newtonsoft.Json
|
||||
/// </summary>
|
||||
protected void SetStateBasedOnCurrent()
|
||||
{
|
||||
JTokenType currentObject = Peek();
|
||||
JsonContainerType currentObject = Peek();
|
||||
|
||||
switch (currentObject)
|
||||
{
|
||||
case JTokenType.Object:
|
||||
case JsonContainerType.Object:
|
||||
_currentState = State.Object;
|
||||
break;
|
||||
case JTokenType.Array:
|
||||
case JsonContainerType.Array:
|
||||
_currentState = State.Array;
|
||||
break;
|
||||
case JTokenType.Constructor:
|
||||
case JsonContainerType.Constructor:
|
||||
_currentState = State.Constructor;
|
||||
break;
|
||||
case JTokenType.None:
|
||||
case JsonContainerType.None:
|
||||
_currentState = State.Finished;
|
||||
break;
|
||||
default:
|
||||
@@ -413,16 +526,16 @@ namespace Newtonsoft.Json
|
||||
}
|
||||
}
|
||||
|
||||
private JTokenType GetTypeForCloseToken(JsonToken token)
|
||||
private JsonContainerType GetTypeForCloseToken(JsonToken token)
|
||||
{
|
||||
switch (token)
|
||||
{
|
||||
case JsonToken.EndObject:
|
||||
return JTokenType.Object;
|
||||
return JsonContainerType.Object;
|
||||
case JsonToken.EndArray:
|
||||
return JTokenType.Array;
|
||||
return JsonContainerType.Array;
|
||||
case JsonToken.EndConstructor:
|
||||
return JTokenType.Constructor;
|
||||
return JsonContainerType.Constructor;
|
||||
default:
|
||||
throw new JsonReaderException("Not a valid close JsonToken: {0}".FormatWith(CultureInfo.InvariantCulture, token));
|
||||
}
|
||||
@@ -497,4 +610,4 @@ namespace Newtonsoft.Json
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,9 +160,8 @@ namespace Newtonsoft.Json
|
||||
StateArray = BuildStateArray();
|
||||
}
|
||||
|
||||
private int _top;
|
||||
|
||||
private readonly List<JTokenType> _stack;
|
||||
private readonly List<JsonPosition> _stack;
|
||||
private JsonPosition _currentPosition;
|
||||
private State _currentState;
|
||||
private Formatting _formatting;
|
||||
|
||||
@@ -182,9 +181,31 @@ namespace Newtonsoft.Json
|
||||
/// <value>The top.</value>
|
||||
protected internal int Top
|
||||
{
|
||||
get { return _top; }
|
||||
get
|
||||
{
|
||||
int depth = _stack.Count;
|
||||
if (Peek() != JsonContainerType.None)
|
||||
depth++;
|
||||
|
||||
return depth;
|
||||
}
|
||||
}
|
||||
|
||||
internal string ContainerPath
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_currentPosition.Type == JsonContainerType.None)
|
||||
return string.Empty;
|
||||
|
||||
IEnumerable<JsonPosition> positions = (_currentPosition.InsideContainer())
|
||||
? _stack
|
||||
: _stack.Concat(new[] { _currentPosition });
|
||||
|
||||
return JsonPosition.BuildPath(positions);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the state of the writer.
|
||||
/// </summary>
|
||||
@@ -217,6 +238,20 @@ namespace Newtonsoft.Json
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path of the writer.
|
||||
/// </summary>
|
||||
public string Path
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_currentPosition.Type == JsonContainerType.None)
|
||||
return string.Empty;
|
||||
|
||||
return JsonPosition.BuildPath(_stack.Concat(new[] { _currentPosition }));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates how the output is formatted.
|
||||
/// </summary>
|
||||
@@ -231,34 +266,65 @@ namespace Newtonsoft.Json
|
||||
/// </summary>
|
||||
protected JsonWriter()
|
||||
{
|
||||
_stack = new List<JTokenType>(8);
|
||||
_stack.Add(JTokenType.None);
|
||||
_stack = new List<JsonPosition>(4);
|
||||
_currentState = State.Start;
|
||||
_formatting = Formatting.None;
|
||||
|
||||
CloseOutput = true;
|
||||
}
|
||||
|
||||
private void Push(JTokenType value)
|
||||
private void UpdateScopeWithFinishedValue()
|
||||
{
|
||||
_top++;
|
||||
if (_stack.Count <= _top)
|
||||
_stack.Add(value);
|
||||
if (_currentPosition.Type == JsonContainerType.Array
|
||||
|| _currentPosition.Type == JsonContainerType.Constructor)
|
||||
{
|
||||
if (_currentPosition.Position == null)
|
||||
_currentPosition.Position = 0;
|
||||
else
|
||||
_currentPosition.Position++;
|
||||
}
|
||||
}
|
||||
|
||||
private void Push(JsonContainerType value)
|
||||
{
|
||||
UpdateScopeWithFinishedValue();
|
||||
|
||||
if (_currentPosition.Type == JsonContainerType.None)
|
||||
{
|
||||
_currentPosition.Type = value;
|
||||
}
|
||||
else
|
||||
_stack[_top] = value;
|
||||
{
|
||||
_stack.Add(_currentPosition);
|
||||
var state = new JsonPosition
|
||||
{
|
||||
Type = value
|
||||
};
|
||||
_currentPosition = state;
|
||||
}
|
||||
}
|
||||
|
||||
private JTokenType Pop()
|
||||
private JsonContainerType Pop()
|
||||
{
|
||||
JTokenType value = Peek();
|
||||
_top--;
|
||||
JsonPosition oldPosition;
|
||||
if (_stack.Count > 0)
|
||||
{
|
||||
oldPosition = _currentPosition;
|
||||
_currentPosition = _stack[_stack.Count - 1];
|
||||
_stack.RemoveAt(_stack.Count - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
oldPosition = _currentPosition;
|
||||
_currentPosition = new JsonPosition();
|
||||
}
|
||||
|
||||
return value;
|
||||
return oldPosition.Type;
|
||||
}
|
||||
|
||||
private JTokenType Peek()
|
||||
private JsonContainerType Peek()
|
||||
{
|
||||
return _stack[_top];
|
||||
return _currentPosition.Type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -280,7 +346,7 @@ namespace Newtonsoft.Json
|
||||
public virtual void WriteStartObject()
|
||||
{
|
||||
AutoComplete(JsonToken.StartObject);
|
||||
Push(JTokenType.Object);
|
||||
Push(JsonContainerType.Object);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -297,7 +363,7 @@ namespace Newtonsoft.Json
|
||||
public virtual void WriteStartArray()
|
||||
{
|
||||
AutoComplete(JsonToken.StartArray);
|
||||
Push(JTokenType.Array);
|
||||
Push(JsonContainerType.Array);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -315,7 +381,7 @@ namespace Newtonsoft.Json
|
||||
public virtual void WriteStartConstructor(string name)
|
||||
{
|
||||
AutoComplete(JsonToken.StartConstructor);
|
||||
Push(JTokenType.Constructor);
|
||||
Push(JsonContainerType.Constructor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -332,6 +398,7 @@ namespace Newtonsoft.Json
|
||||
/// <param name="name">The name of the property.</param>
|
||||
public virtual void WritePropertyName(string name)
|
||||
{
|
||||
_currentPosition.PropertyName = name;
|
||||
AutoComplete(JsonToken.PropertyName);
|
||||
}
|
||||
|
||||
@@ -482,17 +549,17 @@ namespace Newtonsoft.Json
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteEnd(JTokenType type)
|
||||
private void WriteEnd(JsonContainerType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case JTokenType.Object:
|
||||
case JsonContainerType.Object:
|
||||
WriteEndObject();
|
||||
break;
|
||||
case JTokenType.Array:
|
||||
case JsonContainerType.Array:
|
||||
WriteEndArray();
|
||||
break;
|
||||
case JTokenType.Constructor:
|
||||
case JsonContainerType.Constructor:
|
||||
WriteEndConstructor();
|
||||
break;
|
||||
default:
|
||||
@@ -502,36 +569,36 @@ namespace Newtonsoft.Json
|
||||
|
||||
private void AutoCompleteAll()
|
||||
{
|
||||
while (_top > 0)
|
||||
while (Top > 0)
|
||||
{
|
||||
WriteEnd();
|
||||
}
|
||||
}
|
||||
|
||||
private JTokenType GetTypeForCloseToken(JsonToken token)
|
||||
private JsonContainerType GetTypeForCloseToken(JsonToken token)
|
||||
{
|
||||
switch (token)
|
||||
{
|
||||
case JsonToken.EndObject:
|
||||
return JTokenType.Object;
|
||||
return JsonContainerType.Object;
|
||||
case JsonToken.EndArray:
|
||||
return JTokenType.Array;
|
||||
return JsonContainerType.Array;
|
||||
case JsonToken.EndConstructor:
|
||||
return JTokenType.Constructor;
|
||||
return JsonContainerType.Constructor;
|
||||
default:
|
||||
throw new JsonWriterException("No type for token: " + token);
|
||||
}
|
||||
}
|
||||
|
||||
private JsonToken GetCloseTokenForType(JTokenType type)
|
||||
private JsonToken GetCloseTokenForType(JsonContainerType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case JTokenType.Object:
|
||||
case JsonContainerType.Object:
|
||||
return JsonToken.EndObject;
|
||||
case JTokenType.Array:
|
||||
case JsonContainerType.Array:
|
||||
return JsonToken.EndArray;
|
||||
case JTokenType.Constructor:
|
||||
case JsonContainerType.Constructor:
|
||||
return JsonToken.EndConstructor;
|
||||
default:
|
||||
throw new JsonWriterException("No close token for type: " + type);
|
||||
@@ -543,15 +610,24 @@ namespace Newtonsoft.Json
|
||||
// write closing symbol and calculate new state
|
||||
|
||||
int levelsToComplete = 0;
|
||||
JsonContainerType type = GetTypeForCloseToken(tokenBeingClosed);
|
||||
|
||||
for (int i = 0; i < _top; i++)
|
||||
if (_currentPosition.Type == type)
|
||||
{
|
||||
int currentLevel = _top - i;
|
||||
|
||||
if (_stack[currentLevel] == GetTypeForCloseToken(tokenBeingClosed))
|
||||
levelsToComplete = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int top = Top - 2;
|
||||
for (int i = top; i >= 0; i--)
|
||||
{
|
||||
levelsToComplete = i + 1;
|
||||
break;
|
||||
int currentLevel = top - i;
|
||||
|
||||
if (_stack[currentLevel].Type == type)
|
||||
{
|
||||
levelsToComplete = i + 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -569,26 +645,26 @@ namespace Newtonsoft.Json
|
||||
}
|
||||
|
||||
WriteEnd(token);
|
||||
}
|
||||
|
||||
JTokenType currentLevelType = Peek();
|
||||
JsonContainerType currentLevelType = Peek();
|
||||
|
||||
switch (currentLevelType)
|
||||
{
|
||||
case JTokenType.Object:
|
||||
_currentState = State.Object;
|
||||
break;
|
||||
case JTokenType.Array:
|
||||
_currentState = State.Array;
|
||||
break;
|
||||
case JTokenType.Constructor:
|
||||
_currentState = State.Array;
|
||||
break;
|
||||
case JTokenType.None:
|
||||
_currentState = State.Start;
|
||||
break;
|
||||
default:
|
||||
throw new JsonWriterException("Unknown JsonType: " + currentLevelType);
|
||||
switch (currentLevelType)
|
||||
{
|
||||
case JsonContainerType.Object:
|
||||
_currentState = State.Object;
|
||||
break;
|
||||
case JsonContainerType.Array:
|
||||
_currentState = State.Array;
|
||||
break;
|
||||
case JsonContainerType.Constructor:
|
||||
_currentState = State.Array;
|
||||
break;
|
||||
case JsonContainerType.None:
|
||||
_currentState = State.Start;
|
||||
break;
|
||||
default:
|
||||
throw new JsonWriterException("Unknown JsonType: " + currentLevelType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -623,11 +699,18 @@ namespace Newtonsoft.Json
|
||||
|
||||
internal void AutoComplete(JsonToken tokenBeingWritten)
|
||||
{
|
||||
if (tokenBeingWritten != JsonToken.StartObject
|
||||
&& tokenBeingWritten != JsonToken.StartArray
|
||||
&& tokenBeingWritten != JsonToken.StartConstructor)
|
||||
UpdateScopeWithFinishedValue();
|
||||
|
||||
// gets new state based on the current state and what is being written
|
||||
State newState = StateArray[(int)tokenBeingWritten][(int)_currentState];
|
||||
|
||||
if (newState == State.Error)
|
||||
{
|
||||
throw new JsonWriterException("Token {0} in state {1} would result in an invalid JSON object.".FormatWith(CultureInfo.InvariantCulture, tokenBeingWritten.ToString(), _currentState.ToString()));
|
||||
}
|
||||
|
||||
if ((_currentState == State.Object || _currentState == State.Array || _currentState == State.Constructor) && tokenBeingWritten != JsonToken.Comment)
|
||||
{
|
||||
|
||||
@@ -85,7 +85,7 @@ using System.Security;
|
||||
// by using the '*' as shown below:
|
||||
[assembly: AssemblyVersion("4.0.8.0")]
|
||||
#if !PocketPC
|
||||
[assembly: AssemblyFileVersion("4.0.8.14618")]
|
||||
[assembly: AssemblyFileVersion("4.0.8.14623")]
|
||||
#endif
|
||||
|
||||
[assembly: CLSCompliant(true)]
|
||||
|
||||
@@ -35,11 +35,12 @@ namespace Newtonsoft.Json.Serialization
|
||||
/// </summary>
|
||||
public class ErrorContext
|
||||
{
|
||||
internal ErrorContext(object originalObject, object member, Exception error)
|
||||
internal ErrorContext(object originalObject, object member, string path, Exception error)
|
||||
{
|
||||
OriginalObject = originalObject;
|
||||
Member = member;
|
||||
Error = error;
|
||||
Path = path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -58,6 +59,11 @@ namespace Newtonsoft.Json.Serialization
|
||||
/// <value>The member that caused the error.</value>
|
||||
public object Member { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets the path of the JSON location when the error occurred.
|
||||
/// </summary>
|
||||
/// <value>The path of the JSON location when the error occurred.</value>
|
||||
public string Path { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether this <see cref="ErrorContext"/> is handled.
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if handled; otherwise, <c>false</c>.</value>
|
||||
|
||||
@@ -37,7 +37,12 @@ namespace Newtonsoft.Json.Serialization
|
||||
/// </summary>
|
||||
public class JsonArrayContract : JsonContract
|
||||
{
|
||||
internal Type CollectionItemType { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Type"/> of the collection items.
|
||||
/// </summary>
|
||||
/// <value>The <see cref="Type"/> of the collection items.</value>
|
||||
public Type CollectionItemType { get; private set; }
|
||||
|
||||
internal JsonContract CollectionItemContract { get; set; }
|
||||
|
||||
private readonly bool _isCollectionItemTypeNullableType;
|
||||
|
||||
@@ -42,8 +42,16 @@ namespace Newtonsoft.Json.Serialization
|
||||
/// <value>The property name resolver.</value>
|
||||
public Func<string, string> PropertyNameResolver { get; set; }
|
||||
|
||||
internal Type DictionaryKeyType { get; private set; }
|
||||
internal Type DictionaryValueType { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Type"/> of the dictionary keys.
|
||||
/// </summary>
|
||||
/// <value>The <see cref="Type"/> of the dictionary keys.</value>
|
||||
public Type DictionaryKeyType { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Type"/> of the dictionary values.
|
||||
/// </summary>
|
||||
/// <value>The <see cref="Type"/> of the dictionary values.</value>
|
||||
public Type DictionaryValueType { get; private set; }
|
||||
|
||||
internal JsonContract DictionaryKeyContract { get; set; }
|
||||
internal JsonContract DictionaryValueContract { get; set; }
|
||||
|
||||
@@ -79,10 +79,10 @@ namespace Newtonsoft.Json.Serialization
|
||||
}
|
||||
}
|
||||
|
||||
protected ErrorContext GetErrorContext(object currentObject, object member, Exception error)
|
||||
protected ErrorContext GetErrorContext(object currentObject, object member, string path, Exception error)
|
||||
{
|
||||
if (_currentErrorContext == null)
|
||||
_currentErrorContext = new ErrorContext(currentObject, member, error);
|
||||
_currentErrorContext = new ErrorContext(currentObject, member, path, error);
|
||||
|
||||
if (_currentErrorContext.Error != error)
|
||||
throw new InvalidOperationException("Current error context error is different to requested error.");
|
||||
@@ -98,9 +98,9 @@ namespace Newtonsoft.Json.Serialization
|
||||
_currentErrorContext = null;
|
||||
}
|
||||
|
||||
protected bool IsErrorHandled(object currentObject, JsonContract contract, object keyValue, Exception ex)
|
||||
protected bool IsErrorHandled(object currentObject, JsonContract contract, object keyValue, string path, Exception ex)
|
||||
{
|
||||
ErrorContext errorContext = GetErrorContext(currentObject, keyValue, ex);
|
||||
ErrorContext errorContext = GetErrorContext(currentObject, keyValue, path, ex);
|
||||
contract.InvokeOnError(currentObject, Serializer.Context, errorContext);
|
||||
|
||||
if (!errorContext.Handled)
|
||||
|
||||
@@ -712,7 +712,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (IsErrorHandled(dictionary, contract, keyValue, ex))
|
||||
if (IsErrorHandled(dictionary, contract, keyValue, reader.Path, ex))
|
||||
HandleError(reader, initialDepth);
|
||||
else
|
||||
throw;
|
||||
@@ -767,39 +767,47 @@ namespace Newtonsoft.Json.Serialization
|
||||
contract.InvokeOnDeserializing(list, Serializer.Context);
|
||||
|
||||
int initialDepth = reader.Depth;
|
||||
int index = 0;
|
||||
|
||||
JsonContract collectionItemContract = GetContractSafe(contract.CollectionItemType);
|
||||
JsonConverter collectionItemConverter = GetConverter(collectionItemContract, null);
|
||||
|
||||
while (ReadForType(reader, collectionItemContract, collectionItemConverter != null, true))
|
||||
while (true)
|
||||
{
|
||||
switch (reader.TokenType)
|
||||
try
|
||||
{
|
||||
case JsonToken.EndArray:
|
||||
contract.InvokeOnDeserialized(list, Serializer.Context);
|
||||
|
||||
return wrappedList.UnderlyingCollection;
|
||||
case JsonToken.Comment:
|
||||
break;
|
||||
default:
|
||||
try
|
||||
if (ReadForType(reader, collectionItemContract, collectionItemConverter != null, true))
|
||||
{
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
object value = CreateValueNonProperty(reader, contract.CollectionItemType, collectionItemContract, collectionItemConverter);
|
||||
case JsonToken.EndArray:
|
||||
contract.InvokeOnDeserialized(list, Serializer.Context);
|
||||
|
||||
wrappedList.Add(value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (IsErrorHandled(list, contract, wrappedList.Count, ex))
|
||||
{
|
||||
HandleError(reader, initialDepth);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw;
|
||||
}
|
||||
return wrappedList.UnderlyingCollection;
|
||||
case JsonToken.Comment:
|
||||
break;
|
||||
default:
|
||||
object value = CreateValueNonProperty(reader, contract.CollectionItemType, collectionItemContract, collectionItemConverter);
|
||||
|
||||
wrappedList.Add(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (IsErrorHandled(list, contract, index, reader.Path, ex))
|
||||
HandleError(reader, initialDepth);
|
||||
else
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -911,7 +919,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (IsErrorHandled(newObject, contract, memberName, ex))
|
||||
if (IsErrorHandled(newObject, contract, memberName, reader.Path, ex))
|
||||
HandleError(reader, initialDepth);
|
||||
else
|
||||
throw;
|
||||
@@ -1155,71 +1163,88 @@ namespace Newtonsoft.Json.Serialization
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonToken.PropertyName:
|
||||
string memberName = reader.Value.ToString();
|
||||
|
||||
try
|
||||
{
|
||||
// attempt exact case match first
|
||||
// then try match ignoring case
|
||||
JsonProperty property = contract.Properties.GetClosestMatchProperty(memberName);
|
||||
string memberName = reader.Value.ToString();
|
||||
|
||||
if (property == null)
|
||||
try
|
||||
{
|
||||
if (Serializer.MissingMemberHandling == MissingMemberHandling.Error)
|
||||
throw CreateSerializationException(reader, "Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, contract.UnderlyingType.Name));
|
||||
// attempt exact case match first
|
||||
// then try match ignoring case
|
||||
JsonProperty property = contract.Properties.GetClosestMatchProperty(memberName);
|
||||
|
||||
reader.Skip();
|
||||
continue;
|
||||
if (property == null)
|
||||
{
|
||||
if (Serializer.MissingMemberHandling == MissingMemberHandling.Error)
|
||||
throw CreateSerializationException(reader, "Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, contract.UnderlyingType.Name));
|
||||
|
||||
reader.Skip();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (property.PropertyContract == null)
|
||||
property.PropertyContract = GetContractSafe(property.PropertyType);
|
||||
|
||||
JsonConverter propertyConverter = GetConverter(property.PropertyContract, property.MemberConverter);
|
||||
|
||||
if (!ReadForType(reader, property.PropertyContract, propertyConverter != null, false))
|
||||
throw CreateSerializationException(reader, "Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName));
|
||||
|
||||
SetPropertyPresence(reader, property, propertiesPresence);
|
||||
|
||||
SetPropertyValue(property, propertyConverter, reader, newObject);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (IsErrorHandled(newObject, contract, memberName, reader.Path, ex))
|
||||
HandleError(reader, initialDepth);
|
||||
else
|
||||
throw;
|
||||
}
|
||||
|
||||
if (property.PropertyContract == null)
|
||||
property.PropertyContract = GetContractSafe(property.PropertyType);
|
||||
|
||||
JsonConverter propertyConverter = GetConverter(property.PropertyContract, property.MemberConverter);
|
||||
|
||||
if (!ReadForType(reader, property.PropertyContract, propertyConverter != null, false))
|
||||
throw CreateSerializationException(reader, "Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName));
|
||||
|
||||
SetPropertyPresence(reader, property, propertiesPresence);
|
||||
|
||||
SetPropertyValue(property, propertyConverter, reader, newObject);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (IsErrorHandled(newObject, contract, memberName, ex))
|
||||
HandleError(reader, initialDepth);
|
||||
else
|
||||
throw;
|
||||
}
|
||||
break;
|
||||
case JsonToken.EndObject:
|
||||
foreach (KeyValuePair<JsonProperty, PropertyPresence> propertyPresence in propertiesPresence)
|
||||
{
|
||||
JsonProperty property = propertyPresence.Key;
|
||||
PropertyPresence presence = propertyPresence.Value;
|
||||
|
||||
switch (presence)
|
||||
foreach (KeyValuePair<JsonProperty, PropertyPresence> propertyPresence in propertiesPresence)
|
||||
{
|
||||
case PropertyPresence.None:
|
||||
if (property.Required == Required.AllowNull || property.Required == Required.Always)
|
||||
throw CreateSerializationException(reader, "Required property '{0}' not found in JSON.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName));
|
||||
JsonProperty property = propertyPresence.Key;
|
||||
PropertyPresence presence = propertyPresence.Value;
|
||||
|
||||
if (property.PropertyContract == null)
|
||||
property.PropertyContract = GetContractSafe(property.PropertyType);
|
||||
if (presence == PropertyPresence.None || presence == PropertyPresence.Null)
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (presence)
|
||||
{
|
||||
case PropertyPresence.None:
|
||||
if (property.Required == Required.AllowNull || property.Required == Required.Always)
|
||||
throw CreateSerializationException(reader, "Required property '{0}' not found in JSON.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName));
|
||||
|
||||
if (HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling), DefaultValueHandling.Populate)
|
||||
&& property.Writable)
|
||||
property.ValueProvider.SetValue(newObject, EnsureType(reader, property.DefaultValue, CultureInfo.InvariantCulture, property.PropertyContract, property.PropertyType));
|
||||
break;
|
||||
case PropertyPresence.Null:
|
||||
if (property.Required == Required.Always)
|
||||
throw CreateSerializationException(reader, "Required property '{0}' expects a value but got null.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName));
|
||||
break;
|
||||
if (property.PropertyContract == null)
|
||||
property.PropertyContract = GetContractSafe(property.PropertyType);
|
||||
|
||||
if (HasFlag(property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling), DefaultValueHandling.Populate)
|
||||
&& property.Writable)
|
||||
property.ValueProvider.SetValue(newObject, EnsureType(reader, property.DefaultValue, CultureInfo.InvariantCulture, property.PropertyContract, property.PropertyType));
|
||||
break;
|
||||
case PropertyPresence.Null:
|
||||
if (property.Required == Required.Always)
|
||||
throw CreateSerializationException(reader, "Required property '{0}' expects a value but got null.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName));
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (IsErrorHandled(newObject, contract, property.PropertyName, reader.Path, ex))
|
||||
HandleError(reader, initialDepth);
|
||||
else
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
contract.InvokeOnDeserialized(newObject, Serializer.Context);
|
||||
return newObject;
|
||||
contract.InvokeOnDeserialized(newObject, Serializer.Context);
|
||||
return newObject;
|
||||
}
|
||||
case JsonToken.Comment:
|
||||
// ignore
|
||||
break;
|
||||
|
||||
@@ -330,7 +330,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (IsErrorHandled(value, contract, property.PropertyName, ex))
|
||||
if (IsErrorHandled(value, contract, property.PropertyName, writer.ContainerPath, ex))
|
||||
HandleError(writer, initialDepth);
|
||||
else
|
||||
throw;
|
||||
@@ -439,7 +439,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (IsErrorHandled(values.UnderlyingCollection, contract, index, ex))
|
||||
if (IsErrorHandled(values.UnderlyingCollection, contract, index, writer.ContainerPath, ex))
|
||||
HandleError(writer, initialDepth);
|
||||
else
|
||||
throw;
|
||||
@@ -614,7 +614,7 @@ namespace Newtonsoft.Json.Serialization
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (IsErrorHandled(values.UnderlyingDictionary, contract, propertyName, ex))
|
||||
if (IsErrorHandled(values.UnderlyingDictionary, contract, propertyName, writer.ContainerPath, ex))
|
||||
HandleError(writer, initialDepth);
|
||||
else
|
||||
throw;
|
||||
|
||||
Reference in New Issue
Block a user