I have an API JSON response that wraps the data content in a data
property, which looks like this:
{
"data":{
"email":"[email protected]",
"mobile":"+1555555123",
"id":4,
"first_name":"Merchant",
"last_name":"Vendor",
"role":"Merchant",
}
}
So when making request for a User Object with a Library like RequestSharp
, the response.Content
has the content for the User wrapped in the data
json property as it comes from the API. Code:
var request = RequestHelper.CreateTokenRequest(email, password); // Create the request from a helper
var client = new RestClient(BaseUrl); // create new RestSharp Client
IRestResponse response = client.Execute(request); // execute the request
var content = response.Content; // raw content as string
This is fine, but when I go to deserialize the json to an Object with System.Text.Json
, like the following, will create the User
object but will not assign any of the attributes, although this is sort of expected because the serializer is looking for properties with first_name
and last_name
... not ['data']['first_name']
User account = JsonSerializer.Deserialize<User>(response.Content, options);
How can I get the JsonSerializer.Deserialize
to ignore the data
wrapper? In other API calls it may be the name of the Object such as transaction
or user
, either way, it wraps the data.
Other Notes:
I am targeting the latest .Net Core 3.1 and am migrating from Newtonsoft Json.Net
My User Object:
using System.ComponentModel;
using System.Text.Json.Serialization;
namespace MyApplication.Models
{
public interface IUser
{
string FirstName { get; set; }
string LastName { get; set; }
string Email { get; set; }
string Mobile { get; set; }
string Id { get; set; }
string Role { get; set; }
}
public class User : IUser
{
[JsonPropertyName("first_name")]
public string FirstName { get; set; }
[JsonPropertyName("last_name")]
public string LastName { get; set; }
[JsonPropertyName("email")]
public string Email { get; set; }
[JsonPropertyName("mobile")]
public string Mobile { get; set; }
[JsonPropertyName("id")]
public string Id { get; set; }
[JsonPropertyName("role")]
public string Role { get; set; }
[JsonIgnore]
public string Token { get; set; }
}
}
Update after resolution:
I selected the answer from u/Nikunj Kakadiya below as something that would work and was most similar to what I ended up doing.
I created a generic template based container class to handle the data
like this:
public class Container<T>
{
[JsonPropertyName("data")]
public T Data { get; set; }
}
I then used that container class to wrap the returned json contents from the API call, like this:
var options = new JsonSerializerOptions
{
AllowTrailingCommas = true
};
Container<User> accountContainer = JsonSerializer.Deserialize<Container<User>>(response.Content, options);
User account = accountContainer.Data;
Additionally, as u/Pavel Anikhouski noted, my serialization of the User
class led to an error that required me to create a custom converter for the id
field. The API returns the id
as an integer although it is a string in the User
class. This was the error I ran into which was initially confusing but I was able to figure out pretty quickly: ERROR: The JSON value could not be converted to System.String. Path: $.data.id | LineNumber: 0 | BytePositionInLine: 77.
Here is the custom converter IntToStringConverter
:
public class IntToStringConverter : JsonConverter<string>
{
public override string Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options) => reader.GetInt32().ToString();
public override void Write(
Utf8JsonWriter writer,
string value,
JsonSerializerOptions options) =>
writer.WriteStringValue(value);
}
and then changed the User
Class to use the customer converter:
...
[JsonPropertyName("id")]
[JsonConverter(typeof(IntToStringConverter))]
public string Id { get; set; }
...
id
property – Pavel AnikhouskiJsonSerializer.Deserialize<Dictionary<string, User>>(response.Content, options).Values.SingleOrDefault();
– dbc