C#: Best way to have XML element name from generic type name

落爺英雄遲暮 提交于 2019-12-31 04:37:14

问题


I want to create a xml for a generic class. One of the properties has the generic type. For this property I don't want to use the property name as its XML element name, but the name of the generic type.

The class looks like this:

[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>
    public TEntity Entity { get; set; }
}

When serializing a store item of kind StoreItem<SewageArea> the XML should contain something like:

<Entity Status="Deleted">
    <SewageArea ...>
       ...
    </SewageArea>
<Entity>

The requirement is, that the SewageArea in the above example should be serialized in the "normal" way. Another important thing is that if its possible the code should be prepared to automatically serializes new added properties at the StoreItemclass.


回答1:


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.



来源:https://stackoverflow.com/questions/38344516/c-best-way-to-have-xml-element-name-from-generic-type-name

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!