Сбой DataContractJsonSerializer для универсальных / полиморфных объектов

1

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

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 или XmlException System.InvalidOperationException, в зависимости от некоторых параметров.

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

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

    localName       "TreeOf{0}{#}"        string

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

--редактировать--

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

Теги:
generics
datacontractserializer

1 ответ

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();
}

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню