【问题标题】:How do I deserialize a nested JSON object which is a string in System.Text.Json?如何反序列化作为 System.Text.Json 中的字符串的嵌套 JSON 对象?
【发布时间】:2021-11-29 00:30:19
【问题描述】:

我在 ASP.NET 5 MVC 应用程序中反序列化 JSON,使用:

var tulemus = JsonSerializer.Deserialize<EstoJarelMaksTulemnus>(apiResponse, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

public class EstoJarelMaksTulemnus
{
   public string[] Errors { get; set; }
   public EstoData Data { get; set; }
   public string Mac { get; set; }
}

public class EstoData
{
   public string Id { get; set; }
   public string Status { get; set; }
   public string Purchase_url { get; set; }
   public string Merchant_reference { get; set; }
   public decimal Amount { get; set; }
   public string Currency { get; set; }
   public bool Is_test { get; set; }
   public string Return_url { get; set; }
   public string Notification_url { get; set; }
}

这会引发错误:

System.Text.Json.JsonException:无法转换 JSON 值 到 Store.Controllers.CheckoutController+EstoData。路径:$.data | 行号:0 | BytePositionInLine: 497. 在 System.Text.Json.ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(类型 属性类型)...

apiResponse 是一个单行字符串,包含一个嵌入的EstoData

{"errors":[],"data":"{\"id\":\"iUW3YiDIz5Ahg5eO8hV7d3Cv7SVbZ913\",\"status\":\"CREATED\",\"purchase_url\":\"https:\\\/\\\/user.esto.com\\\/application\\\/iUW3YiDIeO8hV7d3Cv7SVbZ913\",\"merchant_reference\":\"15502\",\"amount\":93.95,\"currency\":\"EUR\",\"is_test\":true,\"return_url\":\"http:\\\/\\\/localhost:54274\\\/CheckoutController\\\/EstoJarelmaksOK?tellimus=104742\",\"notification_url\":\"http:\\\/\\\/localhost:54274\\\/CheckoutController\\\/EstoJarelmaksTeade?tellimus=104742\"}","mac":"E9C3E61FC347D8043ABDCF464D537C37A609EC878F95B5F526271A2287F2D2E507B5A14FA3AF5F7A6D4CDECB6E8A1DBDF9A5633E0B3AD96DA35FA1C9"}

位置 497 似乎指向 Data 属性的末尾,位于 mac 之前。

考虑到 EstoData 在我的对象中是具体的嵌套类型,但在 JSON 中是 string,我如何将此 JSON 反序列化为 EstoJarelMaksTulemnus

【问题讨论】:

  • 在过去的一周里,我已经看到了十几个关于此的问题。为什么每个人都在编写或使用返回这种格式的 API?

标签: c# asp.net-core asp.net-core-mvc json-deserialization system.text.json


【解决方案1】:

您需要创建一个自定义JsonConverter&lt;T&gt;,在这种情况下TEstoData,以便能够正确反序列化嵌套的Data JSON 对象。

这应该可以再次用于反序列化和序列化对象:

StringToEstoDataConverter.cs

public class StringToEstoDataConverter : JsonConverter<EstoData>
{
  public override EstoData? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
  {
      using (var jsonDoc = JsonDocument.ParseValue(ref reader))
      {
          var infoData = jsonDoc.RootElement.GetString();
          if (infoData != null)
              return JsonSerializer.Deserialize<EstoData>(infoData, options);
      }

      return default;
  }

  public override void Write(Utf8JsonWriter writer, EstoData value, JsonSerializerOptions options)
  {
      JsonSerializer.Serialize(writer, value, value.GetType(), options);
  }
}

EstoJarelMaksTulemnus.cs

public class EstoJarelMaksTulemnus
{
  public string[] Errors { get; set; }

  [JsonConverter(typeof(StringToEstoDataConverter))]
  public EstoData Data { get; set; }

  public string Mac { get; set; }
}

用法:

var tulemus = JsonSerializer.Deserialize<EstoJarelMaksTulemnus>(apiResponse, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

这是一个工作演示:

public class Program
{
  public static void Main()
  {
      var data =
          "{\"errors\":[],\"data\":\"{\\\"id\\\":\\\"iUW3YiDIz5Ahg5eO8hV7d3Cv7SVbZ913\\\",\\\"status\\\":\\\"CREATED\\\",\\\"purchase_url\\\":\\\"https:\\\\\\/\\\\\\/user.esto.ee\\\\\\/application\\\\\\/iUW3YiDIz5Ahg5eO8hV7d3Cv7SVbZ913\\\",\\\"merchant_reference\\\":\\\"158502\\\",\\\"amount\\\":93.95,\\\"currency\\\":\\\"EUR\\\",\\\"is_test\\\":true,\\\"return_url\\\":\\\"http:\\\\\\/\\\\\\/localhost:54274\\\\\\/CheckoutController\\\\\\/EstoJarelmaksOK?tellimus=104742\\\",\\\"notification_url\\\":\\\"http:\\\\\\/\\\\\\/localhost:54274\\\\\\/CheckoutController\\\\\\/EstoJarelmaksTeade?tellimus=104742\\\"}\",\"mac\":\"E9C3E61FC347D80200F542C43ABDCF464D537C37A609EC878F95B5F526271A2287F2D2E507B5A14FA3AF5F7A6D4CDECB6E8A1DBDF9A5633E0B3AD96DA35FA1C9\"}";


      var tulemus = JsonSerializer.Deserialize<EstoJarelMaksTulemnus>(data, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

      Console.WriteLine(tulemus.Errors.Length);
      Console.WriteLine(tulemus.Data.Id);
      Console.WriteLine(tulemus.Data.Status);
      Console.WriteLine(tulemus.Data.Purchase_url);
      Console.WriteLine(tulemus.Data.Merchant_reference);
      Console.WriteLine(tulemus.Data.Amount);
      Console.WriteLine(tulemus.Data.Currency);
      Console.WriteLine(tulemus.Data.Is_test);
      Console.WriteLine(tulemus.Data.Return_url);
      Console.WriteLine(tulemus.Data.Notification_url);
      Console.WriteLine(tulemus.Mac);
  }
}

public class StringToEstoDataConverter : JsonConverter<EstoData>
{
  public override EstoData? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
  {
      using (var jsonDoc = JsonDocument.ParseValue(ref reader))
      {
          var infoData = jsonDoc.RootElement.GetString();
          if (infoData != null)
              return JsonSerializer.Deserialize<EstoData>(infoData, options);
      }

      return default;
  }

  public override void Write(Utf8JsonWriter writer, EstoData value, JsonSerializerOptions options)
  {
      JsonSerializer.Serialize(writer, value, value.GetType(), options);
  }
}


public class EstoJarelMaksTulemnus
{
  public string[] Errors { get; set; }

  [JsonConverter(typeof(StringToEstoDataConverter))]
  public EstoData Data { get; set; }

  public string Mac { get; set; }
}

public class EstoData
{
  public string Id { get; set; }
  public string Status { get; set; }
  public string Purchase_url { get; set; }
  public string Merchant_reference { get; set; }
  public decimal Amount { get; set; }
  public string Currency { get; set; }
  public bool Is_test { get; set; }
  public string Return_url { get; set; }
  public string Notification_url { get; set; }
}

输出:

0
iUW3YiDIz5Ahg5eO8hV7d3Cv7SVbZ913
CREATED
https://user.esto.ee/application/iUW3YiDIz5Ahg5eO8hV7d3Cv7SVbZ913
158502
93.95
EUR
True
http://localhost:54274/CheckoutController/EstoJarelmaksOK?tellimus=104742
http://localhost:54274/CheckoutController/EstoJarelmaksTeade?tellimus=104742
E9C3E61FC347D80200F542C43ABDCF464D537C37A609EC878F95B5F526271A2287F2D2E507B5A14FA3AF5F7A6D4CDECB6E8A1DBDF9A5633E0B3AD96DA35FA1C9

【讨论】:

    【解决方案2】:

    你的原始json中的“data”属性是双重序列化的,所以它应该第二次从json字符串反序列化为对象

    所以试试这段代码

    var json=...your json;
    
    var jd = JsonSerializer.Deserialize<Root>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
    
    // the second deserialize data property from json string to instance
    var jdd= JsonSerializer.Deserialize<Data>(jd.data);
    
    JsonDeserialized result = new JsonDeserialized {errors=jd.errors, data=jdd, mac=jd.mac};
    
    var jsonFixed=JsonSerializer.Serialize(result);
    

    固定的json

    {
      "errors": [],
      "data": {
        "id": "iUW3YiDIz5Ahg5eO8hV7d3Cv7SVbZ913",
        "status": "CREATED",
        "purchase_url": "https://user.esto.ee/application/iUW3YiDIz5Ahg5eO8hV7d3Cv7SVbZ913",
        "merchant_reference": "158502",
        "amount": 93.95,
        "currency": "EUR",
        "is_test": true,
        "return_url": "http://localhost:54274/CheckoutController/EstoJarelmaksOK?tellimus=104742",
        "notification_url": "http://localhost:54274/CheckoutController/EstoJarelmaksTeade?tellimus=104742"
      },
      "mac": "E9C3E61FC347D80200F542C43ABDCF464D537C37A609EC878F95B5F526271A2287F2D2E507B5A14FA3AF5F7A6D4CDECB6E8A1DBDF9A5633E0B3AD96DA35FA1C9"
    }
    

    测试

    JsonDeserialized deserializedFixedJson = Deserialize<JsonDeserialized>(jsonFixed, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
    

    public class JsonDeserialized
    {
        public List<object> errors { get; set; }
        public Data data { get; set; }
        public string mac { get; set; }
    }
    public class Data
    {
        public string id { get; set; }
        public string status { get; set; }
        public string purchase_url { get; set; }
        public string merchant_reference { get; set; }
        public double amount { get; set; }
        public string currency { get; set; }
        public bool is_test { get; set; }
        public string return_url { get; set; }
        public string notification_url { get; set; }
    }
    public class Root
    {
        public List<object> errors { get; set; }
        public string data { get; set; }
        public string mac { get; set; }
    }
    

    【讨论】:

      【解决方案3】:

      apiResponse 中的Data 不是对象,它是一个字符串,您可以使用Newtonsoft Json.Net 将字符串转换为模型,如本期所述:Deserializing stringified (quote enclosed) nested objects with Newtonsoft Json.Net

      【讨论】:

      • 应用程序当前仅使用 .NET5 JsonSerializer。在同一个应用程序中使用两个序列化器是否合理,或者可以使用 .NET 自己的 Deserializer 解决吗?
      • @Andrus 在两者之间使用临时对象或使用我在下面概述的 JsonConverter 是可行的 - 您可以使用 2 个序列化程序,但在这种情况下,这将是矫枉过正
      猜你喜欢
      • 2020-06-09
      • 2022-01-20
      • 1970-01-01
      • 1970-01-01
      • 2019-09-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多