I'm using an XmlSerializer
. My class:
[Serializable]
[XmlRoot(ElementName="MyClass")]
public class MyClass
{
public string Value;
}
I would like to serialize it so that Value
ends up as an attribute of a subelement named (for instance) "Text".
Desired outcome:
<MyClass>
<Text Value="3"/>
</MyClass>
But NOT (which would be the effect of marking Value as an XmlAttribute
)
<MyClass Value="3">
</MyClass>
And NOT (which would be the effect of marking Value as an XmlElement
):
<MyClass>
<Value>3</Value>
</MyClass>
How do I achieve this?
I am aware that I could change the type of Value
from string to another serializable custom class.
Unfortunately, I have plenty of such properties, so I'd need to create dozens of tiny classes.
Is there any quicker solution?
EDIT:
In response to your comments:
No, not every property has to be serialized to a subelement named "Text". Subelement's name is unique and unambiguous.
Sample output XML:
<visibility>
<site visible="yes"/>
<comparator visible="no"/>
<expiration days="7"/>
<comment>blahblahblah</comment>
<visibility>
Sample class:
!
[XmlRoot(ElementName="Visibility")]
public class Visibility
{
[XPath("/site@visible")] // if only this was possible!
public string OnSite
{
get { return SiteVisible ? "yes" : "no"; }
}
[XPath("/comparator@visible")] // as above...
public string InComparator
{
get { return ComparatorVisible ? "yes" : "no"; }
}
[XmlIgnore]
public bool SiteVisible;
[XmlIgnore]
public bool ComparatorVisible;
[XPath("/expiration@days")] // as above...
public int ExpiresAfterDays;
[XmlElement("comment")] // this is easy
public string Comment;
}
Serialization/ De-serialization allow communication with another application by sending and receiving data. With XmlSerializer, you can control how objects are encoded into XML. Call the Serialize method with the parameters of the StreamWriter and object to serialize.
The XmlSerializer creates C# (. cs) files and compiles them into . dll files in the directory named by the TEMP environment variable; serialization occurs with those DLLs. These serialization assemblies can be generated in advance and signed by using the SGen.exe tool.
By applying the XmlRootAttribute, you can control the XML stream generated by the XmlSerializer. For example, you can change the element name and namespace.
Public class declarations. Controls XML serialization of the attribute target as an XML root element.
Without changing the type of Value
I think it's not possible. You can add the attribute XmlElement(ElementName="Text")
on Value
but you will obtain a result similar to this:
<MyClass>
<Text>3</Text>
</MyClass>
Edited: Another solution can involve XSLT transformation: you can generate the xml using .Net serialization and after apply a xml transformation.
XslTransform myXslTransform = new XslTransform();
myXslTransform.Load(xsltDoc);
myXslTransform.Transform(sourceDoc, resultDoc);
The trasformation of my example should be something like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<root>
<xsl:apply-templates/>
</root>
</xsl:template>
<xsl:template match="MyClass">
<MyClass>
<Text>
<xsl:attribute name="Value">
<xsl:value-of select="Text"/>
</xsl:attribute>
</Text>
</MyClass>
</xsl:template>
</xsl:stylesheet>
For this sort of flexibility, you should really think about implementing IXmlSerializable
as this gives you much more control:
[XmlRoot("visibility")]
public class Visibility : IXmlSerializable
{
public string Site;
public string Comparator;
public int Expiration;
public string Comment;
public XmlSchema GetSchema()
{
throw new NotImplementedException();
}
public void ReadXml(XmlReader reader)
{
// implement me if you want to deserialize too.
throw new NotImplementedException();
}
public void WriteXml(XmlWriter writer)
{
WriteProperty(writer, "site", "visible", Site);
WriteProperty(writer, "comparator ", "visible", Comparator);
WriteProperty(writer, "expiration ", "days", Expiration);
if (!string.IsNullOrEmpty(Comment))
{
writer.WriteElementString("comment", Comment);
}
}
private void WriteProperty<T>(XmlWriter writer, string elementName, string attibuteName, T value)
{
if (value != null)
{
writer.WriteStartElement(elementName);
writer.WriteAttributeString(attibuteName, value.ToString());
writer.WriteEndElement();
}
}
}
Obviously, there is bit of manual work here, but it does allow you to keep all the serialization code in one place, rather than having a proliferation of smaller classes.
The example above only implements serialization - you'd need to write an equivalent deserialize implementation if you need to deserialize from xml to your type.
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