反序列化动态属性列表



我的应用程序正在使用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?

鉴于 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);
.

NET 憎恶动态类型。它们在编译时面对固体类型检查。话虽如此,还是有支持的:

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

如果你甚至不能定义类型(你可能有一个图像数组和一个字符串(,唯一的方法是 ExpandoObject。它是专门为处理此类情况而设计的。它基本上是一个带有一些语法糖的List[string,对象],但它也包括属性更改通知等功能。

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

自定义转换器将允许您提供自己的逻辑来反序列化特定类型。Newtonsoft 使用目标类来确定类型是否期望在 json 中找到并调用相应的转换器。

    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。例如,您可以将字典替换为数组,并让转换器按顺序填充它。

最新更新