C#:从泛型类型名称中获取 XML 元素名称的最佳方式
C#: Best way to have XML element name from generic type name
我想为通用 class 创建一个 xml。其中一个属性具有通用类型。对于这个 属性 我不想使用 属性 名称作为它的 XML 元素名称,而是泛型类型的名称。
class 看起来像这样:
[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<SewageArea>
类型的商店项目时,XML 应该包含如下内容:
<Entity Status="Deleted">
<SewageArea ...>
...
</SewageArea>
<Entity>
要求是,上面例子中的SewageArea
要按照"normal"的方式序列化。另一件重要的事情是,如果可能的话,代码应该准备好在 StoreItem
class.
处自动序列化新添加的属性。
您想按照 的方式做一些事情,但您不能这样做,因为属性参数不能包含泛型类型参数,即 [XmlElement(typeof(TEntity))]
。实施 IXmlSerializable
的明显替代方案很不方便,因为您失去了随后添加到 StoreItem<TEntity>
.
的属性的自动序列化
相反,您可以使用 [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<TEntity>(value))
因为根元素名称错误。如果可能的话,您可能希望捕获并忽略来自此方法的异常。
然后,对于样本 TEntity
class
public class SewageArea
{
public double Area { get; set; }
}
XML 输出为:
<Entity Status="State1">
<SewageArea>
<Area>10101</Area>
</SewageArea>
</Entity>
示例 fiddle.
我想为通用 class 创建一个 xml。其中一个属性具有通用类型。对于这个 属性 我不想使用 属性 名称作为它的 XML 元素名称,而是泛型类型的名称。
class 看起来像这样:
[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<SewageArea>
类型的商店项目时,XML 应该包含如下内容:
<Entity Status="Deleted">
<SewageArea ...>
...
</SewageArea>
<Entity>
要求是,上面例子中的SewageArea
要按照"normal"的方式序列化。另一件重要的事情是,如果可能的话,代码应该准备好在 StoreItem
class.
您想按照 [XmlElement(typeof(TEntity))]
。实施 IXmlSerializable
的明显替代方案很不方便,因为您失去了随后添加到 StoreItem<TEntity>
.
相反,您可以使用 [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<TEntity>(value))
因为根元素名称错误。如果可能的话,您可能希望捕获并忽略来自此方法的异常。
然后,对于样本 TEntity
class
public class SewageArea
{
public double Area { get; set; }
}
XML 输出为:
<Entity Status="State1"> <SewageArea> <Area>10101</Area> </SewageArea> </Entity>
示例 fiddle.