如何使用JSON.net处理同一个属性的单个项目和数组 [英] How to handle both a single item and an array for the same property using 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屋!