C#:解析对具有 x 属性的类对象的非 JSON 仅数组 api 响应

Posted

技术标签:

【中文标题】C#:解析对具有 x 属性的类对象的非 JSON 仅数组 api 响应【英文标题】:C#: Parsing a non-JSON array-only api response to a class object with x properties 【发布时间】:2018-07-03 22:11:28 【问题描述】:

我有来自一些 Rest API 的响应,它只返回一些数组,如下所示:

[
[123,"0.01","0.02","0.03","0.04","12345.00000",123456789,"300.000",4000,"123.000","456.000","0"],
[456,"0.04","0.03","0.02","0.01","54321.00000",987654321,"500.000",4000,"123.000","456.000","1"],
[789,"0.05","0.06","0.07","0.08","12345.00000",123456789,"700.000",8000,"456.000","123.000","0"]    
]

在这个例子中,数据集的数量是 3,但数量总是不同的,也可能是 100+。

我想把这个读出到一个类对象中,根据响应中显示的每种类型的值,它有 12 个数组:

public class foo

    ...
    public int[] firstParam;
    public string[] secondParam;
    public string[] thirdParam;

    ...        

例如,firstParam 应包含 123,456,789secondParam 应该包含"0.01","0.04","0.05" 等等。

列的架构已知并记录在Public Rest API for Binance: Kline/Candlestick data. 中。例如https://api.binance.com/api/v1/klines?symbol=XVGBTC&interval=1h

之类的查询

【问题讨论】:

试试这个链接:***.com/a/10432672/4180382 带双引号的值是浮点数,不是字符串。 您知道各个列的架构吗?即第一列是起始值,等等?另外,您使用什么进行 JSON 解析?是json.net吗? @ole:感谢您的链接,但收到的字符串不符合 json 标准,因为没有“关键字”等。 @AlexanderS。 - 它是格式完美的 JSON。上传到jsonlint.com,没有错误。只是所有容器都是数组(值的有序序列)而不是对象(名称/值对的无序序列)。请参阅json.org 进行确认。 【参考方案1】:

API 响应完全有效JSON;它是原始值的锯齿状二维数组,其中列具有Public Rest API for Binance: Kline/Candlestick data 中定义的特定含义。因此,可以使用json.net 对其进行解析和反序列化,例如作为object [][]:

var arrays = JsonConvert.DeserializeObject<object [][]>(jsonString);

(工作示例.Net fiddle #1。)

但是,与其将 JSON 反序列化为锯齿状二维对象数组或(如您在问题中建议的那样)具有与列值对应的数组属性的单个根对象,我建议您设计一个类 BinanceKlineData 表示这些特定列的单行值,然后使用来自How to deserialize an array of values with a fixed schema to a strongly typed data class? 的custom JsonConverter ObjectToArrayConverter&lt;BinanceKlineData&gt; 反序列化为List&lt;BinanceKlineData&gt;

首先,使用列的文档含义,您可以定义您的类型BinanceKlineData,如下所示:

public class BinanceKlineData

    [JsonProperty(Order = 1)]
    public long OpenTime  get; set; 
    [JsonProperty(Order = 2)]
    public decimal Open  get; set;  // Or string, if you prefer
    [JsonProperty(Order = 3)]
    public decimal High  get; set;  // Or string, if you prefer
    [JsonProperty(Order = 4)]
    public decimal Low  get; set;  // Or string, if you prefer
    [JsonProperty(Order = 5)]
    public decimal Close  get; set;  // Or string, if you prefer
    [JsonProperty(Order = 6)]
    public decimal Volume  get; set;  // Or string, if you prefer
    [JsonProperty(Order = 7)]
    public long CloseTime  get; set; 
    [JsonProperty(Order = 8)]
    public decimal QuoteAssetVolume  get; set;  // Or string, if you prefer
    [JsonProperty(Order = 9)]
    public long NumberOfTrades  get; set;  // Should this be an long or a decimal?
    [JsonProperty(Order = 10)]
    public decimal TakerBuyBaseAssetVolume  get; set; 
    [JsonProperty(Order = 11)]
    public decimal TakerBuyQuoteAssetVolume  get; set; 
    // public string Ignore  get; set; 

请注意,我已使用 [JsonProperty(Order = N)] 注释属性。此顺序对应于 Rest API 中列的顺序,将用于通知 Json.NET 如何通过列索引将列映射到属性。另请注意,我将数字列建模为decimal,尽管它们在 JSON 中显示为字符串。如果您更喜欢文字反序列化,可以使用 string

接下来,从this answer 中获取通用ObjectToArrayConverter&lt;T&gt;。它具有利用Order = N 元数据按列索引将行值映射到通用类型T 中的成员值的逻辑。

最后,您将能够将 JSON 反序列化为 List&lt;BinanceKlineData&gt;,如下所示:

var settings = new JsonSerializerSettings

    Converters =  new ObjectToArrayConverter<BinanceKlineData>() ,
;

var root = JsonConvert.DeserializeObject<List<BinanceKlineData>>(jsonString, settings);

样本工作.Net fiddle #2。

或者,如果您愿意,可以将转换器直接应用到BinanceKlineData,如下所示:

[JsonConverter(typeof(ObjectToArrayConverter<BinanceKlineData>))]
public class BinanceKlineData

    // Remainder as before

当转换器直接应用于类型时,不再需要通过JsonSerializerSettings.Converters 传递它。

示例fiddle #3。

【讨论】:

@AlexanderS。 - 不客气。如果问题得到解答,请mark it as such。【参考方案2】:

根据响应的确切格式,您可能可以执行以下操作:

var s = "[[123,\"0.01\",\"0.02\",\"0.03\",\"0.04\",\"12345.00000\",123456789,\"300.000\",4000,\"123.000\",\"456.000\",\"0\"],[456,\"0.04\",\"0.03\",\"0.02\",\"0.01\",\"54321.00000\",987654321,\"500.000\",4000,\"123.000\",\"456.000\",\"1\"],[789,\"0.05\",\"0.06\",\"0.07\",\"0.08\",\"12345.00000\",123456789,\"700.000\",8000,\"456.000\",\"123.000\",\"0\"]]";

var lines = s.Split(new char[]  '[', ']' , StringSplitOptions.RemoveEmptyEntries).Select(a => a.Split(new char[]  ',' , StringSplitOptions.RemoveEmptyEntries).Select(b => b.TrimEnd('"').TrimStart('"')).ToArray()).Where(a => a.Any());

var c = lines.Count();

var foo = new foo

    firstParam = new int[c],
    secondParam = new string[c],
    thirdParam = new string[c]
;

for (int i = 0; i < c; i++)

    foo.firstParam[i] = Int32.Parse(lines.ElementAt(i)[0]);
    foo.secondParam[i] = lines.ElementAt(i)[1];
    foo.thirdParam[i] = lines.ElementAt(i)[2];


Console.WriteLine(string.Join(", ", foo.firstParam)); \\123, 456, 789
Console.WriteLine(string.Join(", ", foo.secondParam)); \\0.01, 0.04, 0.05
Console.WriteLine(string.Join(", ", foo.thirdParam)); \\0.02, 0.03, 0.06

【讨论】:

以上是关于C#:解析对具有 x 属性的类对象的非 JSON 仅数组 api 响应的主要内容,如果未能解决你的问题,请参考以下文章

使用 C# 解析复杂的 JSON

在 C# 中创建一个每次都可以具有不同属性的类或对象

API Json 对具有大写属性首字母的 C# 对象的响应

合并具有相同属性值的json对象c#

MATLAB 中 A = K * B 的类(具有依赖行为的非依赖属性)

将 JSON 字符串解析为对象的最简单的 C# 函数是啥? [关闭]