Пытаясь отвлечь мою программу, я использую 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>));
так что он принимает правильный тип, без успеха.
После 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();
}
И теперь он отлично работает!