【问题标题】:XmlSerializer replace xsi:type to node nameXmlSerializer 将 xsi:type 替换为节点名称
【发布时间】:2012-03-10 17:25:01
【问题描述】:

目前 XmlSerializer 产生以下结构:

<config>
  <BaseType xsi:type="DerivedType1" />
  <BaseType xsi:type="DerivedType2" />
</config>

有没有办法让它把类型名放入节点:

<config>
  <DerivedType1 />
  <DerivedType2 />
</config>

?

【问题讨论】:

  • 能否提供你要序列化的类的代码?

标签: .net serialization xml-serialization


【解决方案1】:

使用XmlElementAttribute 构造函数重载(string elementName, Type type)。 它允许您根据在成员中找到的实际类型替换元素名称。 如果您有多个派生类型,请将其中的几个堆叠在一起。

如果您尝试序列化像 List&lt;Base&gt; 这样可能包含 Derived 实例的通用集合,请以相同方式使用 XmlArrayItem

以这种方式定义也隐式地使派生类型已知,因此不需要XmlInclude属性

示例定义:

[XmlRoot]
public class Data
{
    [XmlElement("Derived1", typeof(Derived1))]
    [XmlElement("Derived2", typeof(Derived2))]
    public Base foo { get; set; }
    [XmlArrayItem("Derived1", typeof(Derived1))]
    [XmlArrayItem("Derived2", typeof(Derived2))]
    public List<Base> fooList { get; set; }
}

public class Base { ... }
public class Derived1 : Base { ... }
public class Derived2 : Base { ... }

【讨论】:

    【解决方案2】:

    为什么人们总是说“你永远无法反序列化”。 这绝对是 FALSE。

    public class BaseClass {
      public string Name {get;set;}
    }
    [XmlRoot("BaseClass")]
    public class ChildClass : BaseClass {
      public int Value {get;set;}
    }
    [XmlRoot("BaseClass")]
    public class FlatClass
    {
      public string Name {get;set;}
      public int Value {get;set;}
    }
    
    XmlSerializer ser1 = new XmlSerializer(typeof(BaseClass));
    XmlSerializer ser2 = new XmlSerializer(typeof(ChildClass));
    XmlSerializer ser3 = new XmlSerializer(typeof(FlatClass));
    ser1.Serialize(File.Open("ser1.xml", FileMode.Create), new BaseClass(){Name="Base"});
    ser2.Serialize(File.Open("ser2.xml", FileMode.Create), new ChildClass(){Name="Child",Value = 1});
    
    ser1.Deserialize(File.OpenRead("ser2.xml"));
    ser2.Deserialize(File.OpenRead("ser1.xml"));
    ser3.Deserialize(File.OpenRead("ser2.xml"));
    

    轰隆隆。工作得很好!!!! 序列化在这三个方面都完美无缺。生成的对象在任一侧可能都不是 100%,但它确实会反序列化。 Ser1 在反序列化 ser2.xml 时忽略 Value 元素 Ser2 在反序列化 ser1.xml 时跳过 Value 属性

    唯一打破这种模式的是:

    ser1.Serailize(File.Open("ser3.xml", FileMode.Create), new ChildClass(){Name = "Child2", Value = 2});
    XmlSerialize ser3 = new XmlSerializer(typeof(FlatClass));
    ser3.Deserialize(File.OpenRead("ser3.xml"));
    

    最后一次中断,因为 BaseClass 的序列化程序遵循在元素上包含 xsi:type="ChildClass" 属性的架构标准(尽管这是一个有价值且 99% 的时间标准)。 Ser3 无法处理该类型,因为它与该类型无关,特别是如果 FlatClass 存在于跨 WAN 或 LAN 线路的另一个程序集中。 就像 Honey-badger 一样,XmlSerailizer 不关心元素或值,只要它可以找到它们并且架构中的任何内容都不会破​​坏该过程。 XSI:TYPE 属性破坏了架构。

    例如,当使用 WCF 或其他基于 XML 通信的系统时,如果服务有一个名为 FlatClass 的类,它不会反序列化包含 xsi:type="" 属性的 ChildClass。但是,如果您不使用 BaseClass 的序列化程序,它将反序列化完全相同的 XML,而无需该 xsi:type 属性。

    Q.E.D. 包含 xsi:type 属性通常是有益的、必要的和可取的。

    这么说有没有办法为 BaseClass 类型创建一个 XmlSerializer 并告诉它在序列化子类型时不要包含 xsi:type 属性?

    谢谢 Jaeden "Sifo Dyas" al'Raec 毁灭者

    【讨论】:

      【解决方案3】:

      你可以用 XmlElement 属性覆盖元素名称,例如

      [XmlElement("DerivedType1")]
      public BaseType : DerivedType1 {get;set;}
      

      if 仍然会将 xsi:type 放入其中,并产生更大的混乱......

      你的班级是什么样子的?

      【讨论】:

      • 我不想覆盖元素名称。我希望序列化程序将 Name 设置为等于类名
      • 这就是这个答案的意图。
      • 它会这样做。据我所知,您无法禁止输入,因为那样您将永远无法反序列化。如果您不需要反序列化,那么您就没有序列化,因此您应该停止使用序列化来搞乱,只需为您的类编写一个 dumptoxml 方法。序列化是有规律的,不遵守就是不做……
      • @TonyHopkinson Arr,够公平的...BaseType : DerivedType1 让我觉得这是一个类定义...无论如何,这段代码无法编译,您能否修复它,以便我可以删除我的反对票?
      • @TonyHopkinson 认为: DerivedType1 没用(因为问题中定义了层次结构)并破坏了代码的有效性。此外,您的解决方案没有提供如何使用多个后代类型及其潜在的不同 xml 标记的示例。只是说......
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-27
      • 2011-09-24
      • 1970-01-01
      相关资源
      最近更新 更多