26

我有以下课程

[XmlRoot]
public class AList
{
   public List<B> ListOfBs {get; set;}
}

public class B
{
   public string BaseProperty {get; set;}
}

public class C : B
{
    public string SomeProperty {get; set;}
}

public class Main
{
    public static void Main(string[] args)
    {
        var aList = new AList();
        aList.ListOfBs = new List<B>();
        var c = new C { BaseProperty = "Base", SomeProperty = "Some" };
        aList.ListOfBs.Add(c);

        var type = typeof (AList);
        var serializer = new XmlSerializer(type);
        TextWriter w = new StringWriter();
        serializer.Serialize(w, aList);
    }    
}

现在,当我尝试运行代码时,我在最后一行得到了 InvalidOperationException

类型 XmlTest.C 不是预期的。使用 XmlInclude 或 SoapInclude 属性指定静态未知的类型。

我知道用 [XmlRoot] 添加一个 [XmlInclude(typeof(C))] 属性可以解决问题。但我想动态地实现它。因为在我的项目中,C 类在加载之前是未知的。C 类正在作为插件加载,因此我无法在其中添加 XmlInclude 属性。

我也试过

TypeDescriptor.AddAttributes(typeof(AList), new[] { new XmlIncludeAttribute(c.GetType()) });

var type = typeof (AList);

但没有用。它仍然给出同样的例外。

有谁知道如何实现它?

4

4 回答 4

35

两种选择;最简单的(但给出奇怪的 xml)是:

XmlSerializer ser = new XmlSerializer(typeof(AList),
    new Type[] {typeof(B), typeof(C)});

使用示例输出:

<AList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <ListOfBs>
    <B />
    <B xsi:type="C" />
  </ListOfBs>
</AList>

更优雅的是:

XmlAttributeOverrides aor = new XmlAttributeOverrides();
XmlAttributes listAttribs = new XmlAttributes();
listAttribs.XmlElements.Add(new XmlElementAttribute("b", typeof(B)));
listAttribs.XmlElements.Add(new XmlElementAttribute("c", typeof(C)));
aor.Add(typeof(AList), "ListOfBs", listAttribs);

XmlSerializer ser = new XmlSerializer(typeof(AList), aor);

使用示例输出:

<AList xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <b />
  <c />
</AList>

无论哪种情况,您都必须缓存并重新使用该ser实例;否则你会从动态编译中大量使用内存。

于 2010-04-22T09:37:45.850 回答
10

基于 Marc 的第一个答案(我只需要阅读,所以我不需要防止奇怪的输出),受此codeproject的启发,我使用更动态/通用的类型数组来解释未知类型。

    public static XmlSerializer GetSerializer()
    {
        var lListOfBs = (from lAssembly in AppDomain.CurrentDomain.GetAssemblies()
                           from lType in lAssembly.GetTypes()
                           where typeof(B).IsAssignableFrom(lType)
                           select lType).ToArray();
        return new XmlSerializer(typeof(AList), lListOfBs);
    }

(一个可能会使其更高效,例如使用静态或只读类型数组而不是局部变量。这样可以避免重复使用反射。但我对何时加载程序集以及类和属性知之甚少进行初始化,以了解这是否会给您带来麻烦。我的使用量并不多,花时间研究这一切,所以我只是多次使用相同的反射。)

于 2013-01-18T16:44:48.663 回答
2

查看 XmlSerializer 的文档。有一个构造函数期望已知类型作为第二个参数。这应该适用于您的用例。

于 2010-04-22T09:33:14.547 回答
0

我不认为属性可以在运行时应用,因为它们用于在 CIL 代码中创建元数据。

于 2010-04-22T09:33:26.943 回答