我正在使用JSON.NET开发一个与第三方REST API接口的ASP.NET应用程序。API以以下格式返回JSON:
[
{
user: {
id: 12345,
first_name: "John",
last_name: "Smith",
created_at: "11/12/13Z00:00:00"
}
},
{
user: {
id: 12346,
first_name: "Bob",
last_name: "Adams",
created_at: "12/12/13Z00:00:00"
}
}
]
user
字段是多余的,但我无法控制API,因此我必须使用它。我目前正在使用以下代码来反序列化JSON:
// Deserialize
var responseBody = JsonConvert.DeserializeObject<IEnumerable<UserWrapper>>(responseString);
// Access Properties
foreach (var userWrapper in responseBody)
{
var firstName = userWrapper.User.FirstName
}
// Model classes
public class UserWrapper
{
[JsonProperty(PropertyName = "user")]
public User User { get; set; }
}
public class User
{
[JsonProperty(PropertyName = "id")]
public string ID { get; set; }
[JsonProperty(PropertyName = "first_name")]
public string FirstName { get; set; }
[JsonProperty(PropertyName = "last_name")]
public string LastName { get; set; }
[JsonProperty(PropertyName = "created_at")]
public DateTime CreatedAt { get; set; }
}
拥有包装器类有点难看。所以我的问题是:
有没有一种方法可以使用JSON.NET消除包装类
是的,您可以通过为User
类创建一个自定义转换器并在反序列化过程中使用它来实现这一点。转换器代码可能看起来像这样:
class UserConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(User));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject wrapper = JObject.Load(reader);
return wrapper["user"].ToObject<User>();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteStartObject();
writer.WritePropertyName("user");
writer.WriteRawValue(JsonConvert.SerializeObject(value));
writer.WriteEndObject();
}
}
保持User
类与您定义的完全一样(使用所有JsonProperty
装饰)。然后,您可以反序列化JSON并访问如下属性:
var users = JsonConvert.DeserializeObject<List<User>>(responseString, new UserConverter());
foreach (var user in users)
{
var firstName = user.FirstName
// etc...
}
UserWrapper
类不再需要,可以删除。
编辑
重要提示:如果使用[JsonConverter]
属性装饰User
类,则上述解决方案将不起作用。这是因为编写的自定义转换器使用序列化程序的第二个副本来处理User
反序列化,一旦我们降低了一个级别以获得真实的用户数据。如果对类进行修饰,那么序列化程序的所有副本都将尝试使用转换器,最终会出现自引用循环。
如果您更愿意使用[JsonConverter]
属性,那么自定义转换器类将不得不手动处理User
类的所有属性的反序列化。采用这种方法,代码会变成这样:
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject wrapper = JObject.Load(reader);
JToken user = wrapper["user"];
return new User
{
ID = user["id"].Value<string>(),
FirstName = user["first_name"].Value<string>(),
LastName = user["last_name"].Value<string>(),
CreatedAt = user["created_at"].Value<DateTime>()
};
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
User user = (User)value;
writer.WriteStartObject();
writer.WritePropertyName("user");
writer.WriteStartObject();
writer.WritePropertyName("id");
writer.WriteValue(user.ID);
writer.WritePropertyName("first_name");
writer.WriteValue(user.FirstName);
writer.WritePropertyName("last_name");
writer.WriteValue(user.LastName);
writer.WritePropertyName("created_at");
writer.WriteValue(user.CreatedAt);
writer.WriteEndObject();
writer.WriteEndObject();
}
这种方法的明显问题是,您牺牲了一点可维护性:如果您向User
类添加了更多属性,则必须记住更改UserConverter
以匹配。