【问题标题】:How to add XmlInclude attribute dynamically如何动态添加 XmlInclude 属性
【发布时间】:2010-04-22 09:23:06
【问题描述】:

我有以下课程

[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);

但没用。它仍然给出同样的异常。

有人知道如何实现吗?

【问题讨论】:

    标签: c# .net xml-serialization xmlinclude


    【解决方案1】:

    两个选项;最简单的(但给出奇怪的 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 实例;否则动态编译会导致内存溢出。

    【讨论】:

    • 我试过这个,但它给了我以下异常“可能没有为 XmlTest.AList 类型指定 XmlRoot 和 XmlType 属性。”知道它为什么会出现以及如何解决它吗?
    • @Anindya - 这很奇怪; “原样”的例子对我来说很好。您使用的是什么框架版本?
    • @Anindya - 我已经尝试过 4.0 和 2.0(涵盖 3.0 和 3.5)并且无法重现。你能给出更完整的描述吗?
    • 我正在疯狂寻找XmlAttributes 接受XmlIncludeAttribute 的属性。我从没想过要改用XmlElementAttribute。谢谢。
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-06-24
          • 2020-09-05
          • 1970-01-01
          • 2020-10-31
          • 2013-08-10
          • 2017-01-07
          • 1970-01-01
          相关资源
          最近更新 更多