При чтении JSON, соответствующего Product
, необходимо реструктурировать свойства "custom_attributes"
до объекта JSON верхнего уровня, где они могут быть распознаны сериализатором. Это проще всего сделать с помощью custom JsonConverter
, который предварительно загружает JSON в иерархию 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);
Примечания:
Я не пытался реализовать WriteJson
, поскольку нет способав общих чертах различайте, какие свойства должны быть понижены до объекта "custom_attributes"
при сериализации.
Если это требование, вы можете реализовать некоторый пользовательский атрибут , чтобы пометитьсоответствующие свойства.
Демонстрационная скрипка здесь .