仅当数组为空时,JSON反序列化才会失败 [英] JSON deserialization fails only when array is empty

查看:179
本文介绍了仅当数组为空时,JSON反序列化才会失败的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在某些情况下,当我收到其array属性之一为空的JSON时,反序列化失败,并抛出以下异常:

On some occasions when I receive JSON whose one of the array property is empty the deserialization fails, throwing the following exception :

无法将当前JSON对象(例如{"name":"value"})反序列化为类型'SonicApi.ClickMark []',因为该类型需要JSON数组(例如[1,2,3])正确反序列化.

要解决此错误,可以将JSON更改为JSON数组(例如[1,2,3]),也可以更改反序列化类型,使其成为普通的.NET类型(例如,不是整数之类的原始类型) ,而不是可以从JSON对象反序列化的集合类型(如数组或List).还可以将JsonObjectAttribute添加到类型中,以强制其从JSON对象反序列化.

路径auftakt_result.click_marks,第1行,位置121.

尝试使用以下代码忽略空值没有帮助:

Trying to ignore null values with the following code didn't help:

var jsonSerializerSettings = new JsonSerializerSettings();
jsonSerializerSettings.NullValueHandling = NullValueHandling.Ignore;

以下是产生错误的JSON示例:

Here is an example of JSON that produces the error :

{
    "status": {
        "code": 200
    },
    "auftakt_result": {
        "clicks_per_bar": 0,
        "overall_tempo": 0,
        "overall_tempo_straight": 0,
        "click_marks": {}
    }
}

这是JSON的示例,其数组不为空并且不会产生任何错误:

Here is an example of JSON whose array is not empty and does not produce any error:

{
    "status": {
        "code": 200
    },
    "auftakt_result": {
        "clicks_per_bar": 8,
        "overall_tempo": 144.886978,
        "overall_tempo_straight": 144.90889,
        "click_marks": [
            {
                "index": 0,
                "bpm": 144.226624,
                "probability": 0.828170717,
                "time": 0.0787981859,
                "downbeat": "false"
            },
            {
                "index": 1,
                "bpm": 144.226517,
                "probability": 0.831781149,
                "time": 0.286802721,
                "downbeat": "false"
            },
etc ...

以下是代表上述对象的C#类型:

Here are the C# types representing above objects:

public sealed class AnalyzeTempoResponse
{
    [JsonProperty("auftakt_result")]
    public AuftaktResult AuftaktResult { get; set; }

    [JsonProperty("status")]
    public Status Status { get; set; }
}

public sealed class Status
{
    [JsonProperty("code")]
    public int Code { get; set; }
}

public sealed class AuftaktResult
{
    [JsonProperty("clicks_per_bar")]
    public int ClicksPerBar { get; set; }

    [JsonProperty("overall_tempo")]
    public double OverallTempo { get; set; }

    [JsonProperty("overall_tempo_straight")]
    public double OverallTempoStraight { get; set; }

    [JsonProperty("click_marks")]
    public ClickMark[] ClickMarks { get; set; }
}

public sealed class ClickMark
{
    [JsonProperty("index")]
    public int Index { get; set; }

    [JsonProperty("bpm")]
    public double Bpm { get; set; }

    [JsonProperty("probability")]
    public double Probability { get; set; }

    [JsonProperty("time")]
    public double Time { get; set; }

    [JsonProperty("downbeat")]
    public string Downbeat { get; set; }

}

如何反序列化click_marks内容为null的响应?

How can I deserialize responses whose click_marks content is null ?

如果那很重要,我正在使用Newtonsoft.Json的最新版本:v6.0

If that matters, I am using the latest version of Newtonsoft.Json : v6.0

编辑

根据@khellang的回答,这是采用的解决方案:

Here is the adopted solution according @khellang's answer :

public class ClickMarkArrayConverter : CustomCreationConverter<ClickMark[]>
{
    public override ClickMark[] Create(Type objectType)
    {
        return new ClickMark[] {};
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
        JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.StartArray)
        {
            return serializer.Deserialize(reader, objectType);
        }

        if (reader.TokenType == JsonToken.StartObject)
        {
            serializer.Deserialize(reader); // NOTE : value must be consumed otherwise an exception will be thrown
            return null;
        }

        throw new NotSupportedException("Should not occur, check JSON for a new type of malformed syntax");
    }
}

推荐答案

null值无关(您的JSON示例都没有 any 属性的null值).您正在尝试将JSON对象反序列化为ClickMark[]:

It has nothing to do with null values (none of your JSON examples have null values for any property). You're trying to deserialize a JSON object into a ClickMark[]:

"click_marks": {} // <-- This is an object, not null, not an array.

之所以可以用于第二个示例,是因为click_marks属性实际上是 一个ClickMark对象的数组:

The reason it works for example number two is that the click_marks property actually is an array of ClickMark objects:

"click_marks": [{...}, {...}, {...}] // <-- This is an array with three objects.

数据来自哪里?您需要确保click_marks属性是数组或对象,而不是两者,并且您键入的C#对象ClickMarks与JSON属性的类型"匹配.

Where does the data come from? You need to make sure that the click_marks property is either an array or an object, not both, and that your typed C# object ClickMarks matches the "type" of the JSON property.

如果您无法控制数据,例如如果它来自第三方,我建议您编写一个自定义JsonConverter,您可以将其应用于该单个属性:

If you have no control over the data, e.g. if it comes from a 3rd party, I'd suggest you write a custom JsonConverter that you can apply to that single property:

public class ObjectToArrayConverter<T> : CustomCreationConverter<T[]>
{
    public override T[] Create(Type objectType)
    {
        return new T[0];
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.StartArray)
        {
            return serializer.Deserialize(reader, objectType);
        }
        else
        {
            return new T[] { serializer.Deserialize<T>(reader) };
        }
    }
}

并像这样应用它:

public sealed class AuftaktResult
{
    // ...

    [JsonProperty("click_marks")]
    [JsonConverter(typeof(ObjectToArrayConverter<ClickMark>))]
    public ClickMark[] ClickMarks { get; set; }
}

这将检查该值是否为单个对象,并将其包装在数组中,以便与您的C#POCO属性匹配:)

This will check if the value is a single object and wrap it in an array so it will match your C# POCO property :)

这篇关于仅当数组为空时,JSON反序列化才会失败的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆