关于c#:是否可以在属性中指定路径,以将类中的属性映射到JSON中的子属性?

Can I specify a path in an attribute to map a property in my class to a child property in my JSON?

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

1
2
3
4
5
6
7
8
9
10
11
{
   "picture":
    {
       "id": 123456,
       "data":
        {
           "type":"jpg",
           "url":"http://www.someplace.com/mypicture.jpg"
        }
    }
}

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

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

这可能吗?


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

因此,假设您的课程如下所示:

1
2
3
4
5
6
7
8
9
10
class Person
{
    [JsonProperty("name")]
    public string Name { get; set; }

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

    public string ProfilePicture { get; set; }
}

您可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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属性,使其表现出与您描述的相同的效果。转换器将需要在类级别上运行,并结合使用上述技术和某些技术来填充所有属性。这是代码中的样子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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现在如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
 "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类)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
[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; }
}

有了所有属性,您就可以照常反序列化了,它应该"正常工作":

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

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