Rename class when serialized to XML

I am trying to serialize the Outer class shown below and create an XElement from serialized XML. It has a property of type Inner . I would like to change the name of both Inner (before Inner_X ) and Outer (before 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 { } 

This creates an XElement that looks like this:

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

I would like to:

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

I want to save information about how a class should be renamed with this class. I thought I could do this with the XmlType attribute. However, this is ignored and the property name is used instead.

I looked here and here , among other places, and I feel that this should work. What am I missing?

Explanation

"By storing information about how a class should be renamed with this class," I mean that the term Inner_X should only appear in the Inner class. It should not be displayed at all in the Outer class.

+5
source share
3 answers

When the XmlSerializer serializes the type, the type itself manages the names of the elements created for its properties. That is, the name of the property becomes the name of the element if it is not statically overridden by XmlElementAttribute.ElementName . XmlTypeAttribute.TypeName usually only manages the name of the element when an instance of the type to which it is applied is not serialized as a property of some containing type - for example, when it is the root element or when it is contained in a collection that is serialized with an external element of the container. This construction avoids name conflicts in cases when a given type has several properties of the same type.

However, in the case of polymorphic property types, there is an exception. For them, the XmlSerializer has the ability to use the XML type name for each of the possible polymorphic types as the name of the element, thereby identifying the actual C # type from which this element was created. To enable this functionality, you need to add several [XmlElement(typeof(TDerived))] attributes to the property, one for each possible type of TDerived .

You can use this feature to generate the required XML by entering the psuedo-polymorphic proxy property:

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

Then the output will be as you require:

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

Prototype fiddle .

However, as @evk commented, if your Outer class contains several properties of the same type, this cannot be done.

Another possibility to think about: if you just donโ€™t want to manually duplicate name strings of type "Inner_X" in several places (that is, in the attributes [XmlType(string name)] and [XmlElement(string name)] ), you can centralize the type names by making them 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"; } 

Update

I just noticed your comment. I assume that Inner will be an abstract base class, each subclass of which will be serialized for different element names . If so, then the XmlSerializer can indeed be used to use the XML type name as the element name, but only when it can determine statically that the property type is actually polymorphic due to the presence of several attributes [XmlElement(typeof(TDerived))] , Thus, the following classes will generate the required 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 { } 
+1
source

You need to set the element name for the property, not the xml type of the inner class. Try the following:

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

It is very simple. You need to use XmlRootAttribute for class and XmlElementAttribute for members, as described here on MSDN .

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

I created a working .NET Fiddle to illustrate this. This SO Q and A seemed to be addressing this similar issue. Finally, when decoding XML to a string, you should probably use a different encoding, no? According to these , the strings are encoded in UTF-16 encoding - it doesnโ€™t matter, but I thought that I would pay attention to it.

The script I shared leads to the following XML:

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

Update

After you clarified your question with an explanation, I now understand what you are asking. Unfortunately, (as far as I know), this is not possible to control, as you wish through attributes. You will either have to create your own XML serializer / deserializer, or accept the fact that there are limitations with attribute support.

0
source

Source: https://habr.com/ru/post/1247523/


All Articles