I\'m sending large amounts of different JSON graphs from a server to a client (I control both) and they all contain a pathological case: a large array of homogeneous (same t
You can achieve what you want by using Custom JsonConverter
. Lets say you have the following test class:
public class MyTestClass
{
public MyTestClass(int key1, string key2, decimal key3)
{
m_key1 = key1;
m_key2 = key2;
m_key3 = key3;
}
private int m_key1;
public int Key1 { get { return m_key1; } }
private string m_key2;
public string Key2 { get { return m_key2; } }
private decimal m_key3;
public decimal Key3 { get { return m_key3; } }
}
This solution assumes that you'll work with List
all the time, but it is not tied to the type MyTestClass
. It is a generic solution that can work with any List
, but the type T has get only properties and has a constructor that sets all property values.
var list = new List
{
new MyTestClass
{
Key1 = 1,
Key2 = "Str 1",
Key3 = 8.3m
},
new MyTestClass
{
Key1 = 72,
Key2 = "Str 2",
Key3 = 134.8m
},
new MyTestClass
{
Key1 = 99,
Key2 = "Str 3",
Key3 = 91.45m
}
};
If you serialize this list with the usual JSON.NET serialization the result would be:
[{"Key1":1,"Key2":"Str 1","Key3":8.3},{"Key1":72,"Key2":"Str 2","Key3":134.8},{"Key1":99,"Key2":"Str 3","Key3":91.45}]
That's not what you expect. From what you posted, the desired result for you is:
[["Key1","Key2","Key3"],[1,"Str 1",8.3],[72,"Str 2",134.8],[99,"Str 3",91.45]]
where the first inner array represents the key names and starting from the second to the last are the values of each property of each object from the list. You can achieve this kind of serialization by writing custom JsonConverter
:
public class CustomJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (!(objectType.IsGenericType)) return null;
var deserializedList = (IList)Activator.CreateInstance(objectType);
var jArray = JArray.Load(reader);
var underlyingType = objectType.GetGenericArguments().Single();
var properties = underlyingType.GetProperties();
Type[] types = new Type[properties.Length];
for (var i = 0; i < properties.Length; i++)
{
types[i] = properties[i].PropertyType;
}
var values = jArray.Skip(1);
foreach (JArray value in values)
{
var propertiesValues = new object[properties.Length];
for (var i = 0; i < properties.Length; i++)
{
propertiesValues[i] = Convert.ChangeType(value[i], properties[i].PropertyType);
}
var constructor = underlyingType.GetConstructor(types);
var obj = constructor.Invoke(propertiesValues);
deserializedList.Add(obj);
}
return deserializedList;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (!(value.GetType().IsGenericType) || !(value is IList)) return;
var val = value as IList;
PropertyInfo[] properties = val.GetType().GetGenericArguments().Single().GetProperties();
writer.WriteStartArray();
writer.WriteStartArray();
foreach (var p in properties)
writer.WriteValue(p.Name);
writer.WriteEndArray();
foreach (var v in val)
{
writer.WriteStartArray();
foreach (var p in properties)
writer.WriteValue(v.GetType().GetProperty(p.Name).GetValue(v));
writer.WriteEndArray();
}
writer.WriteEndArray();
}
}
and use the following line for serialization:
var jsonStr = JsonConvert.SerializeObject(list, new CustomJsonConverter());
To deserialize the string into a list of objects from typeof(MyTestClass)
, use the following line:
var reconstructedList = JsonConvert.DeserializeObject>(jsonStr, new CustomJsonConverter());
You can use the CustomJsonConverter
with any generic list of objects.
Please note that this solution assumes that the order of the properties during serialization and deserialization is the same.