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

查看:703
本文介绍了如何使用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?

推荐答案

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

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

在进入转换器之前,我们需要定义一个类以将数据反序列化到其中.对于可以在单个项目和数组之间变化的Categories属性,请将其定义为List<string>并用[JsonConverter]属性进行标记,以便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天全站免登陆