using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public long Id { get; set; }
public string SKU { get; set; }
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; }
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>();
var attributes = obj["custom_attributes"].RemoveFromLowestPossibleParent().ToJTokenType<JObject>();
foreach (var item in attributes.PropertyValues().OfType<JObject>())
var name = (string)item["attribute_code"];
obj.Add(name, item["value"]);
existingValue = (T)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
using (var tokenReader = obj.CreateReader())
serializer.Populate(tokenReader, 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 || item.Type == JTokenType.Null)
throw new JsonException(string.Format("Cannot cast {0} to {1}", item.Type, typeof(TJToken)));
public static JToken RemoveFromLowestPossibleParent(this JToken node)
var contained = node.Parent is JProperty ? node.Parent : node;
if (contained is JProperty)
((JProperty)node.Parent).Value = null;
public static void Test()
var settings = new JsonSerializerSettings
Converters = { new CustomAttributeObjectConverter<Product>() },
var product = JsonConvert.DeserializeObject<Product>(json, settings);
var json2 = JsonConvert.SerializeObject(product, Formatting.Indented, settings);
Console.WriteLine(json2);
Assert.IsTrue(product.ShipLength == (decimal)JToken.Parse(json).SelectToken("..custom_attributes..[?(@.attribute_code == 'ship_length')].value"));
Assert.IsTrue(product.ShipHeight == (decimal)JToken.Parse(json).SelectToken("..custom_attributes..[?(@.attribute_code == 'ship_height')].value"));
Assert.IsTrue(product.ShipWidth == (decimal)JToken.Parse(json).SelectToken("..custom_attributes..[?(@.attribute_code == 'ship_width')].value"));
""name"":""Pillow Covers - King Mauve (2-pack)"",
""attribute_code"":""ship_length"",
""attribute_code"":""ship_width"",
""attribute_code"":""ship_height"",
public static void Main()
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");
public static string GetNetCoreVersion()
var assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
var assemblyPath = assembly.CodeBase.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];