【问题标题】:Wrapping Serialized Properties in Additional Elements with .NET XML Serialization使用 .NET XML 序列化将序列化属性包装在其他元素中
【发布时间】:2009-08-04 10:07:01
【问题描述】:

我正在使用 C# + VSTS2008 + .Net 3.0 进行 XML 序列化。代码工作正常。下面是我的代码和当前的序列化 XML 结果。

现在我想在输出 XML 文件中添加两个附加层。这是我预期的 XML 结果。有什么简单的方法可以做到这一点?我不确定 NestingLevel 是否可以帮助做到这一点。我想找到一种不改变 MyClass 和 MyObject 结构的简单方法。

预期的 XML 序列化结果,

<?xml version="1.0"?>
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 

xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <MyObjectProperty>
    <AdditionalLayer1>
      <AdditionalLayer2>
        <ObjectName>Foo</ObjectName>
      </AdditionalLayer1>
    </AdditionalLayer2>
  </MyObjectProperty>
</MyClass>

当前 XML 序列化结果,

<?xml version="1.0"?>
<MyClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 

xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <MyObjectProperty>
    <ObjectName>Foo</ObjectName>
  </MyObjectProperty>
</MyClass>

我当前的代码,

public class MyClass
{
    public MyObject MyObjectProperty;
}
public class MyObject
{
    public string ObjectName;
}

public class Program
{
    static void Main(string[] args)
    {
        XmlSerializer s = new XmlSerializer(typeof(MyClass));
        FileStream fs = new FileStream("foo.xml", FileMode.Create);
        MyClass instance = new MyClass();
        instance.MyObjectProperty = new MyObject();
        instance.MyObjectProperty.ObjectName = "Foo";
        s.Serialize(fs, instance);

        return;
    }
}

【问题讨论】:

    标签: c# .net xml visual-studio-2008 xml-serialization


    【解决方案1】:

    我想找到一种不改变 MyClass 和 MyObject 结构的简单方法。

    坦率地说,这不会发生。 XmlSerializer(简单使用时)遵循类/成员的结构。所以你不能在不改变结构的情况下添加额外的层。

    当以定制方式 (IXmlSerializable) 使用时,您可以肯定地说它并不简单......这不是一个有趣的实现接口。

    我的建议:引入一个模拟您所需格式的 DTO,并在序列化之前对其进行填充。

    【讨论】:

    • 谢谢 Marc,我有一个相关的问题,我发现只有属性/成员名称被序列化为 XML 作为元素名称,类名没有被序列化为元素名称,例如成员“public MyObject MyObjectProperty” , 元素由“MyObjectProperty”命名,但不是由“MyObject”命名。与使用类型名称相比,这种设计是否更有意义?
    • 如果您想控制名称,请使用 [XmlElement(...)][XmlAttribute(...)][XmlType(...)][XmlRoot(...)] - 前两个最有用(例如,在违规属性上) .
    • 对不起,马克,我不是说我想改名。我的问题是关于 .Net XML 序列化设计的,您可以看到当我们序列化嵌套类型时,成员/属性类型丢失但成员/类型名称被保留。为什么这样设计,不保留类型信息?
    • "数据传输对象";一个单独的对象模型,主要用于序列化和类似的简单事情。它不必具有业务逻辑(将其留给 actual 对象);它所要做的就是看起来正确的形状......
    • 因为它是 contract 序列化器,而不是 type 序列化器。重要的是,它无关类型是什么。我可以提供一组完全独立的类,只要它们同意合同(即 xml 名称等),它们就可以正常工作。还要考虑到通常会有代理类将 xml 用作 Web 服务,并且该代码可能不是 .NET;所以在 xml 中几乎没有类型名称的位置——只有数据。
    【解决方案2】:

    为什么需要这两个额外的层?

    这是业务需求,你应该将这两个对象添加到你的对象模型中:

    public class MyClass
    {
        public AdditionalLayer1 AdditionalLayer1;
    }
    public class AdditionalLayer1
    {
        public AdditionalLayer2 AdditionalLayer2;
    }
    public class AdditionalLayer2
    {
        public MyObject MyObjectProperty;
    }
    public class MyObject
    {
        public string ObjectName;
    }
    

    如果纯粹是因为某些兼容性要求,您可以执行上述操作,或者在 MyClass 上实现 IXmlSerializable:

    public class MyClass : IXmlSerializable
    {
        public MyObject MyObjectProperty;
    
        public void WriteXml (XmlWriter writer)
        {
            //open the two layers
            //serialize MyObject
            //close the two layers
        }
    
        public void ReadXml (XmlReader reader)
        {
            //read all the layers back, etc
        }
    
        public XmlSchema GetSchema()
        {
            return(null);
        }
    
    }
    

    【讨论】:

    • 我有一个相关的问题,我发现只有属性/成员名称被序列化为 XML 作为元素名称,类名没有被序列化为元素名称,例如对于成员“public MyObject MyObjectProperty”,元素是由“MyObjectProperty”命名,但不是由“MyObject”命名。与使用类型名称相比,这种设计是否更有意义?
    • 我想这更有意义,因为您可以在单个类的两个不同上下文中重用相同的类型,并且您希望使用不同的元素名称对其进行序列化。
    【解决方案3】:

    您可以在序列化后使用 XSL 进行转换并添加“缺失”标签。

    【讨论】:

    • 由于 XSLT 中的 T 代表转换,转换转换没有多大意义,所以我省略了 T,但是是的 ;-)
    • 我一直认为 XSL 很棘手。无论如何,推荐一本 XSL 的好书?
    • 不是真的...我是通过做和 Google 学到的,但请参阅 oreilly.com/catalog/9780596000530 和相关书籍
    【解决方案4】:

    我建议序列化为MemoryStream 而不是FileStream,然后将XML 加载到XmlDocument,然后使用该类的方法插入所需的额外节点。之后,您可以将其保存出来。

    【讨论】:

    • 如果你走那条路,StringWriter 会更简单;无需编码/解码。
    • 为什么序列化到 MemoryStream 需要编码/解码?你的意思是从 XML 字符串中获取/设置 UTF-8 字节吗?
    【解决方案5】:

    我最近才这样做,并没有发现重写 IXmlSerializable 接口非常困难或复杂。像往常一样进行序列化/反序列化,但对于包含子类的类,您需要将这些子类包装在从 IXmlSerializable 派生的额外标签中:

    class ContainingClass : IXmlSerializable
    {
     ...
    
    #region IXmlSerializable Members
    
    public XmlSchema GetSchema()
    {
      return null;
    }
    
    public void ReadXml(XmlReader reader)
    {
      **reader.ReadStartElement("Equipment");**
    
      XmlSerializer ser = new XmlSerializer(typeof(Host));
    
      while (reader.NodeType != XmlNodeType.EndElement)
      {
        Host newHost = new Host();
        newHost.Name = (string) ser.Deserialize(reader);
        _hosts.Add(newHost);
    
        reader.Read();
      }
    
      // Read the node to its end.
      // Next call of the reader methods will crash if not called.
      **reader.ReadEndElement(); // "Equipment"**
    }
    
    public void WriteXml(XmlWriter writer)
    {
      XmlSerializer ser = new XmlSerializer(typeof(Host));
      **writer.WriteStartElement("Equipment");**
      foreach (Host host in this._hosts)
      {
        ser.Serialize(writer, host);
      }
      **writer.WriteEndElement();**
    }
    
    #endregion
    

    我在这里所做的只是包装低级对象的反序列化。在这里,我在这个类的集合中的所有主机周围包装了一个额外的标签“设备”。您可以在此处添加任意数量的标签,只要关闭您开始的每个标签即可。

    注意:粗体显示为 ** 文本 ** - 只需确保去掉双星号 :)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多