【问题标题】:Xml serialization of child class with dotnet. Don't write the child root node from parent class使用 dotnet 对子类进行 XML 序列化。不要写父类的子根节点
【发布时间】:2011-01-25 16:52:00
【问题描述】:

这是我的问题。我有一个父类“Foo”和一个子类“Bar”:

[Serializable]
public class Foo, IXmlSerializable
{
     public Bar Child {get; set;}    

     #region IXmlSerializable Membres
     public System.Xml.Schema.XmlSchema GetSchema()
     {
            return null;
    }
    public void ReadXml(System.Xml.XmlReader reader)
    {
        throw new NotImplementedException();
    }
     public void WriteXml(System.Xml.XmlWriter writer)
     {
        new XmlSerializer(this.Child.GetType()).Serialize(writer, this.Child);
     }
        #endregion
}


[Serializable]
public class Bar
{
    [XmlElement]
    public string MyElement1 {get; set;}
    [XmlElement]
    public string MyElement2 {get; set;}
}

如果我按原样序列化这些类,我会得到这样的结果:

<xml>
<Foo>
    <Bar>
        <MyElement1>beer</MyElement>
        <MyElement2>vodka</MyElement>
    </Bar>
</Foo>

如何控制从“Foo”(父)类的序列化以删除“Bar”节点?我想要这样的东西:

<xml>
<Foo>
    <MyElement1>beer</MyElement>
    <MyElement2>vodka</MyElement>
</Foo>

这个示例非常简单。 感谢您的帮助!

【问题讨论】:

    标签: .net xml serialization


    【解决方案1】:

    您需要更改FooWriteXml 方法并执行以下操作:

    public void WriteXml(System.Xml.XmlWriter writer)
    {
       //new XmlSerializer(this.Child.GetType()).Serialize(writer, this.Child);
       writer.WriteElementString("MyElement1", this.Child.MyElement1); 
       writer.WriteElementString("MyElement2", this.Child.MyElement2);
    }
    

    这将呈现您正在寻找的 XML(基本上使 &lt;Bar&gt; 节点消失)。

    【讨论】:

    • 感谢您的帮助!这是一个解决方案,但我不喜欢它,因为如果我的“Bar”类上的某些元素或属性发生变化,我需要更改我的“Foo”类“WriteXml”方法。我真的不能让“Bar”序列化,检索结果,然后删除类名节点?
    • @Ben:您可能可以这样做 - 但我认为这不会比我提到的更好或更清洁。你的“Foo”类实现了IXmlSerializable——所以它应该是处理任何特殊情况的类,真的。另外:删除这样的 XML 节点还是很奇怪……为什么需要这个??
    【解决方案2】:

    @Marc:解释起来很复杂,我只需要它......

    我想我找到了办法。也许不是最好的,但也不是最差的……感谢您的帮助!

    public void WriteXml(System.Xml.XmlWriter writer)
            {
                if (this.Child != null)
                {     
                    XmlSerializer xs = new XmlSerializer(typeof(Bar));
                    XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
                    ns.Add("", "http://www.w3.org/2001/XMLSchema-instance");
                    ns.Add("", "http://www.w3.org/2001/XMLSchema");
                    XElement res = SerializeAsXElement(xs, this.Child, ns);               
                    MoveDescendants(res, writer);
                   // new XmlSerializer(this.InitRes.GetType()).Serialize(writer, this.InitRes);
                }
            }
    
            #endregion
    
    
    
            /// <summary>
            /// Moves all the children of src (including all its elements and attributes) to the 
            /// destination element, dst.
            /// </summary>
            /// <param name="src">The source element.</param>
            /// <param name="dst">The destination element.</param>
            public static void MoveDescendants(XElement src, XmlWriter dst)
            {
                foreach (XAttribute attr in src.Attributes())
                {
                    dst.WriteAttributeString(attr.Value, attr.Name.LocalName);
                }
    
                foreach (XNode elem in src.Nodes())
                {
                    elem.WriteTo(dst);
                }
            }
    
    
            public static XElement SerializeAsXElement(XmlSerializer xs, object o, XmlSerializerNamespaces ns)
            {
                XDocument d = new XDocument();
                using (XmlWriter w = d.CreateWriter()) xs.Serialize(w, o, ns);
                XElement e = d.Root;
                e.Remove();
                return e;
            }
    

    【讨论】:

      【解决方案3】:

      我不确定在什么情况下会导致您要求这样做,感觉不太对,但现在我只假设您有正当理由。

      如果您在 Bar 上实现 IXmlSerializable 并让 Foo 代理通过它而不是使用 XmlSerializer,那么这应该可以满足您的需求......这只是意味着您失去了在 Bar 上使用序列化属性的便利。

      【讨论】:

        【解决方案4】:

        这里的诀窍是使用这两种机制来进行序列化和反序列化。

        在下面的示例中,我有一个 Foo 类型的 List ,它是 IXmlSerializable ,它有一个抽象的孩子。每个具体的子节点都使用更整洁的属性来实现序列化。

        对于写入,Foos WriteXml 写出一个元素,其名称是合约子的完整类型名称。然后序列化孩子。

        对于阅读,Foos ReadXml 会进行一些丑陋的读取以将自己定位在全名所在的位置,并创建具体类型的新实例(但请注意,Foo 不知道所有可能的具体类型) 然后它基于具体类型创建一个新的 XmlSerializer 并将其反序列化。 再次,它需要做一些丑陋的读取来定位下一个元素的阅读器。

        这会产生以下 XML

        <?xml version="1.0"?>
        <ArrayOfFoo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <Foo>
            <ParentData>a</ParentData>
            <XmlAbstractSerialisationTest.Bar1>
              <Bar1>
                <Id>0</Id>
                <Name>hello</Name>
                <Extra1>boo</Extra1>
                <Extra2>good</Extra2>
              </Bar1>
            </XmlAbstractSerialisationTest.Bar1>
          </Foo>
          <Foo>
            <ParentData>b</ParentData>
            <XmlAbstractSerialisationTest.Bar2>
              <Bar2>
                <Id>0</Id>
                <Name>hello</Name>
                <Extra1>boo</Extra1>
                <Extra2>123</Extra2>
              </Bar2>
            </XmlAbstractSerialisationTest.Bar2>
          </Foo>
        </ArrayOfFoo>

        代码是:

          namespace XmlAbstractSerialisationTest
          {
           using System;
           using System.Collections.Generic;
           using System.IO;
           using System.Reflection;
           using System.Xml.Serialization;
        
           class Program
           {
               static void Main(string[] args)
               {
                   // Write
                   List<Foo> data = new List<Foo>{
                       new Foo{
                           ParentData = "a",
                           Child = new Bar1{
                               Id = 0,
                               Name = "hello",
                               Extra1 = "boo",
                               Extra2 = "good"
                           }
                       },
                       new Foo{
                           ParentData = "b",
                           Child = new Bar2{
                               Id = 0,
                               Name = "hello",
                               Extra1 = "boo",
                               Extra2 = 123
                           }
                       }
                   };
        
                   XmlSerializer xs = new XmlSerializer(typeof(List<Foo>));
                   using (FileStream fs = new FileStream("test.xml", FileMode.Create, FileAccess.Write))
                   {
                       xs.Serialize(fs, data);
                   }
        
                   // Read
                   List<Foo> newData;
                   using (FileStream fs = new FileStream("test.xml", FileMode.Open, FileAccess.Read))
                   {
                       newData = xs.Deserialize(fs) as List<Foo>;
                   }
               }
           }
        
           public class Foo : IXmlSerializable
           {
               public string ParentData { get; set; }
        
               public BaseBar Child { get; set; }
        
               public System.Xml.Schema.XmlSchema GetSchema()
               {
                   throw new System.NotImplementedException();
               }
        
               public void ReadXml(System.Xml.XmlReader reader)
               {
                   reader.Read();
                   reader.Read();
                   ParentData = reader.Value;
                   reader.Read();
                   reader.Read();
        
                   Assembly ass = Assembly.LoadFile(@"[Full path to assembly]");
                   Child = ass.CreateInstance(reader.Name) as BaseBar;
                   reader.Read();
                   XmlSerializer xs = new XmlSerializer(Child.GetType());
                   Child = xs.Deserialize(reader) as BaseBar;
                   reader.Read();
                   reader.Read();
               }
        
               public void WriteXml(System.Xml.XmlWriter writer)
               {
                   writer.WriteElementString("ParentData", ParentData);
        
                   writer.WriteStartElement(Child.GetType().FullName);
        
                   XmlSerializer xs = new XmlSerializer(Child.GetType());
                   xs.Serialize(writer, Child);
        
                   writer.WriteEndElement();
               }
           }
        
           [Serializable]
           public abstract class BaseBar
           {
               [XmlElement]
               public int Id { get; set; }
        
               [XmlElement]
               public string Name { get; set; }
           }
        
           [Serializable]
           public class Bar1 : BaseBar
           {
               [XmlElement]
               public string Extra1 { get; set; }
        
               [XmlElement]
               public string Extra2 { get; set; }
           }
        
           [Serializable]
           public class Bar2 : BaseBar
           {
               [XmlElement]
               public string Extra1 { get; set; }
        
               [XmlElement]
               public int Extra2 { get; set; }
           }
        
          }
        

        上面的代码可以序列化和反序列化任意复杂的具体类型,而父类不知道这些类型。 每次处理具体类型时,都需要序列化全名。然后反序列化必须知道全名(编码到元素名称中)和可以找到类型的程序集。

        上面的代码有问题! 如果具体类型具有非默认构造函数,则 CreateInstance 方法将失败。 可能它可以使用 FormatterServices.GetUninitializedObject 明天我会玩这个。

        此代码应该可以工作,但您需要将“[程序集的完整路径]”更新为正确的程序集路径。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-09-02
          • 1970-01-01
          • 1970-01-01
          • 2011-12-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多