问题
I have an API JSON response that wraps the data content in a data
property, which looks like this:
{
"data":{
"email":"admin@example.com",
"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; }
...
回答1:
You need to make one another class. That is given below
Public class UserData
{
public User data { get; set; };
}
Now you can Deserialize data using new class called UserData like below
UserData account = JsonSerializer.Deserialize<UserData>(response.Content, options);
回答2:
It's possible to get a User
object using System.Text.Json
API without specifying a name of property data
from your JSON sample
{
"data":{
"email":"admin@example.com",
"mobile":"+1555555123",
"id":4,
"first_name":"Merchant",
"last_name":"Vendor",
"role":"Merchant",
}
}
by the following code
var document = JsonDocument.Parse(json, new JsonDocumentOptions { AllowTrailingCommas = true });
var enumerator = document.RootElement.EnumerateObject();
if (enumerator.MoveNext())
{
var userJson = enumerator.Current.Value.GetRawText();
var user = JsonSerializer.Deserialize<User>(userJson,
new JsonSerializerOptions {AllowTrailingCommas = true});
}
In the sample above the JsonDocument is loaded, than RootElement
is enumerated to get the first nested object as text to deserialize into User
instance.
It's more easier to get a property by name like document.RootElement.GetProperty("data");
, but the name can be different actually, according to your question. Accessing through indexer, like document.RootElement[0]
is also not possible, because it works only when ValueKind
of an element is Array
, not the Object
, like in your case
I've also changed the "id":4,
to "id":"4",
, because getting an error
Cannot get the value of a token type 'Number' as a string.
回答3:
You can create an object like this:
public class Data
{
public string email { get; set; }
public string mobile { get; set; }
public int id { get; set; }
public string first_name { get; set; }
public string last_name { get; set; }
public string role { get; set; }
}
public class RootObject
{
public Data data { get; set; }
}
then
var data = JsonSerializer.Deserialize<RootObject>(JsonData);
then you can access the data like the following:
RootObject.Data.email ;
RootObject.Data.first_name
Also, anytime you need to convert JSON string to C# POCO class you can use a tool like this : http://json2csharp.com/
回答4:
Either you get rid of the data object or you can write a custom json Converter. I suggest to change the sent data or just consume it as it is because what you are trying to do is not a best practice.
来源:https://stackoverflow.com/questions/59568647/system-text-json-deserialize-nested-object-from-api-call-data-is-wrapped-in-pa