【问题标题】:C#: Best way to have XML element name from generic type nameC#:从泛型类型名称中获取 XML 元素名称的最佳方法
【发布时间】:2016-07-13 06:51:47
【问题描述】:

我想为一个泛型类创建一个 xml。其中一个属性具有泛型类型。对于这个属性,我不想使用属性名称作为它的 XML 元素名称,而是使用泛型类型的名称。

类如下所示:

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

当序列化类型为StoreItem&lt;SewageArea&gt; 的商店项目时,XML 应包含以下内容:

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

要求是,上例中的SewageArea 应该以“正常”的方式进行序列化。另一个重要的事情是,如果可能的话,代码应该准备好自动序列化StoreItemclass 中新添加的属性。

【问题讨论】:

  • 这并不容易。你想按照Rename class when serializing to XML 做一些事情,但你不能,因为属性参数不能包含泛型类型参数,即[XmlElement(typeof(TEntity))] 不会编译。
  • 我知道,这并不简单。我想过实现 IXmlSerialisable 接口,但在执行实体对象的“正常”序列化和其他属性的自动序列化时挂起。

标签: c# xml generics xml-serialization


【解决方案1】:

你想按照Rename class when serializing to XML 做一些事情,但你不能,因为属性参数不能包含泛型类型参数,即[XmlElement(typeof(TEntity))]。实现IXmlSerializable 的明显替代方案很不方便,因为您会丢失随后添加到StoreItem&lt;TEntity&gt; 的属性的自动序列化。

相反,您可以使用[XmlAnyElement] 代理属性对您的TEntity 进行嵌套序列化,如下所示:

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

使用扩展方法:

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

请注意[XmlAnyElement] 属性将为所有未知元素调用,因此如果您的 XML 出于某种原因包含意外元素,您可能会因为根元素名称错误而从XObjectExtensions.Deserialize&lt;TEntity&gt;(value)) 引发异常。如果有可能,您可能希望捕获并忽略此方法的异常。

那么,对于示例TEntity

public class SewageArea
{
    public double Area { get; set; }
}

XML 输出为:

<Entity Status="State1">
  <SewageArea>
    <Area>10101</Area>
  </SewageArea>
</Entity>

示例fiddle

【讨论】:

  • 感谢您的详细回复。我会实施并测试它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-04
相关资源
最近更新 更多