2014-12-15 2 views
1

Пытаясь отвлечь мою программу, я использую f-ограниченный полиморфизм для определения моих значений. Я использую DataContractJsonSerializer (для этого нет выбора). Единственная проблема заключается в том, что, по-видимому, для дженериков, он бросает XmlException, говоря, что имя типа плохо отформатировано (5-я строка с конца ниже).DataContractJsonSerializer не работает на общих/полиморфных объектах

public abstract class Value<T> where T : Value<T> { 
} 
public class StringValue : Value<StringValue> { 
    [DataMember] 
    public string S { get; set; } 
} 

[DataContract, KnownType("GetSubclasses")] 
public abstract class Tree<TValue> where TValue : Value<TValue> { 
    public static IEnumerable<Type> GetSubclasses() { 
     return from t in typeof(Tree<>).Assembly.GetTypes() 
       where typeof(Tree<>).IsAssignableFrom(t) 
      select t; 
    } 

    [DataMember] 
    public string Name; 

    protected Tree() {} 
} 

[DataContract] 
public class ConcTree<TValue> : Tree<TValue> where TValue : Value<TValue> { 
    [DataMember] 
    public TValue Value; 

    public ConcTree(string n, TValue reg) { 
     Name = n; 
     Value = reg; 
    } 
} 

var result = new ConcTree<StringValue>("test", new StringValue() { S = "s_value" }); 
var serializer = new DataContractJsonSerializer(typeof (Tree<StringValue>)); 
using (var stream = new MemoryStream()) { 
    serializer.WriteObject(stream, result); // Here XmlException 
    stream.Position = 0; 
    using (var reader = new StreamReader(stream)) 
     return reader.ReadToEnd(); 
} 

Он бросает XmlException или System.InvalidOperationException, в зависимости от некоторых параметров.

'{' характер, шестнадцатеричное значение 0x7B, не могут быть включены в имени ".} {System.Exception System.Xml.XmlException}

При отладке переменных я нахожу это:

localName  "TreeOf{0}{#}"  string 

, который испытывается в функции с именем IsValidNCName. Как преодолеть эти исключения? Перед использованием дженериков я их никогда не было, но я не хочу возвращаться.

--edit--

Я пытался использовать new DataContractJsonSerializer(typeof (ConcTree<StringValue>)); так, что он принимает правильный тип, но безуспешно.

ответ

1

После 36 часов поиска я, наконец, нашел правильный путь. Вместо поиска в зарегистрированных типах сборки я добавляю статический инициализатор ко всем подклассам Tree, которые регистрируют их тип.

Кроме того я должен добавить общий параметр TValue в список известных типов (см # 1), если у меня есть передовые иерархии классов, особенно для десериализации. См. Последнюю часть.

[DataContract, KnownType("GetKnownSubclassesOfRegion")] 
public abstract class Value<T> where T : Value<T> { 
    //Register sub-types statically. 
    protected static readonly List<Type> ValueTypes= new List<Type>(); 
    public static IEnumerable<Type> GetKnownSubclassesOfRegion() { 
     return RegionTypes; 
    } 
} 
[DataContract] 
public class StringValue : Value<StringValue> { 
    [DataMember] 
    public string S { get; set; } 
    static StringValue() { 
     ValueTypes.Add(typeof(StringValue)); 
    } 
} 

И то же самое для дерева:

[DataContract, KnownType("GetSubclasses")] 
public abstract class Tree<TValue> where TValue : Value<TValue> { 
    //Register sub-types statically with their generic parameter which is instantiated. 
    protected static readonly List<Type> RegisteredTypes = new List<Type>(); 
    public static IEnumerable<Type> GetSubclasses() {  //This is new 
     return RegisteredTypes; 
    } 
    static TreeElement() { // #1 
     RegisteredTypes.Add(typeof(TValue)); 
    } 

    [DataMember] 
    public string Name; 

    protected Tree() {} 
} 

[DataContract] 
public class ConcTree<TValue> : Tree<TValue> where TValue : Value<TValue> { 
    [DataMember] 
    public TValue Value; 

    public ConcTree(string n, TValue reg) { 
     Name = n; 
     Value = reg; 
    } 

    static ConcTree() { //This is new 
     ValueTypes.Add(typeof(ConcTree<TValue>)); 
    } 
} 

var result = new ConcTree<StringValue>("test", new StringValue() { S = "s_value" }); 
var serializer = new DataContractJsonSerializer(typeof (Tree<StringValue>)); 
using (var stream = new MemoryStream()) { 
    serializer.WriteObject(stream, result); // No exception anymore. 
    stream.Position = 0; 
    using (var reader = new StreamReader(stream)) 
     return reader.ReadToEnd(); 
} 

И теперь он отлично работает!

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