I'm deserializing JSON in a ASP.NET 5 MVC application using:
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; }
}
This throws the error:
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
is a single line string, containing an embedded 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"}
Position 497 seems to point to the end of the Data
property before mac
.
How can I deserialize this JSON to EstoJarelMaksTulemnus
, considering EstoData
is a concrete nested type in my object but a string
in the JSON?
CodePudding user response:
You need to create a custom JsonConverter<T>
, where T
is EstoData
in this case, to be able to correctly deserialise the nested Data
JSON object.
This should work, both for deserialisation and serialisation of the object again:
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; }
}
Usage:
var tulemus = JsonSerializer.Deserialize<EstoJarelMaksTulemnus>(apiResponse, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
Here's a working demo:
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; }
}
Output:
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
CodePudding user response:
"data" property in your original json is double serialized, so it should be deserialized the second time from json string to object
so try this code
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);
fixed 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"
}
Test
JsonDeserialized deserializedFixedJson = Deserialize<JsonDeserialized>(jsonFixed, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
classes
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; }
}
CodePudding user response:
the Data
in apiResponse
is not an object, it's a string , you can convert the string into a model using Newtonsoft Json.Net
, as is explained in this issue : Deserializing stringified (quote enclosed) nested objects with Newtonsoft Json.Net