Reputation: 10701
Trying to abstract my program, I use f-bounded polymorphism to define my values. I use DataContractJsonSerializer
(no choice for that). The only problem is that apparently for generics, it throws an XmlException
saying that the type name is badly formatted (5th line from the end below).
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();
}
It throws an XmlException
or a System.InvalidOperationException
, depending on some parameters.
The '{' character, hexadecimal value 0x7B, cannot be included in a name."} System.Exception {System.Xml.XmlException}
When debug the variables I find this:
localName "TreeOf{0}{#}" string
which is tested in a function named IsValidNCName. How can I overcome these exceptions? Before using generics I never had them, but I don't want to go back.
--edit--
I tried to use new DataContractJsonSerializer(typeof (ConcTree<StringValue>));
so that it takes the correct type, without success.
Upvotes: 1
Views: 945
Reputation: 10701
After 36h of searching, I finally found the correct way. Instead of looking in the registered types in the assembly, I add a static initializer to all subclasses of Tree
which register their type.
Furthermore I have to add the generic parameter TValue
to the list of known types (see #1) if I have an advanced class hierarchy, especially for deserialization. See the last part.
[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 ));
}
}
And the same for the tree :
[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();
}
And now it works perfectly !
Upvotes: 1