反序列化动态属性列表

时间:2017-12-31 23:51:44

标签: c# asp.net json json.net

我的应用程序正在使用API​​,我正在尝试反序列化返回的图像数据。数据格式如下:

{
    "images":{
         "totalCount":4,
         "0":{
             "url":"file1.jpg"
         },
         "1":{
             "url":"file2.jpg"
         },
         "2":{
             "url":"file3.jpg"
        },
        "3":{
             "url":"file4.jpg"
        }
    }
}

我有这些模型类:

public class MyViewModel
{
    [JsonProperty("images")]
    public ImagesViewModel Images { get; set; }
}

public class ImagesViewModel
{
    [JsonProperty("totalCount")]
    public int TotalCount { get; set; }

    public Dictionary<string, ImageViewModel> ListImages { get; set; }
}

public class ImageViewModel
{
    [JsonProperty("url")]
    public string Url { get; set; }
}

图像集合并非真正的集合,出于某种原因,它只是每个图像的新属性。我试图反序列化我的对象,如:

... // create HttpClient object, add headers and such
System.Net.Http.HttpResponseMessage response = await 
client.GetAsync(endpointUrl);
var jsonString = response.Content.ReadAsStringAsync();
MyViewModel model = 
    JsonConvert.DeserializeObject<MyViewModel>(jsonString.Result);

我恢复totalCount属性就好了,但图像集合又回来了。

有没有办法让我更改我的视图模型,以便我可以正确反序列化json?

3 个答案:

答案 0 :(得分:3)

鉴于JSON的格式,您将不得不走很长的路线并尝试使用JObjects反序列化

//using Newtonsoft.Json.Linq
var jObject = JObject.Parse(jsonString);
var images = jObject.Property("images").Value<JObject>(); ;
var viewModel = new MyViewModel {
    Images = new ImagesViewModel {
        TotalCount = images.Property("totalCount").Value<int>(),
        ListImages = images.Properties().Skip(1).ToDictionary(p => p.Name, p => p.Value<ImageViewModel>())
    }
};

更进一步,使用JsonConverter转换有效负载本身实际上也很有效,因为我们现在知道如何转换它。

public class MyViewModelConverter : JsonConverter {

    public override bool CanConvert(Type objectType) {
        return objectType == typeof(MyViewModel);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
        var jObject = JObject.Load(reader);//<-- Note the use of Load() instead of Parse()
        var images = jObject.Property("images").Value<JObject>(); ;
        var model = new MyViewModel {
            Images = new ImagesViewModel {
                TotalCount = images.Property("totalCount").Value<int>(),
                ListImages = images.Properties().Skip(1).ToDictionary(p => p.Name, p => p.Value<ImageViewModel>())
            }
        };
        return model;
    }

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

并装饰课程本身

[JsonConverter(typeof(MyViewModelConverter))]
public class MyViewModel {
    [JsonProperty("images")]
    public ImagesViewModel Images { get; set; }
}

反序列化现在就像你打算在

之前做的那样
var jsonString = await response.Content.ReadAsStringAsync();
MyViewModel model = JsonConvert.DeserializeObject<MyViewModel>(jsonString);

答案 1 :(得分:0)

.NET Abhors动态类型。它们在编译时面对固体类型检查。话虽如此,有人支持它:

由于示例数据基本上只是一个图像数组,因此任何集合都可以处理此输入。

如果你甚至无法定义类型(你可能有一个图像数组和一个字符串),唯一的方法是ExpandoObject。它专门用于处理此类案件。它基本上是一个带有一些Syntax Sugar的List [string,object],但它也包含了Property Change Notifications等功能。

答案 2 :(得分:0)

听起来像是自定义转换器的工作!

自定义转换器将允许您提供自己的逻辑以反序列化特定类型。如果期望在json中找到并调用适当的转换器,Newtonsoft使用目标类来计算类型。

    class ImagesViewModelConverter : JsonConverter
    {
        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(ImagesViewModel);
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            assertToken(JsonToken.StartObject);

            var obj = new ImagesViewModel()
            {
                ListImages = new Dictionary<string, ImageViewModel>()
            };

            while (reader.Read() && reader.TokenType != JsonToken.EndObject)
            {
                assertToken(JsonToken.PropertyName);
                var propName = (string)reader.Value;
                if (propName.Equals(nameof(ImagesViewModel.TotalCount), StringComparison.InvariantCultureIgnoreCase))
                {
                    reader.Read();
                    assertToken(JsonToken.Integer);
                    obj.TotalCount = (int)((Int64)reader.Value);
                    continue;
                }
                reader.Read();
                var image = serializer.Deserialize<ImageViewModel>(reader); // you can still use normal json deseralization inside a converter

                obj.ListImages.Add(propName, image);
            }

            return obj;

            void assertToken(JsonToken token)
            {
                if (reader.TokenType != token)
                    throw new Exception(); // might wanna add detailed errors
            }
        }

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

然后:

        var settings = new JsonSerializerSettings()
        {
            Converters = new[] { new ImagesViewModelConverter() }
        };
        var obj = JsonConvert.DeserializeObject<MyViewModel>(jsonString, settings);
        });

您甚至可以更改类更容易处理,因为它们不再需要完全匹配json。例如,您可以用数组替换dict并让转换器按顺序填充它。