首页 > 解决方案 > 反序列化模式中带有尴尬附加属性的 JSON 数组

问题描述

我在这里有一个与这个问题非常相似的问题,除了我的应用程序是用 C# 编写的,不幸的是我无法弄清楚如何转换解决方案。我正在尝试反序列化如下所示的 JSON 结果:

"error":[],
"result":
     {
        "MANAEUR":[
                [1619042400,"1.11200","1.13488","1.08341","1.10077","1.09896","58878.56534370",137],
                [1619046000,"1.09767","1.12276","1.08490","1.11097","1.10456","25343.25910419",77],

            ],
        
        "last":1619118000
    }

我使用以下类:

public class ResponseBase
{
    [JsonProperty(PropertyName = "error")]
    public List<string> Error;
}

public class OHLCResponse : ResponseBase
{
    [JsonProperty("result")]
    public OHLCResult Result;
}

public class OHLCResult
{
    [JsonProperty("pair_names")]
    public Dictionary<string, OHLC[]> GetHistory;

    [JsonProperty("last")]
    public long Last;
}

....最后是它的胆量:

public class OHLC
{
    public int Time;
    public decimal Open;
    public decimal High;
    public decimal Low;
    public decimal Close;
    public decimal Vwap;
    public decimal Volume;
    public int Count;
}

我有一个标准的解串器类,它适用于我对同一 API 使用的所有其他调用,但我无法让这个调用工作。当我检索 OHLCResponse 对象时,我没有收到错误消息,并且始终填充“Result.Last”,但“Result.GetHistory”中的预期 OHLC 项数组始终为空/null。我知道数据已经成功返回,因为我可以看到从 WebRequest 返回的变量中的数据,然后我将其传递给反序列化器函数,所以我猜这些类的布局一定是错误的。

谁能看到我做错了什么?

非常感谢,戴夫

标签: c#jsonrestserializationkraken.com

解决方案


您发布的对象不是有效的 JSON。缺少外部花括号。所以我假设它应该是这样的:

{
    "error": [],
    "result": {
        "MANAEUR": [
            [1619042400, "1.11200", "1.13488", "1.08341", "1.10077", "1.09896", "58878.56534370", 137],
            [1619046000, "1.09767", "1.12276", "1.08490", "1.11097", "1.10456", "25343.25910419", 77],

        ],
        "last": 1619118000
    }
}

匿名反序列化

您可以做的第一种方法是使用匿名反序列化,这可能有点笨拙,因为您必须反序列化两次。

让我们从定义一些模型开始:

public sealed class OHLCModel
{
    public long Time { get; set; }
    public decimal Open { get; set; }
    public decimal High { get; set; }
    public decimal Low { get; set; }
    public decimal Close { get; set; }
    public decimal Vwap { get; set; }
    public decimal Volume { get; set; }
    public int Count { get; set; }
}

public sealed class ResultModel
{
    [JsonIgnore]
    public IEnumerable<OHLCModel> Manaeur { get; set; }

    [JsonProperty("last")]
    public long Last { get; set; }
}

public sealed class RootModel
{
    [JsonProperty("error")]
    public List<string> Error { get; set; }

    [JsonProperty("result")]
    public ResultModel Result { get; set; }
}

如您所见,Manaeur当序列化发生时,我们忽略了对象。

为了使这个方法有效,我们这样做:

var json = System.IO.File.ReadAllText(@"c:\users\andy\desktop\test.json");

// First, just grab the object that has the mixed arrays.
// This creates a "template" of the format of the target object
var dto = JsonConvert.DeserializeAnonymousType(json, new
{
    Result = new
    {
        Manaeur = new List<List<object>>()
    }
});
            
// Next, deserialize the rest of it
var fullObject = JsonConvert.DeserializeObject<RootModel>(json);

// transfer the DTO using a Select statement
fullObject.Result.Manaeur = dto.Result.Manaeur.Select(x => new OHLCModel
{
    Time = Convert.ToInt64(x[0]),
    Open = Convert.ToDecimal(x[1]),
    High = Convert.ToDecimal(x[2]),
    Low = Convert.ToDecimal(x[3]),
    Close = Convert.ToDecimal(x[4]),
    Vwap = Convert.ToDecimal(x[5]),
    Volume = Convert.ToDecimal(x[6]),
    Count = Convert.ToInt32(x[7])
});

这不是最理想的解决方案,因为您在几个地方与模型紧密耦合。做到这一点的理想方法是自定义JsonSerializer.

使用自定义 JsonConverter

我们要做的第一件事就是把你改成ResultModel这样:

public sealed class ResultModel
{
    [JsonConverter(typeof(ManaeurJsonConverter)), JsonProperty("MANAEUR")]
    public IEnumerable<OHLCModel> Manaeur { get; set; }

    [JsonProperty("last")]
    public long Last { get; set; }
}

然后实现一个JsonConverter

public sealed class ManaeurJsonConverter : JsonConverter
{
    public override bool CanConvert(Type objectType) => false; // this will never get called

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var lst = JArray.Load(reader).ToObject<List<List<object>>>();
        return lst.Select(x => new OHLCModel
        {
            Time = Convert.ToInt64(x[0]),
            Open = Convert.ToDecimal(x[1]),
            High = Convert.ToDecimal(x[2]),
            Low = Convert.ToDecimal(x[3]),
            Close = Convert.ToDecimal(x[4]),
            Vwap = Convert.ToDecimal(x[5]),
            Volume = Convert.ToDecimal(x[6]),
            Count = Convert.ToInt32(x[7])
        });
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    { // we don't need to write
        throw new NotImplementedException();
    }
}

然后,您可以简单地这样称呼它:

var json = System.IO.File.ReadAllText(@"c:\users\andy\desktop\test.json");

var fullObject = JsonConvert.DeserializeObject<RootModel>(json);

推荐阅读