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