我可以在属性中指定路径以将我的类中的属性映射到我的 JSON 中的子属性吗?

Can I specify a path in an attribute to map a property in my class to a child property in my JSON?(我可以在属性中指定路径以将我的类中的属性映射到我的 JSON 中的子属性吗?)
本文介绍了我可以在属性中指定路径以将我的类中的属性映射到我的 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 中的子属性吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

本站部分内容来源互联网,如果有图片或者内容侵犯了您的权益,请联系我们,我们会在确认后第一时间进行删除!

相关文档推荐

Force JsonConvert.SerializeXmlNode to serialize node value as an Integer or a Boolean(强制 JsonConvert.SerializeXmlNode 将节点值序列化为整数或布尔值)
Using JSON to Serialize/Deserialize TimeSpan(使用 JSON 序列化/反序列化 TimeSpan)
Could not determine JSON object type for type quot;Classquot;(无法确定类型“Class的 JSON 对象类型.)
How to deserialize a JSONP response (preferably with JsonTextReader and not a string)?(如何反序列化 JSONP 响应(最好使用 JsonTextReader 而不是字符串)?)
how to de-serialize JSON data in which Timestamp it-self contains fields?(如何反序列化时间戳本身包含字段的JSON数据?)
JSON.Net custom contract serialization and Collections(JSON.Net 自定义合约序列化和集合)