如何使用 JSON.net 处理同一属性的单个项目和数组 [英] How to handle both a single item and an array for the same property using JSON.net

查看:14
本文介绍了如何使用 JSON.net 处理同一属性的单个项目和数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试修复我的 SendGridPlus 库以处理 SendGrid 事件,但我在 API 中对类别的处理不一致时遇到了一些问题.

I'm trying to fix my SendGridPlus library to deal with SendGrid events, but I'm having some trouble with the inconsistent treatment of categories in the API.

在以下示例负载中,取自 SendGrid API 参考,您会注意到每个项目的 category 属性可以是单个字符串或字符串数​​组.

In the following example payload taken from the SendGrid API reference, you'll notice that the category property for each item can either be a single string or an array of strings.

[
  {
    "email": "john.doe@sendgrid.com",
    "timestamp": 1337966815,
    "category": [
      "newuser",
      "transactional"
    ],
    "event": "open"
  },
  {
    "email": "jane.doe@sendgrid.com",
    "timestamp": 1337966815,
    "category": "olduser",
    "event": "open"
  }
]

似乎我的选择是让 JSON.NET 像这样在输入之前修复字符串,或者配置 JSON.NET 以接受不正确的数据.如果可以的话,我宁愿不做任何字符串解析.

It seems my options to make JSON.NET like this are fixing the string before it comes in, or configuring JSON.NET to accept the incorrect data. I'd rather not do any string parsing if I can get away with it.

有没有其他方法可以使用 Json.Net 来处理这个问题?

Is there any other way I can handle this using Json.Net?

推荐答案

处理这种情况的最佳方法是使用自定义 JsonConverter.

The best way to handle this situation is to use a custom JsonConverter.

在我们使用转换器之前,我们需要定义一个类来反序列化数据.对于可以在单个项目和数组之间变化的 Categories 属性,将其定义为 List 并用 [JsonConverter] 标记它code> 属性,以便 JSON.Net 知道使用该属性的自定义转换器.我还建议使用 [JsonProperty] 属性,以便可以为成员属性赋予有意义的名称,而与 JSON 中定义的内容无关.

Before we get to the converter, we'll need to define a class to deserialize the data into. For the Categories property that can vary between a single item and an array, define it as a List<string> and mark it with a [JsonConverter] attribute so that JSON.Net will know to use the custom converter for that property. I would also recommend using [JsonProperty] attributes so that the member properties can be given meaningful names independent of what is defined in the JSON.

class Item
{
    [JsonProperty("email")]
    public string Email { get; set; }

    [JsonProperty("timestamp")]
    public int Timestamp { get; set; }

    [JsonProperty("event")]
    public string Event { get; set; }

    [JsonProperty("category")]
    [JsonConverter(typeof(SingleOrArrayConverter<string>))]
    public List<string> Categories { get; set; }
}

这是我将如何实现转换器.请注意,我已将转换器设为通用,以便它可以根据需要与字符串或其他类型的对象一起使用.

Here is how I would implement the converter. Notice I've made the converter generic so that it can be used with strings or other types of objects as needed.

class SingleOrArrayConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(List<T>));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken token = JToken.Load(reader);
        if (token.Type == JTokenType.Array)
        {
            return token.ToObject<List<T>>();
        }
        return new List<T> { token.ToObject<T>() };
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

这是一个简短的程序,演示了转换器与您的示例数据的作用:

Here is an short program demonstrating the converter in action with your sample data:

class Program
{
    static void Main(string[] args)
    {
        string json = @"
        [
          {
            ""email"": ""john.doe@sendgrid.com"",
            ""timestamp"": 1337966815,
            ""category"": [
              ""newuser"",
              ""transactional""
            ],
            ""event"": ""open""
          },
          {
            ""email"": ""jane.doe@sendgrid.com"",
            ""timestamp"": 1337966815,
            ""category"": ""olduser"",
            ""event"": ""open""
          }
        ]";

        List<Item> list = JsonConvert.DeserializeObject<List<Item>>(json);

        foreach (Item obj in list)
        {
            Console.WriteLine("email: " + obj.Email);
            Console.WriteLine("timestamp: " + obj.Timestamp);
            Console.WriteLine("event: " + obj.Event);
            Console.WriteLine("categories: " + string.Join(", ", obj.Categories));
            Console.WriteLine();
        }
    }
}

最后,这是上面的输出:

And finally, here is the output of the above:

email: john.doe@sendgrid.com
timestamp: 1337966815
event: open
categories: newuser, transactional

email: jane.doe@sendgrid.com
timestamp: 1337966815
event: open
categories: olduser

小提琴:https://dotnetfiddle.net/lERrmu

编辑

如果你需要另一种方式,即序列化,同时保持相同的格式,你可以实现转换器的WriteJson()方法,如下所示.(请务必移除 CanWrite 覆盖或将其更改为返回 true,否则永远不会调用 WriteJson().)

If you need to go the other way, i.e. serialize, while keeping the same format, you can implement the WriteJson() method of the converter as shown below. (Be sure to remove the CanWrite override or change it to return true, or else WriteJson() will never be called.)

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        List<T> list = (List<T>)value;
        if (list.Count == 1)
        {
            value = list[0];
        }
        serializer.Serialize(writer, value);
    }

小提琴:https://dotnetfiddle.net/XG3eRy

这篇关于如何使用 JSON.net 处理同一属性的单个项目和数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆