json - JSON反序列化映射数组到自己的属性
问题描述
考虑以下第 3 方 (Magento) API 响应,其中这是产品集合中的一个条目:
{
"id":121,
"sku":"008MBLU",
"name":"Pillow Covers - King Mauve (2-pack)",
"custom_attributes":{
"11":{
"attribute_code":"ship_length",
"value":"11.0000"
},
"16":{
"attribute_code":"ship_width",
"value":"7.0000"
},
"19":{
"attribute_code":"ship_height",
"value":"1.0000"
}
}
}
并将所需的结果类反序列化为:
public class Product
{
[JsonProperty("id")]
public long Id { get; set; }
[JsonProperty("sku")]
public string SKU { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("ship_length")]
public decimal ShipLength { get; set; }
[JsonProperty("ship_width")]
public decimal ShipWidth { get; set; }
[JsonProperty("ship_height")]
public decimal ShipHeight { get; set; }
}
我发现了这篇文章,这是我需要的部分内容,它将忽略int
包装每个custom_attribute
. 但是我不知道从哪里开始使用针对该custom_attribute
属性的自定义解析器,然后将其值分配给另一个属性……而且我通常是自定义解析器的新手。
解决方案
在读取对应于 a 的 JSON 时Product
,您需要将 "custom_attributes"
属性重构为顶级 JSON 对象,在该对象中它们可以被序列化程序识别。这可以通过将 JSON 预加载到层次结构中的自定义JsonConverter
JToken
来轻松完成,然后重新构建层次结构:
public class CustomAttributeObjectConverter<T> : JsonConverter<T> where T : new()
{
public override T ReadJson(JsonReader reader, Type objectType, T existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var obj = JToken.Load(reader).ToJTokenType<JObject>();
if (obj == null)
return (T)(object)null;
var attributes = obj["custom_attributes"].RemoveFromLowestPossibleParent().ToJTokenType<JObject>();
if (attributes != null)
{
foreach (var item in attributes.PropertyValues().OfType<JObject>())
{
var name = (string)item["attribute_code"];
if (name != null)
obj.Add(name, item["value"]);
}
}
if (!hasExistingValue)
existingValue = (T)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
using (var tokenReader = obj.CreateReader())
serializer.Populate(tokenReader, existingValue);
return existingValue;
}
public override void WriteJson(JsonWriter writer, T value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanWrite { get { return false; } }
}
public static partial class JsonExtensions
{
public static TJToken ToJTokenType<TJToken>(this JToken item) where TJToken : JToken
{
var result = item as TJToken;
if (item != null)
return result;
if (item == null || item.Type == JTokenType.Null)
return null;
throw new JsonException(string.Format("Cannot cast {0} to {1}", item.Type, typeof(TJToken)));
}
public static JToken RemoveFromLowestPossibleParent(this JToken node)
{
if (node == null)
return null;
// If the parent is a JProperty, remove that instead of the token itself.
var contained = node.Parent is JProperty ? node.Parent : node;
contained.Remove();
// Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
if (contained is JProperty)
((JProperty)node.Parent).Value = null;
return node;
}
}
然后反序列化如下:
var settings = new JsonSerializerSettings
{
Converters = { new CustomAttributeObjectConverter<Product>() },
};
var product = JsonConvert.DeserializeObject<Product>(json, settings);
笔记:
演示小提琴在这里。
推荐阅读
- php - yii2 mpdf,不显示谷歌地图静态卫星视图
- sql - 如何确定多个日期值的模式以及当有多个模式时该怎么做
- c++ - 如何在静态函数中使用成员函数数组?
- javascript - 在 fullpage.js 中自动播放或滚动幻灯片
- windows - SystemParametersInfoForDPI 损坏内存
- automated-tests - 使用范围报告设置 Klov
- clojure - Why doesn't clojure core library support an index-of function?
- php - Foreach 组合成数组
- java - 使用java优化从数据库下载数据
- vba - Excel VBA - 在语句中添加粗体文本