Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Converter and attributes to compact a list and embed properties of immediately contained objects
- // Sample at bottom
- class ListCompactionConverter : JsonConverter {
- private readonly HashSet<Type> _knownGoodTypes = new HashSet<Type>();
- private readonly HashSet<Type> _knownBadTypes = new HashSet<Type>();
- public override bool CanConvert(Type objectType) {
- if (_knownBadTypes.Contains(objectType)) return false;
- if (_knownGoodTypes.Contains(objectType)) return true;
- Type listType = typeof(List<>);
- // or if immutable: listType = typeof(ImmutableList<>)
- if (objectType.IsGenericType && objectType.GetGenericTypeDefinition() == listType) {
- Type itemType = objectType.GetGenericArguments().Single();
- if (itemType.IsClass && !IsStringOrStringable(itemType) && !typeof(IEnumerable).IsAssignableFrom(itemType)) {
- _knownGoodTypes.Add(objectType);
- return true;
- }
- }
- _knownBadTypes.Add(objectType);
- return false;
- }
- private bool IsStringOrStringable(Type type) {
- return type == typeof(string); // or is of any other type that will be converted to a string by any other JsonConverter
- }
- private void StuffWithEmbeds(JObject obj, Type type) {
- var props = type.GetProperties();
- foreach (var prop in props) {
- var embed =
- prop.GetCustomAttributes(typeof(JsonEmbedAttribute), inherit: false)
- .OfType<JsonEmbedAttribute>()
- .FirstOrDefault();
- if (embed == null) continue;
- var fullPrefix = embed.Prefix + "_";
- var propObj = obj[prop.Name] as JObject;
- if (propObj == null) continue;
- foreach (var objProp in propObj.Properties()) {
- obj[fullPrefix + objProp.Name] = objProp.Value;
- }
- obj.Remove(prop.Name);
- }
- }
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
- JArray array = new JArray();
- IList list = (IList)value;
- if (list.Count > 0) {
- JArray keys = new JArray();
- JObject first = JObject.FromObject(list[0], serializer);
- StuffWithEmbeds(first, list[0].GetType());
- foreach (JProperty prop in first.Properties()) {
- keys.Add(new JValue(prop.Name));
- }
- array.Add(keys);
- foreach (object item in list) {
- JObject obj = JObject.FromObject(item, serializer);
- StuffWithEmbeds(obj, item.GetType());
- JArray itemValues = new JArray();
- foreach (JProperty prop in obj.Properties()) {
- itemValues.Add(prop.Value);
- }
- array.Add(itemValues);
- }
- }
- array.WriteTo(writer);
- }
- private object ToListOfElementType(IEnumerable initialElements, Type elementType) {
- var nonspecificListType = typeof(List<>);
- var specificListType = nonspecificListType.MakeGenericType(elementType);
- return Activator.CreateInstance(specificListType, initialElements);
- }
- /* or if immutable
- private object ToListOfElementType(IEnumerable initialElements, Type elementType) {
- var iml = typeof(ImmutableList);
- var notspecificCreateRangeMethod = iml.GetMethod("CreateRange", BindingFlags.Public | BindingFlags.Static);
- var specificCreateRangeMethod = notspecificCreateRangeMethod.MakeGenericMethod(elementType);
- var enumerableType = typeof(Enumerable);
- var notspecificOfTypeMethod = enumerableType.GetMethod("OfType", BindingFlags.Public | BindingFlags.Static);
- var specificOfTypeMethod = notspecificOfTypeMethod.MakeGenericMethod(elementType);
- var typedEnumerable = specificOfTypeMethod.Invoke(null, new[] { initialElements });
- var builtInstance = specificCreateRangeMethod.Invoke(null, new[] { typedEnumerable });
- return builtInstance;
- }*/
- public void ExtractEmbeds(JObject obj, Type type) {
- var props = type.GetProperties();
- foreach (var prop in props) {
- var embed =
- prop.GetCustomAttributes(typeof(JsonEmbedAttribute), inherit: false)
- .OfType<JsonEmbedAttribute>()
- .FirstOrDefault();
- if (embed == null) continue;
- var fullPrefix = embed.Prefix + "_";
- var subProps = obj.Properties().Where(jp => jp.Name.StartsWith(fullPrefix)).ToArray();
- if (subProps.Any()) {
- var subObj = new JObject();
- foreach (var subProp in subProps) {
- var cleanedName = subProp.Name.Substring(fullPrefix.Length);
- subObj[cleanedName] = subProp.Value;
- obj.Remove(subProp.Name);
- }
- obj.Add(prop.Name, subObj);
- }
- }
- }
- public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
- IList list = new List<object>();
- JArray array = JArray.Load(reader);
- if (array.Count > 0) {
- Type itemType = objectType.GetGenericArguments().Single();
- JArray keys = (JArray)array[0];
- foreach (JArray itemValues in array.Children<JArray>().Skip(1)) {
- JObject item = new JObject();
- for (int i = 0; i < keys.Count; i++) {
- item.Add(new JProperty(keys[i].ToString(), itemValues[i]));
- }
- ExtractEmbeds(item, itemType);
- list.Add(item.ToObject(itemType, serializer));
- }
- }
- var elementType = objectType.GetGenericArguments()[0];
- var finalList = ToListOfElementType(list, elementType);
- return finalList;
- }
- }
- [AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
- internal sealed class JsonEmbedAttribute : Attribute {
- private readonly string _prefix;
- public string Prefix {
- get { return _prefix; }
- }
- public JsonEmbedAttribute(string prefix) {
- _prefix = prefix;
- }
- }
- // Sample usage:
- var example = new ExampleModel(new List<PersonModel> {
- new PersonModel(new DateTime(1954, 9, 27), "Larry Wall", new CarModel("Volvo", 1988)),
- new PersonModel(new DateTime(1960, 12, 1), "Anders Hejlsberg", new CarModel("Renault", 2001)),
- new PersonModel(new DateTime(1950, 12, 30), "Bjarne Stroustrup", new CarModel("Ford", 1908))
- }, DateTime.Now);
- var listCompactionConverter = new ListCompactionConverter();
- var jsonString = JsonConvert.SerializeObject(example, listCompactionConverter);
- var reserialized = JsonConvert.DeserializeObject<ExampleModel>(jsonString, listCompactionConverter);
- class CarModel {
- readonly string _make;
- readonly int _year;
- public CarModel(string make, int year) {
- _make = make;
- _year = year;
- }
- public string Make { get { return _make; } }
- public int Year { get { return _year; } }
- }
- class PersonModel {
- readonly DateTime _birthday;
- readonly string _name;
- readonly CarModel _car;
- public PersonModel(DateTime birthday, string name, CarModel car) {
- _birthday = birthday;
- _name = name;
- _car = car;
- }
- public DateTime Birthday { get { return _birthday; } }
- public string Name { get { return _name; } }
- [JsonEmbed("c")]
- public CarModel Car { get { return _car; } }
- }
- class ExampleModel {
- readonly List<PersonModel> _people;
- readonly DateTime _updated;
- public ExampleModel(List<PersonModel> people, DateTime updated) {
- _people = people;
- _updated = updated;
- }
- public List<PersonModel> People { get { return _people; } }
- public DateTime Updated { get { return _updated; } }
- }
Advertisement
Add Comment
Please, Sign In to add comment