XML序列化彼此内部的元素

时间:2017-07-04 14:18:17

标签: c# .net xml generics serialization

我有一个可以自行序列化的类,例如:

[XmlRoot("NameOfMyRoot", Namespace = "myNamespace")]
public class Inner
{
    public Inner(){}
    public string SomeString { get; set; }
}

完全序列化(我正在使用ns的{​​{1}}别名,请参阅完整演示)

myNamespace

现在我希望这个对象成为另一个对象(包装器)的一部分:

<?xml version="1.0" encoding="utf-16"?>
<NameOfMyRoot xmlns:ns="myNamespace">
    <ns:SomeString></ns:SomeString>
</NameOfMyRoot>

这给了我这个:

[XmlRoot("Root")]
public class Outer<T>
{
    public T Property{ get; set; }

    public Outer(){}
    public Outer(T inner)
    {
        Property = inner;
    }
}

我想要的只是将内部对象按原样嵌入其父对象中,如下所示:

<?xml version="1.0" encoding="utf-16"?>
<Root xmlns:ns="myNamespace">
    <Property>
        <ns:SomeString></ns:SomeString>
    </Property>
</Root>

请注意,命名空间不应该移动到root,并且我不能指定元素的名称,因为会有许多不同的类型。 Fiddle with full example

对于cource来说,我可以将它们单独序列化并通过一些令人讨厌的字符串操作进行组合,但我希望有一种巧妙的方法来实现这一点。

1 个答案:

答案 0 :(得分:0)

没有简单的方法可以做到这一点。但是,您可以在运行时执行此操作:

// perhaps discover these details at runtime
var attribs = new XmlAttributeOverrides();
attribs.Add(typeof(Outer<Inner>), "Property", new XmlAttributes
{
    XmlElements = { new XmlElementAttribute {ElementName = "NameOfMyRoot" } }
});
attribs.Add(typeof(Inner), "SomeString", new XmlAttributes
{
    XmlElements = { new XmlElementAttribute {  Namespace = "myNamespace"} }
});

var ser = new XmlSerializer(typeof(Outer<Inner>), attribs);

var obj = new Outer<Inner> { Property = new Inner { SomeString = "abc" } };
ser.Serialize(Console.Out, obj);

请注意,必须存储并重新使用此类序列化程序;对于简单的new XmlSerializer()示例,序列化程序在内部进行缓存并重新使用;但是,对于像这样的非平凡情况,每次都会发出一个新的动态类型,因此你会泄漏内存(它不会被卸载)。

这会给你:

<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <NameOfMyRoot>
    <SomeString xmlns="myNamespace">abc</SomeString>
  </NameOfMyRoot>
</Root>

如果您不想要两个xmlns别名声明,可以单独删除它们,但更改含义,至少是合规读者。同样,ns可以添加为别名:

var obj = new Outer<Inner> { Property = new Inner { SomeString = "abc" } };
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
ns.Add("ns", "myNamespace");
ser.Serialize(Console.Out, obj, ns);

给出:

<Root xmlns:ns="myNamespace">
  <NameOfMyRoot>
    <ns:SomeString>abc</ns:SomeString>
  </NameOfMyRoot>
</Root>

作为替代方法:

using System;
using System.Xml;
using System.Xml.Serialization;

[XmlRoot("NameOfMyRoot")]
public class Inner
{
    public Inner() { }
    [XmlElement(Namespace = "myNamespace")]
    public string SomeString { get; set; }
}
static class Program
{
    static void Main()
    {
        using (XmlWriter writer = XmlWriter.Create(Console.Out))
        {
            writer.WriteStartDocument(false);
            writer.WriteStartElement("Root");

            var ser = new XmlSerializer(typeof(Inner));
            var obj = new Inner { SomeString = "abc" };

            var ns = new XmlSerializerNamespaces();
            ns.Add("", "");
            ns.Add("ns", "myNamespace");
            ser.Serialize(writer, obj, ns);

            writer.WriteEndElement();
            writer.WriteEndDocument();
        }
    }
}

我在这里做的是手动编写根节点,只获取XmlSerializer来编写内部内容。请注意,我必须更改属性SomeString以使其按照您期望的方式工作(具有命名空间的字符串,而不是对象)。