System.Text.Json 从 API 调用中反序列化嵌套对象 - 数据包装在父 JSON 属性中



我有一个 API JSON 响应,它将数据内容包装在data属性中,如下所示:

{ 
"data":{ 
"email":"admin@example.com",
"mobile":"+1555555123",
"id":4,
"first_name":"Merchant",
"last_name":"Vendor",
"role":"Merchant",
}
}

因此,当使用像RequestSharp这样的库请求用户对象时,response.Content将用户的内容包装在datajson 属性中,因为它来自 API。法典:

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

这很好,但是当我使用System.Text.Json将 json 反序列化为对象时,如下所示,将创建User对象,但不会分配任何属性,尽管这是意料之中的,因为序列化程序正在寻找具有first_namelast_name的属性......不['data']['first_name']

User account = JsonSerializer.Deserialize<User>(response.Content, options);

如何让JsonSerializer.Deserialize忽略data包装器?在其他 API 调用中,它可能是对象的名称,例如transactionuser,无论哪种方式,它都会包装数据。

其他注意事项:

我的目标是最新的.Net Core 3.1,并且正在从Newtonsoft Json.Net 迁移


我的用户对象:

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

解决后更新:

我从下面的u/Nikunj Kakadiya那里选择了答案,因为它是可行的,并且与我最终所做的最相似。

我创建了一个基于通用模板的容器类来处理data,如下所示:

public class Container<T>
{
[JsonPropertyName("data")]
public T Data { get; set; }
}

然后,我使用该容器类包装从 API 调用返回的 json 内容,如下所示:

var options = new JsonSerializerOptions
{
AllowTrailingCommas = true
};
Container<User> accountContainer = JsonSerializer.Deserialize<Container<User>>(response.Content, options);
User account = accountContainer.Data;

此外,正如 u/Pavel Anikhouski 所指出的,我对User类的序列化导致了一个错误,需要我为id字段创建自定义转换器。API 将id作为整数返回,尽管它是User类中的字符串。这是我遇到的错误,最初令人困惑,但我很快就弄清楚了:ERROR: The JSON value could not be converted to System.String. Path: $.data.id | LineNumber: 0 | BytePositionInLine: 77.

这是自定义转换器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);
}

然后更改User类以使用客户转换器:

...
[JsonPropertyName("id")]
[JsonConverter(typeof(IntToStringConverter))]
public string Id { get; set; }
...

你需要让另一个类。下面给出

Public class UserData
{
public User data { get; set; }; 
}

现在,您可以使用名为UserData的新类反序列化数据,如下所示

UserData account = JsonSerializer.Deserialize<UserData>(response.Content, options);

可以使用API 获取User对象System.Text.Json而无需从 JSON 示例中指定属性data的名称

{ 
"data":{ 
"email":"admin@example.com",
"mobile":"+1555555123",
"id":4,
"first_name":"Merchant",
"last_name":"Vendor",
"role":"Merchant",
}
}

通过以下代码

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

在上面的示例中,加载了JsonDocument,然后枚举RootElement以获取第一个嵌套对象作为文本,以反序列化为User实例。

document.RootElement.GetProperty("data");这样的名称更容易获得属性,但根据您的问题,名称实际上可能会有所不同。通过索引器(如document.RootElement[0](访问也是不可能的,因为它仅在元素的ValueKindArray时才有效,而不是Object,就像您的情况一样

我还"id":4,更改为"id":"4",,因为收到错误

无法获取字符串形式的令牌类型"数字"的值。

您可以创建如下对象:

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

然后

var data = JsonSerializer.Deserialize<RootObject>(JsonData);

然后,您可以访问如下所示的数据:

RootObject.Data.email ;
RootObject.Data.first_name

此外,每当您需要将JSON字符串转换为C# POCO类时,都可以使用这样的工具 http://json2csharp.com/:

要么删除数据对象,要么编写自定义 json 转换器。我建议更改发送的数据或按原样使用它,因为您要做的不是最佳实践。

最新更新