I'm trying to serialize the Outer
class shown below, and create an XElement
from the serialized XML. It has a property which is of type Inner
. I'd like to change the name of both Inner
(to Inner_X
) and Outer
(to 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
which 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>
What I would like is:
<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 keep the information about how a class should be renamed with that class. I thought I could do this with the XmlType
attribute. However, this is ignored and the property name is used instead.
I've looked here and here, amongst other places, and feel like this should work. What am I missing?
Clarification
By "keep(ing) the information about how a class should be renamed with that class", what I mean is that the term Inner_X
should only appear in the Inner
class. It should not appear at all in the Outer
class.
Xml. Serialization namespace) class is used to serialize and deserialize. The class method Serialize is called. Since we have to serialize in a file, we create a " TextWriter ".
XML serialization is the process of converting XML data from its representation in the XQuery and XPath data model, which is the hierarchical format it has in a Db2® database, to the serialized string format that it has in an application.
XmlSerializer enables you to control how objects are encoded into XML. The XmlSerializer enables you to control how objects are encoded into XML, it has a number of constructors.
Serialization namespace contains several Attribute classes that can be applied to members of a class. For example, if a class contains a member that will be serialized as an XML element, you can apply the XmlElementAttribute attribute to the member.
When XmlSerializer
serializes a type, the type itself controls the names of the elements created for its properties. I.e. the property name becomes the element name, unless overridden statically by XmlElementAttribute.ElementName
. XmlTypeAttribute.TypeName
generally only controls the element name when an instance of the type to which it is applied is not being serialized as the property of some containing type -- for instance, when it is the root element, or when it is contained in a collection that is being serialized with an outer container element. This design avoids name collisions in cases where there are multiple properties of the same type within a given type.
However, there is an exception in the case of polymorphic property types. For these, XmlSerializer
has an option to use the XML type name of each of the possible polymorphic types as the element name, thereby identifying the actual c# type from which the element was created. To enable this functionality, one must add multiple [XmlElement(typeof(TDerived))]
attributes to the property, one for each possible type TDerived
.
You can use this capability to generate the XML you require by introducing a 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 is 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 multiple properties of the same type, this cannot be done.
One other option to think about: if you simply don't want to manually duplicate the "Inner_X"
type name strings in multiple locations (i.e. in both the [XmlType(string name)]
and [XmlElement(string name)]
attributes) you could centralize the type names by making them be 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 intend Inner to be an abstract base class, each subclass of which will serialize to different element names. If this is the case, then XmlSerializer
can indeed be made 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 multiple [XmlElement(typeof(TDerived))]
attributes. Thus the following classes will generate the XML you require:
[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
{
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With