Guest User

Compacting and embedding JsonConverter

a guest
Aug 19th, 2014
1,301
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 9.30 KB | None | 0 0
  1. // Converter and attributes to compact a list and embed properties of immediately contained objects
  2. // Sample at bottom
  3.  
  4.         class ListCompactionConverter : JsonConverter {
  5.  
  6.             private readonly HashSet<Type> _knownGoodTypes = new HashSet<Type>();
  7.             private readonly HashSet<Type> _knownBadTypes = new HashSet<Type>();
  8.  
  9.             public override bool CanConvert(Type objectType) {
  10.                 if (_knownBadTypes.Contains(objectType)) return false;
  11.                 if (_knownGoodTypes.Contains(objectType)) return true;
  12.  
  13.                 Type listType = typeof(List<>);
  14.                 // or if immutable: listType = typeof(ImmutableList<>)
  15.  
  16.                 if (objectType.IsGenericType && objectType.GetGenericTypeDefinition() == listType) {
  17.                     Type itemType = objectType.GetGenericArguments().Single();
  18.                     if (itemType.IsClass && !IsStringOrStringable(itemType) && !typeof(IEnumerable).IsAssignableFrom(itemType)) {
  19.                         _knownGoodTypes.Add(objectType);
  20.                         return true;
  21.                     }
  22.                 }
  23.                 _knownBadTypes.Add(objectType);
  24.                 return false;
  25.             }
  26.  
  27.             private bool IsStringOrStringable(Type type) {
  28.                 return type == typeof(string); // or is of any other type that will be converted to a string by any other JsonConverter
  29.             }
  30.  
  31.             private void StuffWithEmbeds(JObject obj, Type type) {
  32.                 var props = type.GetProperties();
  33.                 foreach (var prop in props) {
  34.                     var embed =
  35.                         prop.GetCustomAttributes(typeof(JsonEmbedAttribute), inherit: false)
  36.                             .OfType<JsonEmbedAttribute>()
  37.                             .FirstOrDefault();
  38.                     if (embed == null) continue;
  39.  
  40.                     var fullPrefix = embed.Prefix + "_";
  41.  
  42.                     var propObj = obj[prop.Name] as JObject;
  43.                     if (propObj == null) continue;
  44.                     foreach (var objProp in propObj.Properties()) {
  45.                         obj[fullPrefix + objProp.Name] = objProp.Value;
  46.                     }
  47.                     obj.Remove(prop.Name);
  48.                 }
  49.             }
  50.  
  51.             public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
  52.                 JArray array = new JArray();
  53.                 IList list = (IList)value;
  54.                 if (list.Count > 0) {
  55.                     JArray keys = new JArray();
  56.  
  57.                     JObject first = JObject.FromObject(list[0], serializer);
  58.                     StuffWithEmbeds(first, list[0].GetType());
  59.                     foreach (JProperty prop in first.Properties()) {
  60.                         keys.Add(new JValue(prop.Name));
  61.                     }
  62.                     array.Add(keys);
  63.  
  64.                     foreach (object item in list) {
  65.                         JObject obj = JObject.FromObject(item, serializer);
  66.                         StuffWithEmbeds(obj, item.GetType());
  67.                         JArray itemValues = new JArray();
  68.                         foreach (JProperty prop in obj.Properties()) {
  69.                             itemValues.Add(prop.Value);
  70.                         }
  71.                         array.Add(itemValues);
  72.                     }
  73.                 }
  74.                 array.WriteTo(writer);
  75.             }
  76.  
  77.             private object ToListOfElementType(IEnumerable initialElements, Type elementType) {
  78.                 var nonspecificListType = typeof(List<>);
  79.                 var specificListType = nonspecificListType.MakeGenericType(elementType);
  80.                 return Activator.CreateInstance(specificListType, initialElements);
  81.             }
  82.  
  83.             /* or if immutable
  84.             private object ToListOfElementType(IEnumerable initialElements, Type elementType) {
  85.                 var iml = typeof(ImmutableList);
  86.                 var notspecificCreateRangeMethod = iml.GetMethod("CreateRange", BindingFlags.Public | BindingFlags.Static);
  87.                 var specificCreateRangeMethod = notspecificCreateRangeMethod.MakeGenericMethod(elementType);
  88.                 var enumerableType = typeof(Enumerable);
  89.                 var notspecificOfTypeMethod = enumerableType.GetMethod("OfType", BindingFlags.Public | BindingFlags.Static);
  90.                 var specificOfTypeMethod = notspecificOfTypeMethod.MakeGenericMethod(elementType);
  91.                 var typedEnumerable = specificOfTypeMethod.Invoke(null, new[] { initialElements });
  92.  
  93.                 var builtInstance = specificCreateRangeMethod.Invoke(null, new[] { typedEnumerable });
  94.  
  95.                 return builtInstance;
  96.             }*/
  97.  
  98.             public void ExtractEmbeds(JObject obj, Type type) {
  99.                 var props = type.GetProperties();
  100.  
  101.                 foreach (var prop in props) {
  102.                     var embed =
  103.                         prop.GetCustomAttributes(typeof(JsonEmbedAttribute), inherit: false)
  104.                             .OfType<JsonEmbedAttribute>()
  105.                             .FirstOrDefault();
  106.                     if (embed == null) continue;
  107.  
  108.                     var fullPrefix = embed.Prefix + "_";
  109.  
  110.                     var subProps = obj.Properties().Where(jp => jp.Name.StartsWith(fullPrefix)).ToArray();
  111.                     if (subProps.Any()) {
  112.                         var subObj = new JObject();
  113.                         foreach (var subProp in subProps) {
  114.                             var cleanedName = subProp.Name.Substring(fullPrefix.Length);
  115.                             subObj[cleanedName] = subProp.Value;
  116.                             obj.Remove(subProp.Name);
  117.                         }
  118.  
  119.                         obj.Add(prop.Name, subObj);
  120.                     }
  121.                 }
  122.             }
  123.  
  124.  
  125.             public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
  126.                 IList list = new List<object>();
  127.                 JArray array = JArray.Load(reader);
  128.                 if (array.Count > 0) {
  129.                     Type itemType = objectType.GetGenericArguments().Single();
  130.  
  131.                     JArray keys = (JArray)array[0];
  132.                     foreach (JArray itemValues in array.Children<JArray>().Skip(1)) {
  133.                         JObject item = new JObject();
  134.                         for (int i = 0; i < keys.Count; i++) {
  135.                             item.Add(new JProperty(keys[i].ToString(), itemValues[i]));
  136.                         }
  137.                         ExtractEmbeds(item, itemType);
  138.  
  139.                         list.Add(item.ToObject(itemType, serializer));
  140.                     }
  141.                 }
  142.  
  143.                 var elementType = objectType.GetGenericArguments()[0];
  144.  
  145.                 var finalList = ToListOfElementType(list, elementType);
  146.  
  147.                 return finalList;
  148.             }
  149.         }
  150.  
  151.         [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
  152.         internal sealed class JsonEmbedAttribute : Attribute {
  153.             private readonly string _prefix;
  154.  
  155.             public string Prefix {
  156.                 get { return _prefix; }
  157.             }
  158.  
  159.             public JsonEmbedAttribute(string prefix) {
  160.                 _prefix = prefix;
  161.             }
  162.         }
  163.  
  164.  
  165. // Sample usage:
  166.         var example = new ExampleModel(new List<PersonModel> {
  167.             new PersonModel(new DateTime(1954, 9, 27), "Larry Wall", new CarModel("Volvo", 1988)),
  168.             new PersonModel(new DateTime(1960, 12, 1), "Anders Hejlsberg", new CarModel("Renault", 2001)),
  169.             new PersonModel(new DateTime(1950, 12, 30), "Bjarne Stroustrup", new CarModel("Ford", 1908))
  170.         }, DateTime.Now);
  171.  
  172.         var listCompactionConverter = new ListCompactionConverter();
  173.         var jsonString = JsonConvert.SerializeObject(example, listCompactionConverter);
  174.         var reserialized = JsonConvert.DeserializeObject<ExampleModel>(jsonString, listCompactionConverter);
  175.  
  176.         class CarModel {
  177.             readonly string _make;
  178.             readonly int _year;
  179.             public CarModel(string make, int year) {
  180.                 _make = make;
  181.                 _year = year;
  182.             }
  183.             public string Make { get { return _make; } }
  184.             public int Year { get { return _year; } }
  185.         }
  186.  
  187.         class PersonModel {
  188.             readonly DateTime _birthday;
  189.             readonly string _name;
  190.             readonly CarModel _car;
  191.             public PersonModel(DateTime birthday, string name, CarModel car) {
  192.                 _birthday = birthday;
  193.                 _name = name;
  194.                 _car = car;
  195.             }
  196.  
  197.             public DateTime Birthday { get { return _birthday; } }
  198.             public string Name { get { return _name; } }
  199.  
  200.             [JsonEmbed("c")]
  201.             public CarModel Car { get { return _car; } }
  202.         }
  203.  
  204.         class ExampleModel {
  205.             readonly List<PersonModel> _people;
  206.             readonly DateTime _updated;
  207.             public ExampleModel(List<PersonModel> people, DateTime updated) {
  208.                 _people = people;
  209.                 _updated = updated;
  210.             }
  211.             public List<PersonModel> People { get { return _people; } }
  212.             public DateTime Updated { get { return _updated; } }
  213.         }
Advertisement
Add Comment
Please, Sign In to add comment