1165 lines
42 KiB
C#
1165 lines
42 KiB
C#
#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.Diagnostics;
|
|
using System.Runtime.Serialization;
|
|
using System.Text;
|
|
using Newtonsoft.Json.Converters;
|
|
using Newtonsoft.Json.Serialization;
|
|
using Newtonsoft.Json.Tests.TestObjects;
|
|
#if NET20
|
|
using Newtonsoft.Json.Utilities.LinqBridge;
|
|
#else
|
|
using System.Linq;
|
|
#endif
|
|
#if DNXCORE50
|
|
using Xunit;
|
|
using Test = Xunit.FactAttribute;
|
|
using Assert = Newtonsoft.Json.Tests.XUnitAssert;
|
|
#else
|
|
using NUnit.Framework;
|
|
#endif
|
|
using System.IO;
|
|
using Newtonsoft.Json.Linq;
|
|
using ErrorEventArgs = Newtonsoft.Json.Serialization.ErrorEventArgs;
|
|
|
|
namespace Newtonsoft.Json.Tests.Serialization
|
|
{
|
|
[TestFixture]
|
|
public class SerializationErrorHandlingTests : TestFixtureBase
|
|
{
|
|
[Test]
|
|
public void ErrorHandlingMetadata()
|
|
{
|
|
List<Exception> errors = new List<Exception>();
|
|
|
|
AAA a2 = JsonConvert.DeserializeObject<AAA>(@"{""MyTest"":{""$type"":""<Namespace>.JsonTest+MyTest2, <Assembly>""}}", new JsonSerializerSettings
|
|
{
|
|
TypeNameHandling = TypeNameHandling.Auto,
|
|
Error = (object sender, Json.Serialization.ErrorEventArgs e) =>
|
|
{
|
|
errors.Add(e.ErrorContext.Error);
|
|
e.ErrorContext.Handled = true;
|
|
}
|
|
});
|
|
|
|
Assert.IsNotNull(a2);
|
|
Assert.AreEqual(1, errors.Count);
|
|
Assert.AreEqual("Error resolving type specified in JSON '<Namespace>.JsonTest+MyTest2, <Assembly>'. Path 'MyTest.$type', line 1, position 61.", errors[0].Message);
|
|
}
|
|
|
|
[Test]
|
|
public void ErrorHandlingMetadata_TopLevel()
|
|
{
|
|
List<Exception> errors = new List<Exception>();
|
|
|
|
JObject a2 = (JObject)JsonConvert.DeserializeObject(@"{""$type"":""<Namespace>.JsonTest+MyTest2, <Assembly>""}", new JsonSerializerSettings
|
|
{
|
|
TypeNameHandling = TypeNameHandling.Auto,
|
|
Error = (object sender, Json.Serialization.ErrorEventArgs e) =>
|
|
{
|
|
errors.Add(e.ErrorContext.Error);
|
|
e.ErrorContext.Handled = true;
|
|
}
|
|
});
|
|
|
|
Assert.IsNull(a2);
|
|
Assert.AreEqual(1, errors.Count);
|
|
Assert.AreEqual("Error resolving type specified in JSON '<Namespace>.JsonTest+MyTest2, <Assembly>'. Path '$type', line 1, position 51.", errors[0].Message);
|
|
}
|
|
|
|
public class AAA
|
|
{
|
|
public ITest MyTest { get; set; }
|
|
}
|
|
|
|
public interface ITest { }
|
|
public class MyTest : ITest { }
|
|
|
|
public class MyClass1
|
|
{
|
|
[JsonProperty("myint")]
|
|
public int MyInt { get; set; }
|
|
[JsonProperty("Mybool")]
|
|
public bool Mybool { get; set; }
|
|
}
|
|
|
|
[Test]
|
|
public void ErrorDeserializingIntegerInObject()
|
|
{
|
|
var errors = new List<string>();
|
|
var json = "{\"myint\":3554860000,\"Mybool\":false}";
|
|
var i = JsonConvert.DeserializeObject<MyClass1>(json, new JsonSerializerSettings
|
|
{
|
|
Error = delegate (object sender, Newtonsoft.Json.Serialization.ErrorEventArgs args)
|
|
{
|
|
errors.Add(args.ErrorContext.Error.Message);
|
|
args.ErrorContext.Handled = true;
|
|
}
|
|
});
|
|
|
|
Assert.AreEqual(1, errors.Count);
|
|
Assert.AreEqual("JSON integer 3554860000 is too large or small for an Int32. Path 'myint', line 1, position 19.", errors[0]);
|
|
}
|
|
|
|
[Test]
|
|
public void ErrorDeserializingListHandled()
|
|
{
|
|
string json = @"[
|
|
{
|
|
""Name"": ""Jim"",
|
|
""BirthDate"": ""\/Date(978048000000)\/"",
|
|
""LastModified"": ""\/Date(978048000000)\/""
|
|
},
|
|
{
|
|
""Name"": ""Jim"",
|
|
""BirthDate"": ""\/Date(978048000000)\/"",
|
|
""LastModified"": ""\/Date(978048000000)\/""
|
|
}
|
|
]";
|
|
|
|
var possibleMsgs = new[]
|
|
{
|
|
"[1] - Error message for member 1 = An item with the same key has already been added.",
|
|
"[1] - Error message for member 1 = An element with the same key already exists in the dictionary.", // mono
|
|
"[1] - Error message for member 1 = An item with the same key has already been added. Key: Jim" // netcore
|
|
};
|
|
VersionKeyedCollection c = JsonConvert.DeserializeObject<VersionKeyedCollection>(json);
|
|
Assert.AreEqual(1, c.Count);
|
|
Assert.AreEqual(1, c.Messages.Count);
|
|
|
|
Console.WriteLine(c.Messages[0]);
|
|
Assert.IsTrue(possibleMsgs.Any(m => m == c.Messages[0]), "Expected One of: " + Environment.NewLine + string.Join(Environment.NewLine, possibleMsgs) + Environment.NewLine + "Was: " + Environment.NewLine + c.Messages[0]);
|
|
}
|
|
|
|
[Test]
|
|
public void DeserializingErrorInChildObject()
|
|
{
|
|
ListErrorObjectCollection c = JsonConvert.DeserializeObject<ListErrorObjectCollection>(@"[
|
|
{
|
|
""Member"": ""Value1"",
|
|
""Member2"": null
|
|
},
|
|
{
|
|
""Member"": ""Value2""
|
|
},
|
|
{
|
|
""ThrowError"": ""Value"",
|
|
""Object"": {
|
|
""Array"": [
|
|
1,
|
|
2
|
|
]
|
|
}
|
|
},
|
|
{
|
|
""ThrowError"": ""Handle this!"",
|
|
""Member"": ""Value3""
|
|
}
|
|
]");
|
|
|
|
Assert.AreEqual(3, c.Count);
|
|
Assert.AreEqual("Value1", c[0].Member);
|
|
Assert.AreEqual("Value2", c[1].Member);
|
|
Assert.AreEqual("Value3", c[2].Member);
|
|
Assert.AreEqual("Handle this!", c[2].ThrowError);
|
|
}
|
|
|
|
[Test]
|
|
public void SerializingErrorIn3DArray()
|
|
{
|
|
ListErrorObject[,,] c = new ListErrorObject[,,]
|
|
{
|
|
{
|
|
{
|
|
new ListErrorObject
|
|
{
|
|
Member = "Value1",
|
|
ThrowError = "Handle this!",
|
|
Member2 = "Member1"
|
|
},
|
|
new ListErrorObject
|
|
{
|
|
Member = "Value2",
|
|
Member2 = "Member2"
|
|
},
|
|
new ListErrorObject
|
|
{
|
|
Member = "Value3",
|
|
ThrowError = "Handle that!",
|
|
Member2 = "Member3"
|
|
}
|
|
},
|
|
{
|
|
new ListErrorObject
|
|
{
|
|
Member = "Value1",
|
|
ThrowError = "Handle this!",
|
|
Member2 = "Member1"
|
|
},
|
|
new ListErrorObject
|
|
{
|
|
Member = "Value2",
|
|
Member2 = "Member2"
|
|
},
|
|
new ListErrorObject
|
|
{
|
|
Member = "Value3",
|
|
ThrowError = "Handle that!",
|
|
Member2 = "Member3"
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
string json = JsonConvert.SerializeObject(c, new JsonSerializerSettings
|
|
{
|
|
Formatting = Formatting.Indented,
|
|
Error = (s, e) =>
|
|
{
|
|
if (e.CurrentObject.GetType().IsArray)
|
|
{
|
|
e.ErrorContext.Handled = true;
|
|
}
|
|
}
|
|
});
|
|
|
|
StringAssert.AreEqual(@"[
|
|
[
|
|
[
|
|
{
|
|
""Member"": ""Value1"",
|
|
""ThrowError"": ""Handle this!"",
|
|
""Member2"": ""Member1""
|
|
},
|
|
{
|
|
""Member"": ""Value2""
|
|
},
|
|
{
|
|
""Member"": ""Value3"",
|
|
""ThrowError"": ""Handle that!"",
|
|
""Member2"": ""Member3""
|
|
}
|
|
],
|
|
[
|
|
{
|
|
""Member"": ""Value1"",
|
|
""ThrowError"": ""Handle this!"",
|
|
""Member2"": ""Member1""
|
|
},
|
|
{
|
|
""Member"": ""Value2""
|
|
},
|
|
{
|
|
""Member"": ""Value3"",
|
|
""ThrowError"": ""Handle that!"",
|
|
""Member2"": ""Member3""
|
|
}
|
|
]
|
|
]
|
|
]", json);
|
|
}
|
|
|
|
[Test]
|
|
public void SerializingErrorInChildObject()
|
|
{
|
|
ListErrorObjectCollection c = new ListErrorObjectCollection
|
|
{
|
|
new ListErrorObject
|
|
{
|
|
Member = "Value1",
|
|
ThrowError = "Handle this!",
|
|
Member2 = "Member1"
|
|
},
|
|
new ListErrorObject
|
|
{
|
|
Member = "Value2",
|
|
Member2 = "Member2"
|
|
},
|
|
new ListErrorObject
|
|
{
|
|
Member = "Value3",
|
|
ThrowError = "Handle that!",
|
|
Member2 = "Member3"
|
|
}
|
|
};
|
|
|
|
string json = JsonConvert.SerializeObject(c, Formatting.Indented);
|
|
|
|
StringAssert.AreEqual(@"[
|
|
{
|
|
""Member"": ""Value1"",
|
|
""ThrowError"": ""Handle this!"",
|
|
""Member2"": ""Member1""
|
|
},
|
|
{
|
|
""Member"": ""Value2""
|
|
},
|
|
{
|
|
""Member"": ""Value3"",
|
|
""ThrowError"": ""Handle that!"",
|
|
""Member2"": ""Member3""
|
|
}
|
|
]", json);
|
|
}
|
|
|
|
[Test]
|
|
public void DeserializingErrorInDateTimeCollection()
|
|
{
|
|
DateTimeErrorObjectCollection c = JsonConvert.DeserializeObject<DateTimeErrorObjectCollection>(@"[
|
|
""2009-09-09T00:00:00Z"",
|
|
""kjhkjhkjhkjh"",
|
|
[
|
|
1
|
|
],
|
|
""1977-02-20T00:00:00Z"",
|
|
null,
|
|
""2000-12-01T00:00:00Z""
|
|
]", new IsoDateTimeConverter());
|
|
|
|
Assert.AreEqual(3, c.Count);
|
|
Assert.AreEqual(new DateTime(2009, 9, 9, 0, 0, 0, DateTimeKind.Utc), c[0]);
|
|
Assert.AreEqual(new DateTime(1977, 2, 20, 0, 0, 0, DateTimeKind.Utc), c[1]);
|
|
Assert.AreEqual(new DateTime(2000, 12, 1, 0, 0, 0, DateTimeKind.Utc), c[2]);
|
|
}
|
|
|
|
[Test]
|
|
public void DeserializingErrorHandlingUsingEvent()
|
|
{
|
|
List<string> errors = new List<string>();
|
|
|
|
JsonSerializer serializer = JsonSerializer.Create(new JsonSerializerSettings
|
|
{
|
|
Error = delegate(object sender, ErrorEventArgs args)
|
|
{
|
|
errors.Add(args.ErrorContext.Path + " - " + args.ErrorContext.Member + " - " + args.ErrorContext.Error.Message);
|
|
args.ErrorContext.Handled = true;
|
|
},
|
|
Converters = { new IsoDateTimeConverter() }
|
|
});
|
|
var c = serializer.Deserialize<List<DateTime>>(new JsonTextReader(new StringReader(@"[
|
|
""2009-09-09T00:00:00Z"",
|
|
""I am not a date and will error!"",
|
|
[
|
|
1
|
|
],
|
|
""1977-02-20T00:00:00Z"",
|
|
null,
|
|
""2000-12-01T00:00:00Z""
|
|
]")));
|
|
|
|
// 2009-09-09T00:00:00Z
|
|
// 1977-02-20T00:00:00Z
|
|
// 2000-12-01T00:00:00Z
|
|
|
|
// The string was not recognized as a valid DateTime. There is a unknown word starting at index 0.
|
|
// Unexpected token parsing date. Expected String, got StartArray.
|
|
// Cannot convert null value to System.DateTime.
|
|
|
|
Assert.AreEqual(3, c.Count);
|
|
Assert.AreEqual(new DateTime(2009, 9, 9, 0, 0, 0, DateTimeKind.Utc), c[0]);
|
|
Assert.AreEqual(new DateTime(1977, 2, 20, 0, 0, 0, DateTimeKind.Utc), c[1]);
|
|
Assert.AreEqual(new DateTime(2000, 12, 1, 0, 0, 0, DateTimeKind.Utc), c[2]);
|
|
|
|
Assert.AreEqual(3, errors.Count);
|
|
var possibleErrs = new[]
|
|
{
|
|
#if !(NET20 || NET35)
|
|
"[1] - 1 - The string was not recognized as a valid DateTime. There is an unknown word starting at index 0.",
|
|
"[1] - 1 - String was not recognized as a valid DateTime.",
|
|
"[1] - 1 - The string 'I am not a date and will error!' was not recognized as a valid DateTime. There is an unknown word starting at index '0'."
|
|
#else
|
|
// handle typo fix in later versions of .NET
|
|
"[1] - 1 - The string was not recognized as a valid DateTime. There is an unknown word starting at index 0.",
|
|
"[1] - 1 - The string was not recognized as a valid DateTime. There is a unknown word starting at index 0."
|
|
#endif
|
|
};
|
|
|
|
Assert.IsTrue(possibleErrs.Any(m => m == errors[0]),
|
|
"Expected One of: " + string.Join(Environment.NewLine, possibleErrs) + Environment.NewLine + "But was: " + errors[0]);
|
|
|
|
Assert.AreEqual("[2] - 2 - Unexpected token parsing date. Expected String, got StartArray. Path '[2]', line 4, position 9.", errors[1]);
|
|
Assert.AreEqual("[4] - 4 - Cannot convert null value to System.DateTime. Path '[4]', line 8, position 12.", errors[2]);
|
|
}
|
|
|
|
[Test]
|
|
public void DeserializingErrorInDateTimeCollectionWithAttributeWithEventNotCalled()
|
|
{
|
|
bool eventErrorHandlerCalled = false;
|
|
|
|
DateTimeErrorObjectCollection c = JsonConvert.DeserializeObject<DateTimeErrorObjectCollection>(
|
|
@"[
|
|
""2009-09-09T00:00:00Z"",
|
|
""kjhkjhkjhkjh"",
|
|
[
|
|
1
|
|
],
|
|
""1977-02-20T00:00:00Z"",
|
|
null,
|
|
""2000-12-01T00:00:00Z""
|
|
]",
|
|
new JsonSerializerSettings
|
|
{
|
|
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]);
|
|
Assert.AreEqual(new DateTime(1977, 2, 20, 0, 0, 0, DateTimeKind.Utc), c[1]);
|
|
Assert.AreEqual(new DateTime(2000, 12, 1, 0, 0, 0, DateTimeKind.Utc), c[2]);
|
|
|
|
Assert.AreEqual(false, eventErrorHandlerCalled);
|
|
}
|
|
|
|
[Test]
|
|
public void SerializePerson()
|
|
{
|
|
PersonError person = new PersonError
|
|
{
|
|
Name = "George Michael Bluth",
|
|
Age = 16,
|
|
Roles = null,
|
|
Title = "Mister Manager"
|
|
};
|
|
|
|
string json = JsonConvert.SerializeObject(person, Formatting.Indented);
|
|
|
|
StringAssert.AreEqual(@"{
|
|
""Name"": ""George Michael Bluth"",
|
|
""Age"": 16,
|
|
""Title"": ""Mister Manager""
|
|
}", json);
|
|
}
|
|
|
|
[Test]
|
|
public void DeserializeNestedUnhandled()
|
|
{
|
|
List<string> errors = new List<string>();
|
|
|
|
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.Path + " - " + args.ErrorContext.Member + " - " + args.ErrorContext.Error.Message);
|
|
}
|
|
};
|
|
|
|
serializer.Deserialize(new StringReader(json), typeof(List<List<DateTime>>));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
e = ex;
|
|
}
|
|
|
|
Assert.AreEqual(@"Could not convert string to DateTime: kjhkjhkjhkjh. Path '[0][0]', line 1, position 16.", e.Message);
|
|
|
|
Assert.AreEqual(1, errors.Count);
|
|
Assert.AreEqual(@"[0][0] - 0 - Could not convert string to DateTime: kjhkjhkjhkjh. Path '[0][0]', line 1, position 16.", errors[0]);
|
|
}
|
|
|
|
[Test]
|
|
public void MultipleRequiredPropertyErrors()
|
|
{
|
|
string json = "{}";
|
|
List<string> errors = new List<string>();
|
|
JsonSerializer serializer = new JsonSerializer();
|
|
serializer.MetadataPropertyHandling = MetadataPropertyHandling.Default;
|
|
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.IsTrue(errors[0].StartsWith(" - Required1 - Required property 'Required1' not found in JSON. Path '', line 1, position 2."));
|
|
Assert.IsTrue(errors[1].StartsWith(" - Required2 - Required property 'Required2' not found in JSON. Path '', line 1, position 2."));
|
|
}
|
|
|
|
[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. Path '[0]', line 1, position 4.", errors[0]);
|
|
Assert.AreEqual("[1] - 1 - Could not convert string to integer: b. Path '[1]', line 1, position 8.", errors[1]);
|
|
}
|
|
|
|
[Test]
|
|
public void HandlingMultidimensionalArrayErrors()
|
|
{
|
|
string json = "[[\"a\",\"45\"],[\"b\",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] - 0 - Could not convert string to integer: a. Path '[0][0]', line 1, position 5.", errors[0]);
|
|
Assert.AreEqual("[1][0] - 0 - Could not convert string to integer: b. Path '[1][0]', line 1, position 16.", errors[1]);
|
|
}
|
|
|
|
[Test]
|
|
public void ErrorHandlingAndAvoidingRecursiveDepthError()
|
|
{
|
|
string json = "{'A':{'A':{'A':{'A':{'A':{}}}}}}";
|
|
JsonSerializer serializer = new JsonSerializer() { };
|
|
IList<string> errors = new List<string>();
|
|
serializer.Error += (sender, e) =>
|
|
{
|
|
e.ErrorContext.Handled = true;
|
|
errors.Add(e.ErrorContext.Path);
|
|
};
|
|
|
|
serializer.Deserialize<Nest>(new JsonTextReader(new StringReader(json)) { MaxDepth = 3 });
|
|
|
|
Assert.AreEqual(1, errors.Count);
|
|
Assert.AreEqual("A.A.A", errors[0]);
|
|
}
|
|
|
|
public class Nest
|
|
{
|
|
public Nest A { get; set; }
|
|
}
|
|
|
|
[Test]
|
|
public void InfiniteErrorHandlingLoopFromInputError()
|
|
{
|
|
IList<string> errors = new List<string>();
|
|
|
|
JsonSerializer serializer = new JsonSerializer();
|
|
serializer.Error += (sender, e) =>
|
|
{
|
|
errors.Add(e.ErrorContext.Error.Message);
|
|
e.ErrorContext.Handled = true;
|
|
};
|
|
|
|
ErrorPerson[] result = serializer.Deserialize<ErrorPerson[]>(new JsonTextReader(new ThrowingReader()));
|
|
|
|
Assert.IsNull(result);
|
|
Assert.AreEqual(3, errors.Count);
|
|
Assert.AreEqual("too far", errors[0]);
|
|
Assert.AreEqual("too far", errors[1]);
|
|
Assert.AreEqual("Infinite loop detected from error handling. Path '[1023]', line 1, position 65536.", errors[2]);
|
|
}
|
|
|
|
[Test]
|
|
public void ArrayHandling()
|
|
{
|
|
IList<string> errors = new List<string>();
|
|
|
|
object o = JsonConvert.DeserializeObject(
|
|
"[0,x]",
|
|
typeof(int[]),
|
|
new JsonSerializerSettings
|
|
{
|
|
Error = (sender, arg) =>
|
|
{
|
|
errors.Add(arg.ErrorContext.Error.Message);
|
|
arg.ErrorContext.Handled = true;
|
|
}
|
|
});
|
|
|
|
Assert.IsNotNull(o);
|
|
|
|
Assert.AreEqual(1, errors.Count);
|
|
Assert.AreEqual("Unexpected character encountered while parsing value: x. Path '[0]', line 1, position 4.", errors[0]);
|
|
|
|
Assert.AreEqual(1, ((int[])o).Length);
|
|
Assert.AreEqual(0, ((int[])o)[0]);
|
|
}
|
|
|
|
[Test]
|
|
public void ArrayHandling_JTokenReader()
|
|
{
|
|
IList<string> errors = new List<string>();
|
|
|
|
JTokenReader reader = new JTokenReader(new JArray(0, true));
|
|
|
|
JsonSerializer serializer = JsonSerializer.Create(new JsonSerializerSettings
|
|
{
|
|
Error = (sender, arg) =>
|
|
{
|
|
errors.Add(arg.ErrorContext.Error.Message);
|
|
arg.ErrorContext.Handled = true;
|
|
}
|
|
});
|
|
object o = serializer.Deserialize(reader, typeof(int[]));
|
|
|
|
Assert.IsNotNull(o);
|
|
|
|
Assert.AreEqual(1, errors.Count);
|
|
Assert.AreEqual("Error reading integer. Unexpected token: Boolean. Path '[1]'.", errors[0]);
|
|
|
|
Assert.AreEqual(1, ((int[])o).Length);
|
|
Assert.AreEqual(0, ((int[])o)[0]);
|
|
}
|
|
|
|
[Test]
|
|
public void ArrayHandlingInObject()
|
|
{
|
|
IList<string> errors = new List<string>();
|
|
|
|
Dictionary<string, int[]> o = JsonConvert.DeserializeObject<Dictionary<string, int[]>>(
|
|
"{'badarray':[0,x,2],'goodarray':[0,1,2]}",
|
|
new JsonSerializerSettings
|
|
{
|
|
MetadataPropertyHandling = MetadataPropertyHandling.Default,
|
|
Error = (sender, arg) =>
|
|
{
|
|
errors.Add(arg.ErrorContext.Error.Message);
|
|
arg.ErrorContext.Handled = true;
|
|
}
|
|
});
|
|
|
|
Assert.IsNotNull(o);
|
|
|
|
Assert.AreEqual(2, errors.Count);
|
|
Assert.AreEqual("Unexpected character encountered while parsing value: x. Path 'badarray[0]', line 1, position 16.", errors[0]);
|
|
Assert.AreEqual("Unexpected character encountered while parsing value: ,. Path 'badarray[1]', line 1, position 17.", errors[1]);
|
|
|
|
Assert.AreEqual(2, o.Count);
|
|
Assert.AreEqual(2, o["badarray"].Length);
|
|
Assert.AreEqual(0, o["badarray"][0]);
|
|
Assert.AreEqual(2, o["badarray"][1]);
|
|
}
|
|
|
|
[Test]
|
|
public void ErrorHandlingEndOfContent()
|
|
{
|
|
IList<string> errors = new List<string>();
|
|
|
|
const string input = "{\"events\":[{\"code\":64411},{\"code\":64411,\"prio";
|
|
|
|
const int maxDepth = 256;
|
|
using (var jsonTextReader = new JsonTextReader(new StringReader(input)) { MaxDepth = maxDepth })
|
|
{
|
|
JsonSerializer jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings
|
|
{
|
|
MaxDepth = maxDepth,
|
|
MetadataPropertyHandling = MetadataPropertyHandling.Default
|
|
});
|
|
jsonSerializer.Error += (sender, e) =>
|
|
{
|
|
errors.Add(e.ErrorContext.Error.Message);
|
|
e.ErrorContext.Handled = true;
|
|
};
|
|
|
|
LogMessage logMessage = jsonSerializer.Deserialize<LogMessage>(jsonTextReader);
|
|
|
|
Assert.IsNotNull(logMessage.Events);
|
|
Assert.AreEqual(1, logMessage.Events.Count);
|
|
Assert.AreEqual("64411", logMessage.Events[0].Code);
|
|
}
|
|
|
|
Assert.AreEqual(3, errors.Count);
|
|
Assert.AreEqual(@"Unterminated string. Expected delimiter: "". Path 'events[1].code', line 1, position 45.", errors[0]);
|
|
Assert.AreEqual(@"Unexpected end when deserializing array. Path 'events[1].code', line 1, position 45.", errors[1]);
|
|
Assert.AreEqual(@"Unexpected end when deserializing object. Path 'events[1].code', line 1, position 45.", errors[2]);
|
|
}
|
|
|
|
[Test]
|
|
public void ErrorHandlingEndOfContentDictionary()
|
|
{
|
|
IList<string> errors = new List<string>();
|
|
|
|
const string input = "{\"events\":{\"code\":64411},\"events2\":{\"code\":64412,";
|
|
|
|
const int maxDepth = 256;
|
|
using (var jsonTextReader = new JsonTextReader(new StringReader(input)) { MaxDepth = maxDepth })
|
|
{
|
|
JsonSerializer jsonSerializer = JsonSerializer.Create(new JsonSerializerSettings { MaxDepth = maxDepth, MetadataPropertyHandling = MetadataPropertyHandling.Default });
|
|
jsonSerializer.Error += (sender, e) =>
|
|
{
|
|
errors.Add(e.ErrorContext.Error.Message);
|
|
e.ErrorContext.Handled = true;
|
|
};
|
|
|
|
IDictionary<string, LogEvent> logEvents = jsonSerializer.Deserialize<IDictionary<string, LogEvent>>(jsonTextReader);
|
|
|
|
Assert.IsNotNull(logEvents);
|
|
Assert.AreEqual(2, logEvents.Count);
|
|
Assert.AreEqual("64411", logEvents["events"].Code);
|
|
Assert.AreEqual("64412", logEvents["events2"].Code);
|
|
}
|
|
|
|
Assert.AreEqual(2, errors.Count);
|
|
Assert.AreEqual(@"Unexpected end when deserializing object. Path 'events2.code', line 1, position 49.", errors[0]);
|
|
Assert.AreEqual(@"Unexpected end when deserializing object. Path 'events2.code', line 1, position 49.", errors[1]);
|
|
}
|
|
|
|
#if !(NET35 || NET20 || PORTABLE40)
|
|
[Test]
|
|
public void ErrorHandlingEndOfContentDynamic()
|
|
{
|
|
IList<string> errors = new List<string>();
|
|
|
|
string json = @"{
|
|
""Explicit"": true,
|
|
""Decimal"": 99.9,
|
|
""Int"": 1,
|
|
""ChildObject"": {
|
|
""Integer"": 123";
|
|
|
|
TestDynamicObject newDynamicObject = JsonConvert.DeserializeObject<TestDynamicObject>(json, new JsonSerializerSettings
|
|
{
|
|
Error = (sender, e) =>
|
|
{
|
|
errors.Add(e.ErrorContext.Error.Message);
|
|
e.ErrorContext.Handled = true;
|
|
},
|
|
MetadataPropertyHandling = MetadataPropertyHandling.Default
|
|
});
|
|
Assert.AreEqual(true, newDynamicObject.Explicit);
|
|
|
|
dynamic d = newDynamicObject;
|
|
|
|
Assert.AreEqual(99.9, d.Decimal);
|
|
Assert.AreEqual(1, d.Int);
|
|
Assert.AreEqual(123, d.ChildObject.Integer);
|
|
|
|
Assert.AreEqual(2, errors.Count);
|
|
Assert.AreEqual(@"Unexpected end when deserializing object. Path 'ChildObject.Integer', line 6, position 18.", errors[0]);
|
|
Assert.AreEqual(@"Unexpected end when deserializing object. Path 'ChildObject.Integer', line 6, position 18.", errors[1]);
|
|
}
|
|
#endif
|
|
|
|
[Test]
|
|
public void WriteEndOnPropertyState()
|
|
{
|
|
JsonSerializerSettings settings = new JsonSerializerSettings();
|
|
settings.Error += (obj, args) => { args.ErrorContext.Handled = true; };
|
|
|
|
var data = new List<ErrorPerson2>()
|
|
{
|
|
new ErrorPerson2 { FirstName = "Scott", LastName = "Hanselman" },
|
|
new ErrorPerson2 { FirstName = "Scott", LastName = "Hunter" },
|
|
new ErrorPerson2 { FirstName = "Scott", LastName = "Guthrie" },
|
|
};
|
|
|
|
Dictionary<string, IEnumerable<IErrorPerson2>> dictionary = data.GroupBy(person => person.FirstName).ToDictionary(group => @group.Key, group => @group.Cast<IErrorPerson2>());
|
|
string output = JsonConvert.SerializeObject(dictionary, Formatting.None, settings);
|
|
Assert.AreEqual(@"{""Scott"":[]}", output);
|
|
}
|
|
|
|
[Test]
|
|
public void WriteEndOnPropertyState2()
|
|
{
|
|
JsonSerializerSettings settings = new JsonSerializerSettings();
|
|
settings.Error += (obj, args) => { args.ErrorContext.Handled = true; };
|
|
|
|
var data = new List<ErrorPerson2>
|
|
{
|
|
new ErrorPerson2 { FirstName = "Scott", LastName = "Hanselman" },
|
|
new ErrorPerson2 { FirstName = "Scott", LastName = "Hunter" },
|
|
new ErrorPerson2 { FirstName = "Scott", LastName = "Guthrie" },
|
|
new ErrorPerson2 { FirstName = "James", LastName = "Newton-King" },
|
|
};
|
|
|
|
Dictionary<string, IEnumerable<IErrorPerson2>> dictionary = data.GroupBy(person => person.FirstName).ToDictionary(group => @group.Key, group => @group.Cast<IErrorPerson2>());
|
|
string output = JsonConvert.SerializeObject(dictionary, Formatting.None, settings);
|
|
|
|
Assert.AreEqual(@"{""Scott"":[],""James"":[]}", output);
|
|
}
|
|
|
|
[Test]
|
|
public void NoObjectWithEvent()
|
|
{
|
|
string json = "{\"}";
|
|
byte[] byteArray = Encoding.UTF8.GetBytes(json);
|
|
MemoryStream stream = new MemoryStream(byteArray);
|
|
JsonTextReader jReader = new JsonTextReader(new StreamReader(stream));
|
|
JsonSerializer s = new JsonSerializer();
|
|
s.Error += (sender, args) => { args.ErrorContext.Handled = true; };
|
|
ErrorPerson2 obj = s.Deserialize<ErrorPerson2>(jReader);
|
|
|
|
Assert.IsNull(obj);
|
|
}
|
|
|
|
[Test]
|
|
public void NoObjectWithAttribute()
|
|
{
|
|
string json = "{\"}";
|
|
byte[] byteArray = Encoding.UTF8.GetBytes(json);
|
|
MemoryStream stream = new MemoryStream(byteArray);
|
|
JsonTextReader jReader = new JsonTextReader(new StreamReader(stream));
|
|
JsonSerializer s = new JsonSerializer();
|
|
|
|
ExceptionAssert.Throws<JsonReaderException>(() => { ErrorTestObject obj = s.Deserialize<ErrorTestObject>(jReader); }, @"Unterminated string. Expected delimiter: "". Path '', line 1, position 3.");
|
|
}
|
|
|
|
public class RootThing
|
|
{
|
|
public Something Something { get; set; }
|
|
}
|
|
|
|
public class RootSomethingElse
|
|
{
|
|
public SomethingElse SomethingElse { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// This could be an object we are passing up in an interface.
|
|
/// </summary>
|
|
[JsonConverter(typeof(SomethingConverter))]
|
|
public class Something
|
|
{
|
|
public class SomethingConverter : JsonConverter
|
|
{
|
|
public override bool CanConvert(Type objectType)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
|
{
|
|
try
|
|
{
|
|
// Do own stuff.
|
|
// Then call deserialise for inner object.
|
|
var innerObject = serializer.Deserialize(reader, typeof(SomethingElse));
|
|
|
|
return null;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// If we get an error wrap it in something less scary.
|
|
throw new Exception("An error occurred.", ex);
|
|
}
|
|
}
|
|
|
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
|
{
|
|
try
|
|
{
|
|
Something s = (Something)value;
|
|
|
|
// Do own stuff.
|
|
// Then call serialise for inner object.
|
|
serializer.Serialize(writer, s.RootSomethingElse);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// If we get an error wrap it in something less scary.
|
|
throw new Exception("An error occurred.", ex);
|
|
}
|
|
}
|
|
}
|
|
|
|
public RootSomethingElse RootSomethingElse { get; set; }
|
|
|
|
public Something()
|
|
{
|
|
this.RootSomethingElse = new RootSomethingElse();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This is an object that is contained in the interface object.
|
|
/// </summary>
|
|
[JsonConverter(typeof(SomethingElseConverter))]
|
|
public class SomethingElse
|
|
{
|
|
public class SomethingElseConverter : JsonConverter
|
|
{
|
|
public override bool CanConvert(Type objectType)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
}
|
|
|
|
[Test]
|
|
public void DeserializeWrappingErrorsAndErrorHandling()
|
|
{
|
|
var serialiser = JsonSerializer.Create(new JsonSerializerSettings() { });
|
|
|
|
string foo = "{ something: { rootSomethingElse { somethingElse: 0 } } }";
|
|
var reader = new System.IO.StringReader(foo);
|
|
|
|
ExceptionAssert.Throws<Exception>(() => { serialiser.Deserialize(reader, typeof(Something)); }, "An error occurred.");
|
|
}
|
|
|
|
[Test]
|
|
public void SerializeWrappingErrorsAndErrorHandling()
|
|
{
|
|
var serialiser = JsonSerializer.Create(new JsonSerializerSettings() { });
|
|
|
|
Something s = new Something
|
|
{
|
|
RootSomethingElse = new RootSomethingElse
|
|
{
|
|
SomethingElse = new SomethingElse()
|
|
}
|
|
};
|
|
RootThing r = new RootThing
|
|
{
|
|
Something = s
|
|
};
|
|
|
|
var writer = new System.IO.StringWriter();
|
|
|
|
ExceptionAssert.Throws<Exception>(() => { serialiser.Serialize(writer, r); }, "An error occurred.");
|
|
}
|
|
|
|
[Test]
|
|
public void DeserializeRootConverter()
|
|
{
|
|
SomethingElse result = JsonConvert.DeserializeObject<SomethingElse>("{}", new JsonSerializerSettings
|
|
{
|
|
Error = (o, e) => { e.ErrorContext.Handled = true; }
|
|
});
|
|
|
|
Assert.IsNull(result);
|
|
}
|
|
|
|
[Test]
|
|
public void SerializeRootConverter()
|
|
{
|
|
string result = JsonConvert.SerializeObject(new SomethingElse(), new JsonSerializerSettings
|
|
{
|
|
Error = (o, e) => { e.ErrorContext.Handled = true; }
|
|
});
|
|
|
|
Assert.AreEqual(string.Empty, result);
|
|
}
|
|
|
|
[Test]
|
|
public void IntegerToLarge_ReadNextValue()
|
|
{
|
|
IList<string> errorMessages = new List<string>();
|
|
|
|
JsonReader reader = new JsonTextReader(new StringReader(@"{
|
|
""string1"": ""blah"",
|
|
""int1"": 2147483648,
|
|
""string2"": ""also blah"",
|
|
""int2"": 2147483648,
|
|
""string3"": ""more blah"",
|
|
""dateTime1"": ""200NOTDATE"",
|
|
""string4"": ""even more blah""
|
|
}"));
|
|
JsonSerializerSettings settings = new JsonSerializerSettings();
|
|
settings.Error = (sender, args) =>
|
|
{
|
|
errorMessages.Add(args.ErrorContext.Error.Message);
|
|
args.ErrorContext.Handled = true;
|
|
};
|
|
JsonSerializer serializer = JsonSerializer.Create(settings);
|
|
|
|
DataModel data = new DataModel();
|
|
serializer.Populate(reader, data);
|
|
|
|
Assert.AreEqual("blah", data.String1);
|
|
Assert.AreEqual(0, data.Int1);
|
|
Assert.AreEqual("also blah", data.String2);
|
|
Assert.AreEqual(0, data.Int2);
|
|
Assert.AreEqual("more blah", data.String3);
|
|
Assert.AreEqual(default(DateTime), data.DateTime1);
|
|
Assert.AreEqual("even more blah", data.String4);
|
|
|
|
//Assert.AreEqual(2, errorMessages.Count);
|
|
Assert.AreEqual("JSON integer 2147483648 is too large or small for an Int32. Path 'int1', line 3, position 20.", errorMessages[0]);
|
|
Assert.AreEqual("JSON integer 2147483648 is too large or small for an Int32. Path 'int2', line 5, position 20.", errorMessages[1]);
|
|
Assert.AreEqual("Could not convert string to DateTime: 200NOTDATE. Path 'dateTime1', line 7, position 27.", errorMessages[2]);
|
|
}
|
|
|
|
[Test]
|
|
public void HandleErrorInDictionaryObject()
|
|
{
|
|
var json = @"{
|
|
model1: { String1: 's1', Int1: 'x' },
|
|
model2: { String1: 's2', Int1: 2 }
|
|
}";
|
|
var dictionary = JsonConvert.DeserializeObject<TolerantDictionary<string, DataModel>>(json);
|
|
|
|
Assert.AreEqual(1, dictionary.Count);
|
|
Assert.IsTrue(dictionary.ContainsKey("model2"));
|
|
Assert.AreEqual("s2", dictionary["model2"].String1);
|
|
Assert.AreEqual(2, dictionary["model2"].Int1);
|
|
}
|
|
|
|
private class DataModel
|
|
{
|
|
public string String1 { get; set; }
|
|
public int Int1 { get; set; }
|
|
public string String2 { get; set; }
|
|
public int Int2 { get; set; }
|
|
public string String3 { get; set; }
|
|
public DateTime DateTime1 { get; set; }
|
|
public string String4 { get; set; }
|
|
}
|
|
}
|
|
|
|
internal interface IErrorPerson2
|
|
{
|
|
}
|
|
|
|
internal class ErrorPerson2 //:IPerson - oops! Forgot to implement the person interface
|
|
{
|
|
public string LastName { get; set; }
|
|
public string FirstName { get; set; }
|
|
}
|
|
|
|
public class ThrowingReader : TextReader
|
|
{
|
|
private int _position = 0;
|
|
private static string element = "{\"FirstName\":\"Din\",\"LastName\":\"Rav\",\"Item\":{\"ItemName\":\"temp\"}}";
|
|
private bool _firstRead = true;
|
|
private bool _readComma = false;
|
|
|
|
public ThrowingReader()
|
|
{
|
|
}
|
|
|
|
public override int Read(char[] buffer, int index, int count)
|
|
{
|
|
char[] temp = new char[buffer.Length];
|
|
int charsRead = 0;
|
|
if (_firstRead)
|
|
{
|
|
charsRead = new StringReader("[").Read(temp, index, count);
|
|
_firstRead = false;
|
|
}
|
|
else
|
|
{
|
|
if (_readComma)
|
|
{
|
|
charsRead = new StringReader(",").Read(temp, index, count);
|
|
_readComma = false;
|
|
}
|
|
else
|
|
{
|
|
charsRead = new StringReader(element).Read(temp, index, count);
|
|
_readComma = true;
|
|
}
|
|
}
|
|
|
|
_position += charsRead;
|
|
if (_position > 65536)
|
|
{
|
|
throw new Exception("too far");
|
|
}
|
|
Array.Copy(temp, index, buffer, index, charsRead);
|
|
return charsRead;
|
|
}
|
|
}
|
|
|
|
public class ErrorPerson
|
|
{
|
|
public string FirstName { get; set; }
|
|
public string LastName { get; set; }
|
|
public ErrorItem Item { get; set; }
|
|
}
|
|
|
|
public class ErrorItem
|
|
{
|
|
public string ItemName { get; set; }
|
|
}
|
|
|
|
[JsonObject]
|
|
public class MyTypeWithRequiredMembers
|
|
{
|
|
[JsonProperty(Required = Required.AllowNull)]
|
|
public string Required1;
|
|
|
|
[JsonProperty(Required = Required.AllowNull)]
|
|
public string Required2;
|
|
}
|
|
|
|
public class LogMessage
|
|
{
|
|
public string DeviceId { get; set; }
|
|
public IList<LogEvent> Events { get; set; }
|
|
}
|
|
|
|
public class LogEvent
|
|
{
|
|
public string Code { get; set; }
|
|
public int Priority { get; set; }
|
|
}
|
|
|
|
public class ErrorTestObject
|
|
{
|
|
[OnError]
|
|
internal void OnError(StreamingContext context, ErrorContext errorContext)
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A dictionary that ignores deserialization errors and excludes bad items
|
|
/// </summary>
|
|
public class TolerantDictionary<TKey, TValue> : Dictionary<TKey, TValue>
|
|
{
|
|
[OnError]
|
|
public void OnDeserializationError(StreamingContext streamingContext, ErrorContext errorContext)
|
|
{
|
|
errorContext.Handled = true;
|
|
}
|
|
}
|
|
}
|