如何反序列化嵌套的 JSON 对象,它是 System.Text.Json 中的字符串?

How do I deserialize a nested JSON object which is a string in System.Text.Json?

我在 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: The JSON value could not be converted to Store.Controllers.CheckoutController+EstoData. Path: $.data | LineNumber: 0 | BytePositionInLine: 497. at System.Text.Json.ThrowHelper.ThrowJsonException_DeserializeUnableToConvertValue(Type propertyType) ...

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.

的结尾

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

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

您需要创建自定义 JsonConverter<T>,在这种情况下 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

"data" 属性 in your original json 是双重序列化的,所以应该从 json string 到 object

第二次反序列化

所以试试这个代码

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; }
}