System.Text.Json 从 API 调用反序列化嵌套对象 - 数据包装在父 JSON 属性 中
System.Text.Json Deserialize nested object from API call - Data is wrapped in parent JSON property
我有一个 API JSON 响应,它将数据内容包装在 data
属性 中,看起来像这样:
{
"data":{
"email":"admin@example.com",
"mobile":"+1555555123",
"id":4,
"first_name":"Merchant",
"last_name":"Vendor",
"role":"Merchant",
}
}
因此,当使用像 RequestSharp
这样的库请求用户对象时,response.Content
的用户内容包含在 data
json 属性 因为它来自 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
这很好,但是当我将 json 反序列化为具有 System.Text.Json
的对象时,如下所示,将创建 User
对象但不会分配任何属性,尽管这是意料之中的,因为序列化程序正在寻找具有 first_name
和 last_name
的属性...而不是 ['data']['first_name']
User account = JsonSerializer.Deserialize<User>(response.Content, options);
如何让 JsonSerializer.Deserialize
忽略 data
包装器?在其他 API 调用中,它可能是对象的名称,例如 transaction
或 user
,无论哪种方式,它都会包装数据。
其他注意事项:
我的目标是最新的 .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 中选择了一个可行的答案,并且与我最终所做的最相似。
我创建了一个基于通用模板的容器 class 来处理 data
这样的:
public class Container<T>
{
[JsonPropertyName("data")]
public T Data { get; set; }
}
然后我使用该容器 class 包装从 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
class 的序列化导致了一个错误,需要我为 id
字段创建自定义转换器. APIreturnsid
虽然是整数,但在User
class中是字符串。这是我 运行 最初感到困惑的错误,但我很快就弄明白了: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
Class 更改为使用客户转换器:
...
[JsonPropertyName("id")]
[JsonConverter(typeof(IntToStringConverter))]
public string Id { get; set; }
...
你们需要互相成就 class。下面给出
Public class UserData
{
public User data { get; set; };
}
现在您可以使用新的 class 调用 UserData 反序列化数据,如下所示
UserData account = JsonSerializer.Deserialize<UserData>(response.Content, options);
您可以像这样创建一个对象:
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 class 您都可以使用这样的工具:http://json2csharp.com/
要么删除数据对象,要么编写自定义 json 转换器。我建议更改发送的数据或按原样使用它,因为您尝试做的不是最佳做法。
可以使用 System.Text.Json
API 获取 User
对象,而无需从您的 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");
这样的名字更容易获得 属性,但根据您的问题,实际名称可能不同。通过 indexer 访问也是不可能的,例如 document.RootElement[0]
,因为它仅在元素的 ValueKind
为 Array
时才有效,而不是 Object
,就像在您的案例
我也将 "id":4,
更改为 "id":"4",
,因为出现错误
Cannot get the value of a token type 'Number' as a string.
我有一个 API JSON 响应,它将数据内容包装在 data
属性 中,看起来像这样:
{
"data":{
"email":"admin@example.com",
"mobile":"+1555555123",
"id":4,
"first_name":"Merchant",
"last_name":"Vendor",
"role":"Merchant",
}
}
因此,当使用像 RequestSharp
这样的库请求用户对象时,response.Content
的用户内容包含在 data
json 属性 因为它来自 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
这很好,但是当我将 json 反序列化为具有 System.Text.Json
的对象时,如下所示,将创建 User
对象但不会分配任何属性,尽管这是意料之中的,因为序列化程序正在寻找具有 first_name
和 last_name
的属性...而不是 ['data']['first_name']
User account = JsonSerializer.Deserialize<User>(response.Content, options);
如何让 JsonSerializer.Deserialize
忽略 data
包装器?在其他 API 调用中,它可能是对象的名称,例如 transaction
或 user
,无论哪种方式,它都会包装数据。
其他注意事项:
我的目标是最新的 .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 中选择了一个可行的答案,并且与我最终所做的最相似。
我创建了一个基于通用模板的容器 class 来处理 data
这样的:
public class Container<T>
{
[JsonPropertyName("data")]
public T Data { get; set; }
}
然后我使用该容器 class 包装从 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
class 的序列化导致了一个错误,需要我为 id
字段创建自定义转换器. APIreturnsid
虽然是整数,但在User
class中是字符串。这是我 运行 最初感到困惑的错误,但我很快就弄明白了: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
Class 更改为使用客户转换器:
...
[JsonPropertyName("id")]
[JsonConverter(typeof(IntToStringConverter))]
public string Id { get; set; }
...
你们需要互相成就 class。下面给出
Public class UserData
{
public User data { get; set; };
}
现在您可以使用新的 class 调用 UserData 反序列化数据,如下所示
UserData account = JsonSerializer.Deserialize<UserData>(response.Content, options);
您可以像这样创建一个对象:
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 class 您都可以使用这样的工具:http://json2csharp.com/
要么删除数据对象,要么编写自定义 json 转换器。我建议更改发送的数据或按原样使用它,因为您尝试做的不是最佳做法。
可以使用 System.Text.Json
API 获取 User
对象,而无需从您的 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");
这样的名字更容易获得 属性,但根据您的问题,实际名称可能不同。通过 indexer 访问也是不可能的,例如 document.RootElement[0]
,因为它仅在元素的 ValueKind
为 Array
时才有效,而不是 Object
,就像在您的案例
我也将 "id":4,
更改为 "id":"4",
,因为出现错误
Cannot get the value of a token type 'Number' as a string.