序列化为XML时重命名类

时间:2016-04-20 13:19:28

标签: c# xml-serialization xmlserializer

我尝试序列化下面显示的Outer类,并从序列化的XML中创建XElement。它具有Inner类型的属性。我想更改Inner(至Inner_X)和Outer(至Outer_X)的名称。

class Program
{
    static void Main(string[] args)
    {
        using (MemoryStream memoryStream = new MemoryStream())
        {
            using (TextWriter streamWriter = new StreamWriter(memoryStream))
            {
                var xmlSerializer = new XmlSerializer(typeof(Outer));

                xmlSerializer.Serialize(streamWriter,  new Outer());

                XElement result = XElement.Parse(Encoding.ASCII.GetString(memoryStream.ToArray()));
            }
        }
    }
}

[XmlType("Outer_X")]
public class Outer
{
    public Outer()
    {
        this.InnerItem = new Inner();
    }

    public Inner InnerItem { get; set; }
}

[XmlType("Inner_X")]
public class Inner
{
}

这会创建一个XElement,如下所示:

<Outer_X xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <InnerItem />
</Outer_X>

我想要的是:

<Outer_X xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Inner_X />
</Outer_X>

我想保留有关如何使用该类重命名类的信息。我认为我可以使用XmlType属性执行此操作。但是,这会被忽略,而是使用属性名称。

我看过herehere以及其他地方,觉得这应该有效。我错过了什么?

澄清

通过&#34;保留有关如何使用该类&#34; 重命名类的信息,我的意思是术语Inner_X应该只出现在Inner课程中。它不应出现在Outer类中。

3 个答案:

答案 0 :(得分:3)

您需要设置属性的元素名称,而不是内部类的xml类型。 试试这个:

[XmlType("Outer_X")]
public class Outer
{
    public Outer()
    {
        this.InnerItem = new Inner();
    }

    [XmlElement("Inner_X")]
    public Inner InnerItem { get; set; }
}


public class Inner
{
}

答案 1 :(得分:2)

XmlSerializer序列化类型时,类型本身控制为其属性创建的元素的名称。即属性名称将成为元素名称,除非XmlElementAttribute.ElementName静态覆盖。 XmlTypeAttribute.TypeName通常仅在应用它的类型的实例 not 被序列化为某些包含类型的属性时控制元素名称 - 例如,当它是根时元素,或者它包含在使用外部容器元素序列化的集合中时。在给定类型中存在多个相同类型属性的情况下,此设计可避免名称冲突。

但是,在多态属性类型的情况下存在例外。对于这些,XmlSerializer可以选择使用每种可能的多态类型的XML类型名称作为元素名称,从而标识从中创建元素的实际c#类型。要启用此功能,必须向属性添加多个[XmlElement(typeof(TDerived))]属性,每个可能的类型TDerived一个。

您可以使用此功能通过引入伪数据代理属性来生成所需的XML:

[XmlType("Outer_X")]
public class Outer
{
    public Outer()
    {
        this.InnerItem = new Inner();
    }

    [XmlIgnore]
    public Inner InnerItem { get; set; }

    [XmlElement(typeof(Inner))]
    [XmlElement(typeof(object))]
    [Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]
    public object InnerItemXmlProxy
    {
        get
        {
            return InnerItem;
        }
        set
        {
            InnerItem = (Inner)value;
        }
    }
}

然后根据需要输出:

<Outer_X xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Inner_X />
</Outer_X>

原型fiddle

但是,正如@evk评论的那样,如果您的Outer类包含多个相同类型的属性,则无法完成此操作。

要考虑的另一个选择:如果您只是不想在多个位置手动复制"Inner_X"类型名称字符串(即[XmlType(string name)][XmlElement(string name)]您可以通过将类型名称设为public const来集中它们:

[XmlType(Outer.XmlTypeName)]
public class Outer
{
    public const string XmlTypeName = "Outer_X";

    public Outer()
    {
        this.InnerItem = new Inner();
    }

    [XmlElement(Inner.XmlTypeName)]
    public Inner InnerItem { get; set; }
}

[XmlType(Inner.XmlTypeName)]
public class Inner
{
    public const string XmlTypeName = "Inner_X";
}

<强>更新

我刚注意到你的评论我打算将Inner作为一个抽象基类,其每个子类将序列化为不同的元素名称。如果是这种情况,那么XmlSerializer确实可以使用XML类型名称作为元素名称 - 但只有当它可以静态地确定属性类型实际上是多态的到期时存在多个 [XmlElement(typeof(TDerived))]属性。因此,以下类将生成您需要的XML:

[XmlType("Outer_X")]
public class Outer
{
    public Outer()
    {
        this.InnerItem = new InnerX();
    }

    [XmlElement(typeof(InnerX))]
    [XmlElement(typeof(Inner))] // Necessary to inform the serializer of polymorphism even though Inner is abstract.
    public Inner InnerItem { get; set; }
}

public abstract class Inner
{
}

[XmlType("Inner_X")]
public class InnerX : Inner
{
}

答案 2 :(得分:0)

这非常简单。您需要使用class的{​​{3}}和成员的XmlRootAttribute,如XmlElementAttribute所述。

[XmlRoot(ElementName = "Outer_X")]
public class Outer
{    
    [XmlElement(ElementName = "Inner_X")]
    public Inner InnerItem { get; set; } = new Inner();
}

public class Inner { }

我创建了一个工作here on MSDN来举例说明这一点。这.NET Fiddle似乎解决了这个类似的问题。最后,当将XML解码为字符串时,您应该使用不同的编码,不是吗?根据{{​​3}},字符串是UTF-16编码的 - 这不是什么大问题,但我认为它会引起注意。

我分享的小提琴导致以下XML:

<Outer_X xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <Inner_X />
</Outer_X>

<强>更新

在您通过澄清更新问题后,我现在明白您的要求。不幸的是,(据我所知)这不能通过属性来控制。您必须创建自己的XML序列化器/反序列化器,或者接受属性支持存在限制的事实。

相关问题