I'm trying to wrap my application with some graceful exception handling. When my app fails to deserialize JSON, I get a JsonSerializationException that has a Message that looks like this:
Error converting value "[IncorrectDataType]" to type 'ApexFramework.Enums+DataTypes'. Path 'Layout[0].ElementContainer[0].ContainerDatatype', line 12, position 58.
From this exception message, I am interested in capturing and presenting in a clean manner the following:
I looked through the
JsonSerializationException
object and there is nothing that I grab with ease, so:
Can anyone confirm that there is no way to work with the JsonSerializationException to get the info I need in a clean manner?
If not, can anyone help me come up with the most elegant and efficient way to grab the info I need from the exception stack?
You can use Json.NET's serialization error event handling functionality to get information about the exception in a somewhat more useful format.
When an exception during deserialization occurs, Json.NET catches and re-throws it at each level in the object hierarchy, giving each object an opportunity to handle the exception using an OnError method. While you don't want to handle the exception, you can take advantage of this to record details about where the exception occurred via the information provided in the ErrorEventArgs passed to the JsonSerializerSettings.Error event handler. For instance, the following code captures and reports the path at which the exception occurred, the member causing the exception, and the stack of objects being deserialized at when the error was encountered:
static bool TryDeserialize<TRootObject>(string jsonString, out TRootObject root)
{
var errorStack = new Stack<Newtonsoft.Json.Serialization.ErrorEventArgs>();
var settings = new JsonSerializerSettings
{
Converters = { new StringEnumConverter() },
Error = (o, e) => errorStack.Push(e)
};
try
{
root = JsonConvert.DeserializeObject<TRootObject>(jsonString, settings);
return true;
}
catch (JsonException ex)
{
var last = errorStack.Last();
var member = last.ErrorContext.Member;
var path = last.ErrorContext.Path;
var objectsStack = String.Join(", ", errorStack
.Where(e => e.CurrentObject != null)
.Select(e => e.CurrentObject.ToString()));
Console.WriteLine("Exception parsing JSON: ");
Console.WriteLine(ex.Message);
Console.WriteLine("Error context details: ");
Console.WriteLine(" Path: {0}\n Member: {1}\n Object stack = {{{2}}}",
path, member, objectsStack);
root = default(TRootObject);
return false;
}
}
Then, if I attempt to deserialize JSON into the data model implied by your question, I get the following error messages:
Exception parsing JSON:
Error converting value "[IncorrectDataType]" to type 'DataTypes'. Path 'Layout[0].ElementContainer[0].ContainerDatatype', line 6, position 56.
Error context details:
Path: Layout[0].ElementContainer[0].ContainerDatatype
Member: ContainerDatatype
Object stack = {RootObject, System.Collections.Generic.List`1[Layout], Layout, System.Collections.Generic.List`1[ElementContainer], ElementContainer}
As you can see, the information you need is more cleanly available, so that you will no longer need to parse the exception message. You will still need to parse the exception path to extract the indices, however the path syntax is standard JSONPath syntax and so is well-defined. You could model your path parsing code on any pre-existing JSONPath parser including Json.NET's own JPath.
Notes:
Sample working .Net fiddle here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With