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

查看:8
本文介绍了我可以在属性中指定路径以将我的类中的属性映射到我的 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天全站免登陆