一尘不染

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

c#

有一些使用Newtonsoft.Json的代码(我无法更改),它DeserializeObject<T>(strJSONData)从Web请求中获取数据并将其转换为类对象(我可以更改类)。通过使用修饰类的属性,`[DataMember(Name

“raw_property_name”)]`可以将原始JSON数据映射到类中的正确属性。有没有一种方法可以将JSON复杂对象的child属性映射到简单属性?这是一个例子:

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

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

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

这可能吗?


阅读 161

收藏
2020-05-19

共1个答案

一尘不染

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

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

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属性表现出您所描述的行为。转换器将需要在类级别上运行,并结合使用上述技术和某些技术来填充所有属性。这是代码中的样子:

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现在如下所示:

{
  "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类)。

[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; }
}

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

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

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

2020-05-19