DataContractJsonSerializer在泛型/多态对象上失败

时间:2014-12-15 17:26:35

标签: c# generics datacontractserializer

尝试抽象我的程序,我使用f-bounded多态来定义我的值。我使用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();
}

它会抛出XmlExceptionSystem.InvalidOperationException,具体取决于某些参数。

  

'{'字符,十六进制值0x7B,不能包含在名称中。“} System.Exception {System.Xml.XmlException}

调试变量时我会发现:

    localName       "TreeOf{0}{#}"        string

在名为IsValidNCName的函数中测试。 我怎样才能克服这些异常?在使用泛型之前我从来没有过它们,但我不想再回去了。

- 编辑 -

我尝试使用new DataContractJsonSerializer(typeof (ConcTree<StringValue>));以便它采用正确的类型,但没有成功。

1 个答案:

答案 0 :(得分: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();
}

现在它完美无缺!