You'd like to do something along the lines of Rename class when serializing to XML but you cannot because attribute arguments cannot contain generic type parameters, i.e. [XmlElement(typeof(TEntity))]
. And the obvious alternative of implementing IXmlSerializable
is inconvenient because you lose automatic serialization of properties subsequently added to StoreItem<TEntity>
.
Instead, what you can do is to make use of an [XmlAnyElement]
surrogate property to do a nested serialization of your TEntity
, as follows:
[XmlRoot("Entity")]
public class StoreItem<TEntity>
where TEntity : class, new()
{
/// <summary>
/// Gets and sets the status of the entity when storing.
/// </summary>
[XmlAttribute]
public System.Data.Services.Client.EntityStates Status { get; set; }
/// <summary>
/// Gets and sets the entity to be stored.
/// </summary>
[XmlIgnore]
public TEntity Entity { get; set; }
[XmlAnyElement]
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]
public XElement XmlEntity
{
get
{
return (Entity == null ? null : XObjectExtensions.SerializeToXElement(Entity, null, true));
}
set
{
Entity = (value == null ? null : XObjectExtensions.Deserialize<TEntity>(value));
}
}
}
Using the extension methods:
public static class XObjectExtensions
{
public static T Deserialize<T>(this XContainer element)
{
return element.Deserialize<T>(null);
}
public static T Deserialize<T>(this XContainer element, XmlSerializer serializer)
{
using (var reader = element.CreateReader())
{
serializer = serializer ?? new XmlSerializer(typeof(T));
object result = serializer.Deserialize(reader);
if (result is T)
return (T)result;
}
return default(T);
}
public static XElement SerializeToXElement<T>(this T obj)
{
return obj.SerializeToXElement(null, true);
}
public static XElement SerializeToXElement<T>(this T obj, XmlSerializer serializer, bool omitStandardNamespaces)
{
var doc = new XDocument();
using (var writer = doc.CreateWriter())
{
XmlSerializerNamespaces ns = null;
if (omitStandardNamespaces)
(ns = new XmlSerializerNamespaces()).Add("", ""); // Disable the xmlns:xsi and xmlns:xsd lines.
serializer = serializer ?? new XmlSerializer(obj.GetType());
serializer.Serialize(writer, obj, ns);
}
var element = doc.Root;
if (element != null)
element.Remove();
return element;
}
}
Note that the [XmlAnyElement]
property will be called for all unknown elements, so if your XML for some reason has unexpected elements, you may get an exception thrown from XObjectExtensions.Deserialize<TEntity>(value))
because the root element name is wrong. You may want to catch and ignore exceptions from this method if that is a possibility.
Then, for the sample TEntity
class
public class SewageArea
{
public double Area { get; set; }
}
The XML output is:
<Entity Status="State1">
<SewageArea>
<Area>10101</Area>
</SewageArea>
</Entity>
Sample fiddle.