序列化为XML时重命名类

我正在尝试序列化下面显示的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

    

我想要的是:

    

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

我看过这里和其他地方,并觉得这应该工作。 我错过了什么?

澄清

通过“保持关于如何使用该类重命名类的信息” ,我的意思是术语Inner_X应该只出现在Inner类中。 它不应该出现Outer类中。

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

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

您可以使用此function通过引入psuedo-polymorphic代理属性来生成所需的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; } } } 

然后根据需要输出:

    

原型小提琴 。

但是,正如@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 { } 

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

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

这非常简单。 您需要使用classXmlRootAttribute和成员的XmlElementAttribute ,如MSDN中所述 。

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

我创建了一个有效的.NET Fiddle来举例说明这一点。 这个SO Q&A似乎解决了这个类似的问题。 最后,当将XML解码为字符串时,您应该使用不同的编码,不是吗? 根据这一点 ,字符串是UTF-16编码 – 没什么大不了的,但我想请注意它。

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

    

更新

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