2014-01-22 3 views
3

У меня есть класс, который выглядит следующим образом:JSON сериализации дженериков

public class MyData : IList<Tuple<double,double>> 

Идея заключается в том, что у вас есть список пар значений. Достаточно просто. Но я хотел, чтобы это было сериализовано так, чтобы оно выглядело как массив массива двойников (т. Е. double[][]), а не список кортежей. Он должен выглядеть следующим образом сериализовать:

[[1,1],[2,2],[3,3]] 

Так что я создал простой JsonConverter, что будет делать это. Она имеет очень простой WriteJson метод, который выглядит следующим образом:

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
{ 
    var obj = value as MyData; 
    double[][] dataArray = (from dp in obj._data 
          select new[] { dp.Item1, dp.Item2 }).ToArray(); 
    var ser = new JsonSerializer(); 
    ser.Serialize(writer, dataArray); 
} 

И я декорировать MyData с соответствующим атрибутом:

[JsonConverter(typeof(MyDataJsonConverter))] 
    public class MyData: IList<Tuple<double,double>> 

И все работает отлично. Теперь же я хочу продлить это и сделать MyData родовое:

public class MyData<S,T>: IList<Tuple<S,T>> 

И это был я столкнуться с проблемами. Сначала я пытался сделать MyDataJsonConverter также родовое:

[JsonConverter(typeof(MyDataJsonConverter<S,T>))] 
    public class MyData<S,T>: IList<Tuple<S,T>> 

Но это не допускается, так как вы не можете использовать дженерики с атрибутами.

Так что я должен держать MyDataJsonConverter необщего, но мне нужно, чтобы выяснить, как сгладить мою коллекцию кортежей в массив (предположительно object[][] сейчас) таким образом, что, когда я сериализации мои данные будут выглядеть следующим образом:

[[1,2],[2,3]] 

или:

[["foo":1],["bar":2]] 

или даже:

[["foo":"bar"],["what":"ever"]] 

Любые идеи о том, как с этим справиться? В WriteJson я больше не могу отличать value до MyData, потому что я не буду знать параметры типа, поэтому все просто сильно разваливается в этой точке.

Я мог бы просто создать отдельные классы для каждой комбинации типов (поэтому один для Tuple<double,double> и один для Tuple<string,double> и т. Д.), Но мне было интересно, есть ли лучший способ, прежде чем я нахожу грубую силу.

+0

Почему 'в obj._data' и не' в obj'? Что такое '_data'? Во втором случае вы можете просто использовать 'IEnumerable ' и использовать LINQ в любом случае. – BartoszKP

+0

@BartoszKP: или даже вызвать негенерический 'GetEnumerator()'. –

+0

@WiktorZychla Чтобы использовать LINQ, вам все равно придется преобразовать его в 'IEnumerable ' впоследствии. – BartoszKP

ответ

3

Хорошо, так вот в основном то, что я закончил делать (в случае, если кому-то еще нужно что-то подобное). Моя коллекция выглядит примерно так:

[JsonConverter(typeof(MyDataJsonConverter))] 
    public class MyData<S,T> : IList<Tuple<S,T>> 
    { 
     private List<Tuple<S, T>> _data; 
     // all the implementation of IList... 
    } 

И мой пользовательский преобразователь (теперь уже не вложенный MyData) выглядит следующим образом:

internal class MyDataJsonConverter : JsonConverter 
{ 

    public override bool CanConvert(Type objectType) 
    { 
     return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(MyData<,>); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     // Note: this is strictly for serializing, deserializing is a whole other 
     // ball of wax that I don't currently need! 
     throw new NotImplementedException(); 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     var obj = value as IEnumerable<dynamic>; 
     object[][] dataArray = (from dp in obj 
           select new object[] { dp.Item1, dp.Item2 }).ToArray(); 
     var ser = new JsonSerializer(); 
     ser.Serialize(writer, dataArray); 
    } 

    public override bool CanRead 
    { 
     get 
     { 
      return false; 
     } 
    } 
} 
1

Первое, что нужно сделать, это использовать in obj, не связывать себя с частными членами класса, а с интерфейсом, который он предоставляет. Тогда единственная проблема, с которой вы столкнулись, - сериализовать Tuple<S, T> с помощью ссылки object с неизвестными S и T. Вы можете сделать это следующим образом, например:

public static Tuple<object, object> UnpackUnknownTuple(object tuple) 
{ 
    var item1Property = tuple.GetType().GetProperty("Item1"); 
    var item2Property = tuple.GetType().GetProperty("Item2"); 

    return Tuple.Create(
     item1Property.GetValue(tuple, null), 
     item2Property.GetValue(tuple, null)); 
} 

public override void WriteJson(
    JsonWriter writer, 
    object value, 
    JsonSerializer serializer) 
{ 
    var obj = value as IEnumerable; 

    var tuples = obj.Cast<object>().Select(UnpackUnknownTuple); 

    object[][] dataArray = (from dp in tuples 
          select new[] { dp.Item1, dp.Item2 }).ToArray(); 

    var ser = new JsonSerializer(); 
    ser.Serialize(writer, dataArray); 
} 

Другая возможность заключается в том, чтобы преобразовать в string вместо object, но я предполагаю, что это может помешать некоторые настройки сериализации. item1Property и item2Property могут быть кэшированы с использованием первого элемента списка, если есть проблемы с производительностью.

Смежные вопросы