1

我正在使用与第三方 REST API 接口的 JSON.NET 开发 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 消除包装类?

4

1 回答 1

1

是的,您可以通过为您的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不再需要,可以删除。

编辑

重要提示:如果您使用该属性装饰您的类,上述解决方案将不起作用。这是因为编写的自定义转换器使用序列化程序的第二个副本来处理反序列化,一旦我们降低了一个级别以获取真实的用户数据。如果你装饰了这个类,那么序列化器的所有副本都将尝试使用转换器,你最终会得到一个自引用循环。User[JsonConverter]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的匹配。

于 2013-07-29T23:57:14.313 回答