有没有办法反序列化我的JSON而不使用每个项目的包装类?

时间:2013-07-29 21:00:45

标签: asp.net json.net

我正在使用JSON.NET开发一个ASP.NET应用程序,该应用程序与第三方REST API连接。 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消除包装类?

1 个答案:

答案 0 :(得分: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]类,则上述解决方案将 NOT 有效。这是因为写入的自定义转换器使用序列化程序的第二个副本来处理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更改为匹配。< / p>