JsonSerializationException解析

时间:2018-11-03 11:23:25

标签: c# json.net

我正在尝试使用一些优美的异常处理来包装我的应用程序。当我的应用程序无法反序列化JSON时,我得到一个JsonSerializationException,其消息如下所示:

  

将值“ [IncorrectDataType]”转换为类型“ ApexFramework.Enums + DataTypes”时出错。路径“布局[0] .ElementContainer [0] .ContainerDatatype”,第12行,位置58。

通过此异常消息,我有兴趣以干净的方式捕获和呈现以下内容:

  • 我需要Layout []中元素容器的索引(在上面的示例中为0)
  • 我需要ElementContainer []中ContainerDataType的索引(在上面的示例中为0)

我浏览了

JsonSerializationException

对象,我没有什么可以轻松抓住的,所以:

任何人都可以确认没有办法与JsonSerializationException一起以干净的方式获取我需要的信息吗?

如果没有,谁能帮助我想出最优雅,最有效的方法来从异常堆栈中获取我需要的信息?

2 个答案:

答案 0 :(得分:0)

我不确定这是否是您所需要的,但是您可以使用Json.NET Schema来验证json字符串并获得更多体面的验证错误消息。

您可以使用DataAnnotations创建json模式(字符串)或验证对象。

您可以在Nuget中找到这两个软件包:

  

安装软件包Newtonsoft.Json.Schema -Version 3.0.10

     

安装包System.ComponentModel.Annotations-版本4.5.0

请考虑以下模型:

public class Album
{
    [DisplayName("Album Id")]
    [Description("Album Id must be a positive integer")]
    [Range(1, Int32.MaxValue)]
    public int AlbumId { get; set; }

    [DisplayName("Artist Id")]
    [Description("Artist Id must be a positive integer")]
    [Range(1, Int32.MaxValue)]
    public int ArtistId { get; set; }

    [DisplayName("Title")]
    [Description("An Album Title is required")]
    [Required()]
    [StringLength(160)]
    public string Title { get; set; }

    [DisplayName("Album Price")]
    [Description("Album Price must be between 0.01 and 100.00")]
    [Range(0.01, 100.00)]
    public decimal Price { get; set; }
}

下面的类将包含有关验证错误的信息:

public class ValidationError
{
    public string Property { get; set; }
    public string Message { get; set; }
    public object CurrentValue { get; set; }
}

反序列化和验证json字符串:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.IO;

using Newtonsoft.Json;
using Newtonsoft.Json.Schema;
using Newtonsoft.Json.Schema.Generation;

// json that contains properties with invalid values
string json = @"{
  ""AlbumId"": -10,
  ""ArtistId"": 1,
  ""Title"": null,
  ""Price"": 200.0
}";


JsonTextReader reader = new JsonTextReader(new StringReader(json));
JSchemaValidatingReader validatingReader = new JSchemaValidatingReader(reader)
{
    Schema = new JSchemaGenerator().Generate(typeof(Album))
};

var errors = new List<ValidationError>();
validatingReader.ValidationEventHandler += (o, a) => errors.Add(new ValidationError
{
    Property = a.ValidationError.Schema.Title,
    Message = a.ValidationError.Schema.Description,
    CurrentValue = a.ValidationError.Value
});

JsonSerializer serializer = new JsonSerializer();
Album album = serializer.Deserialize<Album>(validatingReader);

foreach (ValidationError error in errors)
{
    Debug.WriteLine($"Property: {error.Property}, Message: {error.Message}");
}

输出:

  

属性:相册ID,消息:相册ID必须为正整数

     

属性:标题,消息:需要相册标题

     

属性:相册价格,消息:相册价格必须在0.01到100.00之间

答案 1 :(得分:0)

您可以使用Json.NET的serialization error event handling功能以更有用的格式获取有关异常的信息。

反序列化期间发生异常时,Json.NET会在对象层次结构的每个级别上捕获并重新抛出该异常,从而使每个对象都有机会使用OnError方法处理该异常。当您不想处理异常时,可以利用此优势,通过传递给ErrorEventArgs事件处理程序的JsonSerializerSettings.Error中提供的信息,记录有关异常发生的位置的详细信息。例如,以下代码捕获并报告发生异常的path,引起异常的member,并在遇到错误时对objects的堆栈进行反序列化:< / p>

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;
    }
}   

然后,如果我尝试将JSON反序列化为您的问题所隐含的数据模型,则会收到以下错误消息:

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}

如您所见,您所需的信息更加清晰可用,因此您不再需要解析异常消息。您仍然需要解析异常路径以提取索引,但是路径语法是标准的JSONPath syntax,因此定义明确。您可以在任何先前存在的JSONPath解析器(包括Json.NET自己的JPath)上对路径解析代码进行建模。

注意:

  • 我不特别建议处理该异常并继续,因为Newtonsoft报告Json.NET中的错误处理"very flaky"

.net小提琴here的工作示例。