问题
I am developing an ASP.NET application using JSON.NET that interfaces with a third-party REST API. The API returns JSON in the following format:
[
{
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"
}
}
]
The user
field is redundant, but I do not have control over the API so I have to live with it. I am currently using the following code to deserialize the 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; }
}
Having the wrapper class is a little ugly. So my question is:
Is there a way to eliminate the wrapper class using JSON.NET?
回答1:
Yes, you can do this by creating a custom converter for your User
class and using that during deserialization. The converter code might look something like this:
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();
}
}
Keep the User
class exactly as you defined it (with all the JsonProperty
decorations).
You can then deserialize your JSON and access properties like this:
var users = JsonConvert.DeserializeObject<List<User>>(responseString, new UserConverter());
foreach (var user in users)
{
var firstName = user.FirstName
// etc...
}
The UserWrapper
class is no longer needed and can be deleted.
EDIT
Important note: the above solution will NOT work if you decorate your User
class with the [JsonConverter]
attribute. This is because the custom converter as written uses a second copy of the serializer to handle the User
deserialization once we've gone down a level to get to the real user data. If you decorate the class, then all copies of the serializer will try to use the converter and you'll end up with a self-referencing loop.
If you would rather use the [JsonConverter]
attribute, then the custom converter class will have to handle deserialization of all the properties of the User
class manually. Taking this approach, the code would look like this instead:
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();
}
The obvious problem with this approach is that you sacrifice a little bit of maintainability: if you ever add more properties to your User
class, you have to remember to change your UserConverter
to match.
来源:https://stackoverflow.com/questions/17934114/is-there-a-way-to-deserialize-my-json-without-using-a-wrapper-class-for-each-ite