我可以在属性中指定路径以将类中的属性映射到 JSON 中的子属性吗? [英] Can I specify a path in an attribute to map a property in my class to a child property in my JSON?

查看:15
本文介绍了我可以在属性中指定路径以将类中的属性映射到 JSON 中的子属性吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有一些代码(我无法更改)使用 Newtonsoft.Json 的 DeserializeObject<T>(strJSONData) 从 Web 请求中获取数据并将其转换为类对象(我可以换班).通过使用 [DataMember(Name = "raw_property_name")] 装饰我的类属性,我可以将原始 JSON 数据映射到我的类中的正确属性.有没有办法可以将 JSON 复杂对象的子属性映射到简单属性?举个例子:

There is some code (which I can't change) that uses Newtonsoft.Json's DeserializeObject<T>(strJSONData) to take data from a web request and convert it to a class object (I can change the class). By decorating my class properties with [DataMember(Name = "raw_property_name")] I can map the raw JSON data to the correct property in my class. Is there a way I can map the child property of a JSON complex object to a simple property? Here's an example:

{
    "picture": 
    {
        "id": 123456,
        "data": 
        {
            "type": "jpg",
            "url": "http://www.someplace.com/mypicture.jpg"
        }
    }
}

除了 URL,我不关心图片对象的任何其余部分,因此不想在我的 C# 类中设置复杂的对象.我真的只想要这样的东西:

I don't care about any of the rest of the picture object except for URL, and so don't want to setup a complex object in my C# class. I really just want something like:

[DataMember(Name = "picture.data.url")]
public string ProfilePicture { get; set; }

这可能吗?

推荐答案

好吧,如果您只需要一个额外的属性,一个简单的方法是将您的 JSON 解析为 JObject,使用 ToObject()JObject 填充您的类,然后使用 SelectToken() 拉入额外的属性.

Well, if you just need a single extra property, one simple approach is to parse your JSON to a JObject, use ToObject() to populate your class from the JObject, and then use SelectToken() to pull in the extra property.

所以,假设你的类看起来像这样:

So, assuming your class looked something like this:

class Person
{
    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("age")]
    public string Age { get; set; }

    public string ProfilePicture { get; set; }
}

你可以这样做:

string json = @"
{
    ""name"" : ""Joe Shmoe"",
    ""age"" : 26,
    ""picture"":
    {
        ""id"": 123456,
        ""data"":
        {
            ""type"": ""jpg"",
            ""url"": ""http://www.someplace.com/mypicture.jpg""
        }
    }
}";

JObject jo = JObject.Parse(json);
Person p = jo.ToObject<Person>();
p.ProfilePicture = (string)jo.SelectToken("picture.data.url");

小提琴:https://dotnetfiddle.net/7gnJCK

如果您更喜欢更奇特的解决方案,您可以创建一个自定义 JsonConverter 以启用 JsonProperty 属性,使其表现得像您描述的那样.转换器需要在类级别运行,并使用一些反射结合上述技术来填充所有属性.下面是它在代码中的样子:

If you prefer a more fancy solution, you could make a custom JsonConverter to enable the JsonProperty attribute to behave like you describe. The converter would need to operate at the class level and use some reflection combined with the above technique to populate all the properties. Here is what it might look like in code:

class JsonPathConverter : JsonConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, 
                                    object existingValue, JsonSerializer serializer)
    {
        JObject jo = JObject.Load(reader);
        object targetObj = Activator.CreateInstance(objectType);

        foreach (PropertyInfo prop in objectType.GetProperties()
                                                .Where(p => p.CanRead && p.CanWrite))
        {
            JsonPropertyAttribute att = prop.GetCustomAttributes(true)
                                            .OfType<JsonPropertyAttribute>()
                                            .FirstOrDefault();

            string jsonPath = (att != null ? att.PropertyName : prop.Name);
            JToken token = jo.SelectToken(jsonPath);

            if (token != null && token.Type != JTokenType.Null)
            {
                object value = token.ToObject(prop.PropertyType, serializer);
                prop.SetValue(targetObj, value, null);
            }
        }

        return targetObj;
    }

    public override bool CanConvert(Type objectType)
    {
        // CanConvert is not called when [JsonConverter] attribute is used
        return false;
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override void WriteJson(JsonWriter writer, object value,
                                   JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

为了演示,我们假设 JSON 现在如下所示:

To demonstrate, let's assume the JSON now looks like the following:

{
  "name": "Joe Shmoe",
  "age": 26,
  "picture": {
    "id": 123456,
    "data": {
      "type": "jpg",
      "url": "http://www.someplace.com/mypicture.jpg"
    }
  },
  "favorites": {
    "movie": {
      "title": "The Godfather",
      "starring": "Marlon Brando",
      "year": 1972
    },
    "color": "purple"
  }
}

...除了之前的信息之外,您还对这个人最喜欢的电影(片名和年份)和最喜欢的颜色感兴趣.您将首先使用 [JsonConverter] 属性标记目标类以将其与自定义转换器关联,然后在每个属性上使用 [JsonProperty] 属性,指定所需的属性路径(区分大小写)作为名称.目标属性也不必是基元——您可以像我在这里对 Movie 所做的那样使用子类(注意不需要介入 Favorites 类).

...and you are interested in the person's favorite movie (title and year) and favorite color in addition to the information from before. You would first mark your target class with a [JsonConverter] attribute to associate it with the custom converter, then use [JsonProperty] attributes on each property, specifying the desired property path (case sensitive) as the name. The target properties don't have to be primitives either-- you can use a child class like I did here with Movie (and notice there's no intervening Favorites class required).

[JsonConverter(typeof(JsonPathConverter))]
class Person
{
    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("age")]
    public int Age { get; set; }

    [JsonProperty("picture.data.url")]
    public string ProfilePicture { get; set; }

    [JsonProperty("favorites.movie")]
    public Movie FavoriteMovie { get; set; }

    [JsonProperty("favorites.color")]
    public string FavoriteColor { get; set; }
}

// Don't need to mark up these properties because they are covered by the 
// property paths in the Person class
class Movie
{
    public string Title { get; set; }
    public int Year { get; set; }
}

所有属性都到位后,您可以正常反序列化,它应该正常工作":

With all the attributes in place, you can just deserialize as normal and it should "just work":

Person p = JsonConvert.DeserializeObject<Person>(json);

小提琴:https://dotnetfiddle.net/Ljw32O

这篇关于我可以在属性中指定路径以将类中的属性映射到 JSON 中的子属性吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆