当值可以是对象或空数组时反序列化 JSON

Deserialize JSON when a value can be an object or an empty array(当值可以是对象或空数组时反序列化 JSON)
本文介绍了当值可以是对象或空数组时反序列化 JSON的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 VK API.有时服务器可以返回空数组而不是对象,例如:

I`m working with VK API. Sometimes server can return empty array instead of object, for example:

personal: [] //when it is empty

personal: {
religion: 'Нет',
smoking: 1,
alcohol: 4
} //when not empty.

我正在使用 JsonConvert.DeserializeObject 反序列化大部分 json,而这部分 json 使用

I`m deserializing most of json with JsonConvert.DeserializeObject, and this part of json with

MainObject = ((MainObject["response"].GetObject())["user"].GetObject())["personal"].GetObject();
try
{
Convert.ToByte(MainObject["political"].GetNumber();
} 
catch {}

但是当它处理大量异常时,它会使应用程序运行缓慢.刚才我意识到这里还有一些字段可能在空时返回数组.我只是不知道如何快速清晰地完成它.有什么建议吗?

But it makes app works slowly when it`s handling a lot of exeptions. And just now i realised that here are some more fields that might return array when empty. I just have no ideas how to make it fastly and clearly. Any suggestions?

我的反序列化类(字段为空时不起作用):

My deserializing class (doen`t work when field is empty):

     public class User
            {
//some other fields...
                public Personal personal { get; set; }
//some other fields...
             }
    public class Personal
            {
                public byte political { get; set; }
                public string[] langs { get; set; }
                public string religion { get; set; }
                public string inspired_by { get; set; }
                public byte people_main { get; set; }
                public byte life_main { get; set; }
                public byte smoking { get; set; }
                public byte alcohol { get; set; }
            }

另一个想法(不为空时不起作用):

Another idea (doesn`t work when not empty):

public List<Personal> personal { get; set; }

推荐答案

你可以制作一个 JsonConverter 如下所示,它查找指定类型的对象或空数组.如果是对象,它会反序列化该对象.如果是空数组,则返回null:

You could make a JsonConverter like the following, that looks for either an object of a specified type, or an empty array. If an object, it deserializes that object. If an empty array, it returns null:

public class JsonSingleOrEmptyArrayConverter<T> : JsonConverter where T : class
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(T).IsAssignableFrom(objectType);
    }

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var contract = serializer.ContractResolver.ResolveContract(objectType);
        if (!(contract is Newtonsoft.Json.Serialization.JsonObjectContract || contract is Newtonsoft.Json.Serialization.JsonDictionaryContract))
        {
            throw new JsonSerializationException(string.Format("Unsupported objectType {0} at {1}.", objectType, reader.Path));
        }

        switch (reader.SkipComments().TokenType)
        {
            case JsonToken.StartArray:
                {
                    int count = 0;
                    while (reader.Read())
                    {
                        switch (reader.TokenType)
                        {
                            case JsonToken.Comment:
                                break;
                            case JsonToken.EndArray:
                                return existingValue;
                            default:
                                {
                                    count++;
                                    if (count > 1)
                                        throw new JsonSerializationException(string.Format("Too many objects at path {0}.", reader.Path));
                                    existingValue = existingValue ?? contract.DefaultCreator();
                                    serializer.Populate(reader, existingValue);
                                }
                                break;
                        }
                    }
                    // Should not come here.
                    throw new JsonSerializationException(string.Format("Unclosed array at path {0}.", reader.Path));
                }

            case JsonToken.Null:
                return null;

            case JsonToken.StartObject:
                existingValue = existingValue ?? contract.DefaultCreator();
                serializer.Populate(reader, existingValue);
                return existingValue;

            default:
                throw new InvalidOperationException("Unexpected token type " + reader.TokenType.ToString());
        }
    }

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

public static partial class JsonExtensions
{
    public static JsonReader SkipComments(this JsonReader reader)
    {
        while (reader.TokenType == JsonToken.Comment && reader.Read())
            ;
        return reader;
    }
}

然后像这样使用它:

public class User
{
    //some other fields...
    [JsonConverter(typeof(JsonSingleOrEmptyArrayConverter<Personal>))]
    public Personal personal { get; set; }
    //some other fields...
}

您现在应该能够将用户反序列化到您的 User 类中.

You should now be able to deserialize a user into your User class.

注意事项:

  • 转换器可以通过属性或在 中应用JsonSerializerSettings.Converters.

转换器不是为处理字符串等简单类型而设计的,它是为映射到 JSON 对象的类而设计的.那是因为它使用了 JsonSerializer.Populate() 避免读取过程中的无限递归.

The converter isn't designed to work with simple types such as strings, it's designed for classes that map to a JSON object. That's because it uses JsonSerializer.Populate() to avoid an infinite recursion during reading.

工作示例 .Net fiddles 这里和 这里.

Working sample .Net fiddles here and here.

这篇关于当值可以是对象或空数组时反序列化 JSON的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!

相关文档推荐

DispatcherQueue null when trying to update Ui property in ViewModel(尝试更新ViewModel中的Ui属性时DispatcherQueue为空)
Drawing over all windows on multiple monitors(在多个监视器上绘制所有窗口)
Programmatically show the desktop(以编程方式显示桌面)
c# Generic Setlt;Tgt; implementation to access objects by type(按类型访问对象的C#泛型集实现)
InvalidOperationException When using Context Injection in ASP.Net Core(在ASP.NET核心中使用上下文注入时发生InvalidOperationException)
LINQ many-to-many relationship, how to write a correct WHERE clause?(LINQ多对多关系,如何写一个正确的WHERE子句?)