我试图修复我的SendGridPlus库来处理SendGrid事件,但我有一些麻烦的API中类别的不一致的处理。
在下面的示例负载取自SendGrid API reference,你会注意到每个项目的category属性可以是单个字符串或字符串数组。
[ { "email": "john.doe@sendgrid.com","timestamp": 1337966815,"category": [ "newuser","transactional" ],"event": "open" },{ "email": "jane.doe@sendgrid.com","category": "olduser","event": "open" } ]
看来我的选择使JSON.NET像这样是固定字符串之前,或配置JSON.NET接受不正确的数据。我宁愿不做任何字符串解析,如果我可以逃脱它。
有没有任何其他方式,我可以使用Json.Net处理这个?
处理这种情况的最好方法是使用自定义
JsonConverter
。
在我们进入转换器之前,我们需要定义一个类来反序列化数据。对于可以在单个项目和数组之间变化的Categories属性,将其定义为List< string>并使用[JsonConverter]属性标记它,以便JSON.Net将知道使用该属性的自定义转换器。我还建议使用[JsonProperty]属性,以便可以给成员属性赋予有意义的名称,而与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; } }
这里是如何实现转换器。注意我已经使转换器通用,以便它可以用于字符串或其他类型的对象所需要的。
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(); } }
这里是一个简短的程序演示转换器与您的示例数据:
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"",""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(); } } }
最后,这里是上面的输出:
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()将永远不会被调用。)
public override void WriteJson(JsonWriter writer,JsonSerializer serializer) { List<T> list = (List<T>)value; if (list.Count == 1) { value = list[0]; } serializer.Serialize(writer,value); }