如何使用 Newtonsoft.Json 将复杂的 json 响应反序列化为易于反序列化的结构?

How can I deserialize complex json repsonse into easy-to-deserialize structure using Newtonsoft.Json?

出现这种看起来很奇怪的 json 回复:

{
    "invoices":{
       "0":{
          "invoice":{
             "id":"420",
             "invoicecontents":{
                "0":{
                   "invoicecontent":{
                      "name":"Here's the name of the content 0"
                   }
                },
                "1":{
                   "invoicecontent":{
                      "name":"Here's the name of the content 1"
                   }
                }
             }
          }
       },
       "1":{
         "invoice":{
            "id":"420",
            "invoicecontents":{
               "0":{
                  "invoicecontent":{
                     "name":"Here's the name of the content 0"
                  }
               }
            }
         }
      },
       "parameters":{
          "limit":"3",
          "page":"1",
          "total":"420"
       }
    },
    "status":{
       "code":"OK"
    }
 }

如何将结构更改为这种易于反序列化的结构?

{
   "invoices":[
      {
         "id":"420",
         "invoicecontents":[
            {
               "name":"Here's the name of the content 0"
            },
            {
               "name":"Here's the name of the content 1"
            }
         ]
      },
      {
         "id":"420",
         "invoicecontents":[
            {
               "name":"Here's the name of the content 0"
            }
         ]
      }
   ]
}

我想反序列化为如下发票列表

class Invoice {
    public string Id { get; set; }
    
    [JsonProperty("invoicecontents")]
    public InvoiceContent[] Contents { get; set; }

    class InvoiceContent {
        public string Name { get; set; }
    }
}

获取状态码或者参数都没有问题,我就这么干:

var parsed = JObject.Parse(jsonInvoices);

var statusCode = parsed?["status"]?["code"]?.ToString();
var parameters = parsed?["invoices"]?["parameters"]?;

当我试图实现我之前提到的易于反序列化的 json 结构时,真正的问题就出现了。我试过这样的事情:

var testInvoices = parsed?["invoices"]?
    .SkipLast(1)
    .Select(x => x.First?["invoice"]);

但我无法设法“修复”invoicecontents/invoicecontent 零件。 我的目标是反序列化和存储数据。

试试这个

     var jsonParsed = JObject.Parse(json);
    
    var invoices = ((JObject) jsonParsed["invoices"]).Properties()
    .Select(x => (JObject) x.Value["invoice"] ).Where(x => x != null)
    .Select(s => new Invoice
    {
        Id = s.Properties().First(x => x.Name == "id").Value.ToString(),
        Contents = ((JObject)s.Properties().First(x => x.Name == "invoicecontents").Value).Properties()
        .Select(x => (string) x.Value["invoicecontent"]["name"]).ToList()
    }).ToList();

我也简化了你的 class,我认为用一个字符串保留 classes 数组没有任何意义,我认为它应该只是字符串数组

public class Invoice
{
    public string Id { get; set; }

    public List<string> Contents { get; set; }
}

结果

[
  {
    "Id": "420",
    "Contents": [
      "Here's the name of the content 0",
      "Here's the name of the content 1"
    ]
  },
  {
    "Id": "420",
    "Contents": [
      "Here's the name of the content 0"
    ]
  }
]

这不是 JSON。

JavaScript Object Notation 从字面上描述了 Objects。你在这里看到的是一个笑话的妙语,开头是:“一个 SQL JOIN、一个 StringBuilder 和几个 for 循环走进酒吧......”

正如其他人所证明的那样,JObject 非常适合与 JSON 一起工作,而将其定义为 类 是不切实际的。您可以使用 Linq 来浏览它,但 JSON路径表达式 可以简单得多。

示例 1:让我们获取状态码。

var status = parsed.SelectToken(".status.code").Value<string>();

示例 2:我们 'deserialize' 我们的发票。


public string Invoice 
{ 
  public string Id { get; set; } 
  public List<string> Content { get; set; }
}

public List<Invoice> GetInvoices(string badJson)
{
  var invoices = JObject.Parse(badJson).SelectTokens(".invoices.*.invoice");
  var results = new List<Invoice>();
  foreach (var invoice in invoices)
  {
    results.Add(new Invoice()
    {
      Id = invoice.Value<string>("id"),
      Contents = invoice.SelectTokens(".invoicecontents.*.invoicecontent.name")
        .Values<string>().ToList()
      // Note: JToken.Value<T> & .Values<T>() return nullable types
    });
  }
  return results;
}