使用dotnet对子类进行Xml序列化。不要从父类编写子根节点

时间:2011-01-25 16:52:01

标签: .net xml serialization

这是我的问题。我有一个父类“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>

这个样本非常简单。 谢谢你的帮助!

4 个答案:

答案 0 :(得分:1)

您需要更改Foo的{​​{1}}方法并执行以下操作:

WriteXml

这将呈现您正在寻找的XML(基本上使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); } 节点消失)。

答案 1 :(得分:0)

@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;
        }

答案 2 :(得分:0)

我不确定什么情况会导致你要求这个,它确实感觉不对,但是现在我只是假设你有正当的理由。

如果你在Bar上实现IXmlSerializable并使用Foo代理而不是使用XmlSerializer那么这应该可以得到你想要的东西......它只是意味着你放弃了在Bar上使用序列化属性的便利。

答案 3 :(得分:0)

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

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

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

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

这将生成以下XML

String str="country_province_city";
wordUtils.capitalize(str, '_');
str=str.replaceAll("_", "");

代码是:

<?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>

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

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

此代码应该可以使用,但您需要将“[装配的完整路径]”更新为正确的装配路径。