如何反序列化 json,其中数据可以是对象或空数组,并且对象是 "Dictionary" 格式?

How to deserialize json where the data can be an object or an empty array and as an object is in a "Dictionary" format?

我正在接收 json 以下格式的数据。我无法控制 json 数据。

{
    "response": {
        "status": 1,
        "httpStatus": 200,
        "data": {
            "Countries": {
                "818": {
                    "id": "818",
                    "code": "EG",
                    "name": "Egypt"
                },
                "414": {
                    "id": "414",
                    "code": "KW",
                    "name": "Kuwait"
                },
                "682": {
                    "id": "682",
                    "code": "SA",
                    "name": "Saudi Arabia"
                },
                "784": {
                    "id": "784",
                    "code": "AE",
                    "name": "United Arab Emirates"
                }
            },
            "Regions": [],
            "Cities": [],
            "Exclude": []
        },
        "errors": [],
        "errorMessage": null
    }
}

我正在尝试反序列化数据以获取国家/地区代码。我使用字典来处理国家 ID 部分作为键,并使用 class 来处理其他值。

public class CountryResponseData
{
    public CountryData Data { get; set; }
}

public class CountryData
{
    public Dictionary<string, Country> Countries { get; set; }
}

public class Country
{
    public string Code { get; set; }
}

只要它们是数据中的国家/地区,这就可以正常工作,但一些数据采用以下格式。

{
    "response": {
        "status": 1,
        "httpStatus": 200,
        "data": {
            "Countries": [],
            "Regions": [],
            "Cities": [],
            "Exclude": []
        },
        "errors": [],
        "errorMessage": null
    }
}

因为在这种情况下国家是数组而不是对象,反序列化器不知道如何处理它。

我试过使用自定义 json 转换器,就像这些答案 ( and ) 中描述的那样,但它似乎没有任何作用。

如有任何帮助,我们将不胜感激。

我相信您所指的答案中的代码可以正常工作。我这样测试了你的案例:

public class CountryResponseData
{
    public CountryData Data { get; set; }
}

public class CountryData
{
    [JsonConverter(typeof(EmptyArrayOrDictionaryConverter))]
    public Dictionary<string, Country> Countries { get; set; }
}

public class Country
{
    public string Code { get; set; }
}

// this a modified version of this SO-answer: 
public class EmptyArrayOrDictionaryConverter : JsonConverter
{
    public override bool CanConvert(Type objectType) => objectType.IsAssignableFrom(typeof(Dictionary<string, object>));

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var token = JToken.Load(reader);
        return token.Type switch
        {
            JTokenType.Object => token.ToObject(objectType, serializer), JTokenType.Array when !token.HasValues => Activator.CreateInstance(objectType),
            _ => throw new JsonSerializationException("Object or empty array expected")
        };
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => serializer.Serialize(writer, value);
}

public class MyJsonTestClass
{
    private const string JsonWithCountries = @"{
            ""data"": {
                ""Countries"": {
                    ""818"": {
                        ""id"": ""818"",
                        ""code"": ""EG"",
                        ""name"": ""Egypt""
                    },
                    ""414"": {
                        ""id"": ""414"",
                        ""code"": ""KW"",
                        ""name"": ""Kuwait""
                    },
                    ""682"": {
                        ""id"": ""682"",
                        ""code"": ""SA"",
                        ""name"": ""Saudi Arabia""
                    },
                    ""784"": {
                        ""id"": ""784"",
                        ""code"": ""AE"",
                        ""name"": ""United Arab Emirates""
                    }
                },
                ""Regions"": [],
                ""Cities"": [],
                ""Exclude"": []
            }
        }";

    private const string JsonWithoutCountries = @"{
            ""data"": {
                ""Countries"": [],
                ""Regions"": [],
                ""Cities"": [],
                ""Exclude"": []
            }
        }";

    [Test]
    public void MyJsonTest()
    {
        // START tests using NewtonSoft
        
        var result = JsonConvert.DeserializeObject<CountryResponseData>(JsonWithCountries);
        Assert.NotNull(result?.Data);

        var result2 = JsonConvert.DeserializeObject<CountryResponseData>(JsonWithoutCountries);
        Assert.NotNull(result2?.Data);
    }
}

最简单的方法是将 json 字符串中的 Country[] 替换为 null。相同的代码适用于 jsons

var jsonOriginal = "{\"response\":{\"status\":1,\"httpStatus\":200,\"data\":{\"Countries\":[],\"Regions\":[],\"Cities\":[],\"Exclude\":[]},\"errors\":[],\"errorMessage\":null}}";

var json=jsonOriginal.Replace("\"Countries\":[],","\"Countries\":null,");
// you can add the same for Regions and Cities too if one day they will be changed to dictionary type

你会得到这个输出

{"response":{"status":1,"httpStatus":200,"data":{"Countries":null,"Regions":[],"Cities":[],"Exclude":[]},"errors":[],"errorMessage":null}}

但如果由于某些原因它不适合你,那就有一个困难的方法:

此代码在 visual studio 中进行了测试,适用于我

var json = "{\"response\":{\"status\":1,\"httpStatus\":200,\"data\":{\"Countries\":[],\"Regions\":[],\"Cities\":[],\"Exclude\":[]},\"errors\":[],\"errorMessage\":null}}";

    JsonObj jsonObj = null;
    JsonObj2 jsonObj2 = null;

    try
    {
        jsonObj = JsonConvert.DeserializeObject<JsonObj>(json);
    }
    catch (Exception ex)
    {
        if (ex.Source == "Newtonsoft.Json")
        {

            try
            {
                jsonObj2 = JsonConvert.DeserializeObject<JsonObj2>(json);

            }

            catch (Exception ex1)
            {

                throw;
            }
        }

    }

    if (jsonObj != null) ....
    else ....

public class Country
{
    public string id { get; set; }
    public string code { get; set; }
    public string name { get; set; }
}


public class Data
{
    public Dictionary<string, Country> Countries { get; set; }
    public List<object> Regions { get; set; }
    public List<object> Cities { get; set; }
    public List<object> Exclude { get; set; }
}
public class Data2
{
    public List<object> Countries { get; set; }
    public List<object> Regions { get; set; }
    public List<object> Cities { get; set; }
    public List<object> Exclude { get; set; }
}


public class Response
{
    public int status { get; set; }
    public int httpStatus { get; set; }
    public Data data { get; set; }
    public List<object> errors { get; set; }
    public object errorMessage { get; set; }
}
public class Response2
{
    public int status { get; set; }
    public int httpStatus { get; set; }
    public Data2 data { get; set; }
    public List<object> errors { get; set; }
    public object errorMessage { get; set; }
}

public class JsonObj
{
    public Response response { get; set; }
}


public class JsonObj2
{
    public Response2 response { get; set; }
}