Added logging of json snippets on json deserialization errors.
This commit is contained in:
parent
8b8bfb9bf0
commit
0cce6b74f9
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Reflection;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Converters;
|
using Newtonsoft.Json.Converters;
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
|
@ -14,13 +15,13 @@ namespace NzbDrone.Common.Serializer
|
||||||
static Json()
|
static Json()
|
||||||
{
|
{
|
||||||
SerializerSetting = new JsonSerializerSettings
|
SerializerSetting = new JsonSerializerSettings
|
||||||
{
|
{
|
||||||
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
|
DateTimeZoneHandling = DateTimeZoneHandling.Utc,
|
||||||
NullValueHandling = NullValueHandling.Ignore,
|
NullValueHandling = NullValueHandling.Ignore,
|
||||||
Formatting = Formatting.Indented,
|
Formatting = Formatting.Indented,
|
||||||
DefaultValueHandling = DefaultValueHandling.Include,
|
DefaultValueHandling = DefaultValueHandling.Include,
|
||||||
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
ContractResolver = new CamelCasePropertyNamesContractResolver()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
SerializerSetting.Converters.Add(new StringEnumConverter { CamelCaseText = true });
|
SerializerSetting.Converters.Add(new StringEnumConverter { CamelCaseText = true });
|
||||||
|
@ -34,12 +35,61 @@ namespace NzbDrone.Common.Serializer
|
||||||
|
|
||||||
public static T Deserialize<T>(string json) where T : new()
|
public static T Deserialize<T>(string json) where T : new()
|
||||||
{
|
{
|
||||||
return JsonConvert.DeserializeObject<T>(json, SerializerSetting);
|
try
|
||||||
|
{
|
||||||
|
return JsonConvert.DeserializeObject<T>(json, SerializerSetting);
|
||||||
|
}
|
||||||
|
catch (JsonReaderException ex)
|
||||||
|
{
|
||||||
|
throw DetailedJsonReaderException(ex, json);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static object Deserialize(string json, Type type)
|
public static object Deserialize(string json, Type type)
|
||||||
{
|
{
|
||||||
return JsonConvert.DeserializeObject(json, type, SerializerSetting);
|
try
|
||||||
|
{
|
||||||
|
return JsonConvert.DeserializeObject(json, type, SerializerSetting);
|
||||||
|
}
|
||||||
|
catch (JsonReaderException ex)
|
||||||
|
{
|
||||||
|
throw DetailedJsonReaderException(ex, json);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JsonReaderException DetailedJsonReaderException(JsonReaderException ex, string json)
|
||||||
|
{
|
||||||
|
var lineNumber = ex.LineNumber == 0 ? 0 : (ex.LineNumber - 1);
|
||||||
|
var linePosition = ex.LinePosition;
|
||||||
|
|
||||||
|
var lines = json.Split('\n');
|
||||||
|
if (lineNumber >= 0 && lineNumber < lines.Length &&
|
||||||
|
linePosition >= 0 && linePosition < lines[lineNumber].Length)
|
||||||
|
{
|
||||||
|
var line = lines[lineNumber];
|
||||||
|
var start = Math.Max(0, linePosition - 20);
|
||||||
|
var end = Math.Min(line.Length, linePosition + 20);
|
||||||
|
|
||||||
|
var snippetBefore = line.Substring(start, linePosition - start);
|
||||||
|
var snippetAfter = line.Substring(linePosition, end - linePosition);
|
||||||
|
var message = ex.Message + " (Json snippet '" + snippetBefore + "<--error-->" + snippetAfter + "')";
|
||||||
|
|
||||||
|
// Not risking updating JSON.net from 9.x to 10.x just to get this as public ctor.
|
||||||
|
var ctor = typeof(JsonReaderException).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(Exception), typeof(string), typeof(int), typeof(int) }, null);
|
||||||
|
if (ctor != null)
|
||||||
|
{
|
||||||
|
return (JsonReaderException)ctor.Invoke(new object[] { message, ex, ex.Path, ex.LineNumber, linePosition });
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON.net 10.x ctor in case we update later.
|
||||||
|
ctor = typeof(JsonReaderException).GetConstructor(BindingFlags.Public | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(string), typeof(int), typeof(int), typeof(Exception) }, null);
|
||||||
|
if (ctor != null)
|
||||||
|
{
|
||||||
|
return (JsonReaderException)ctor.Invoke(new object[] { message, ex.Path, ex.LineNumber, linePosition, ex });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryDeserialize<T>(string json, out T result) where T : new()
|
public static bool TryDeserialize<T>(string json, out T result) where T : new()
|
||||||
|
@ -78,4 +128,4 @@ namespace NzbDrone.Common.Serializer
|
||||||
Serialize(model, new StreamWriter(outputStream));
|
Serialize(model, new StreamWriter(outputStream));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using System;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Common.Serializer;
|
using NzbDrone.Common.Serializer;
|
||||||
|
@ -24,5 +25,31 @@ namespace NzbDrone.Libraries.Test.JsonTests
|
||||||
|
|
||||||
result.ShouldBeEquivalentTo(quality, o => o.IncludingAllRuntimeProperties());
|
result.ShouldBeEquivalentTo(quality, o => o.IncludingAllRuntimeProperties());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_log_start_snippet_on_failure()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Json.Deserialize<object>("asdfl kasjd fsdfs derers");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ex.Message.Should().Contain("snippet '<--error-->asdfl kasjd fsdfs de'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_log_line_snippet_on_failure()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Json.Deserialize<object>("{ \"a\": \r\n\"b\",\r\n \"b\": \"c\", asdfl kasjd fsdfs derers vsdfsdf");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ex.Message.Should().Contain("snippet ' \"b\": \"c\", asdfl <--error-->kasjd fsdfs derers v'");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue