我的应用序列化流中的对象。 这是我需要的示例:
<links>
<link href="/users" rel="users" />
<link href="/features" rel="features" />
</links>
在这种情况下,该对象是“链接”对象的集合。
------------第一个版本
起初我使用了DataContractSerializer,但是你不能将成员序列化为属性(source)
这是对象:
[DataContract(Name="link")]
public class LinkV1
{
[DataMember(Name="href")]
public string Url { get; set; }
[DataMember(Name="rel")]
public string Relationship { get; set; }
}
结果如下:
<ArrayOflink xmlns:i="...." xmlns="...">
<link>
<href>/users</href>
<rel>users</rel>
</link>
<link>
<href>/features</href>
<rel>features</rel>
</link>
</ArrayOflink>
------------ 第二个版本
好吧,不是我想要的安静,所以我尝试了经典的 XmlSerializer,但是......哦不,如果根元素,你不能指定根元素和集合元素的名称是一个集合...
代码如下:
[XmlRoot("link")]
public class LinkV2
{
[XmlAttribute("href")]
public string Url { get; set; }
[XmlAttribute("rel")]
public string Relationship { get; set; }
}
结果如下:
<ArrayOfLinkV2>
<LinkV2 href="/users" rel="users" />
<LinkV2 href="/features" rel="features" />
<LinkV2 href="/features/user/{keyUser}" rel="featuresByUser" />
</ArrayOfLinkV2>
------------ 第三个版本
使用 XmlSerializer + 根元素:
[XmlRoot("trick")]
public class TotallyUselessClass
{
[XmlArray("links"), XmlArrayItem("link")]
public List<LinkV2> Links { get; set; }
}
及其结果:
<trick>
<links>
<link href="/users" rel="users" />
<link href="/features" rel="features" />
<link href="/features/user/{keyUser}" rel="featuresByUser" />
</links>
</trick>
很好,但我不想要那个根节点!! 我希望我的集合成为根节点。
这里是限制条件:
- 序列化代码是通用的,它适用于任何可序列化的东西
- 逆向操作(反序列化)也必须有效
- 我不想对结果进行正则表达式(我直接在输出流中序列化)
我现在的解决方案是什么:
- 编写我自己的 XmlSerializer
- 在 XmlSerializer 处理集合时使用技巧(我试过,让它找到一个 XmlRootElement 并将其复数化以生成它自己的 XmlRootAttribute,但这在反序列化时会导致问题 + 项目名称仍然保留类名称)
有什么想法吗?
在这个问题上真正困扰我的是,我想要的似乎真的非常非常简单...
最佳答案
好的,这是我的最终解决方案(希望它对某人有所帮助),它可以序列化一个普通数组,List<>、HashSet<>...
要实现这一点,我们需要告诉序列化程序使用哪个根节点,这有点棘手...
1) 在可序列化对象上使用“XmlType”
[XmlType("link")]
public class LinkFinalVersion
{
[XmlAttribute("href")]
public string Url { get; set; }
[XmlAttribute("rel")]
public string Relationship { get; set; }
}
2) 编写一个“smart-root-detector-for-collection”方法,它将返回一个 XmlRootAttribute
private XmlRootAttribute XmlRootForCollection(Type type)
{
XmlRootAttribute result = null;
Type typeInner = null;
if(type.IsGenericType)
{
var typeGeneric = type.GetGenericArguments()[0];
var typeCollection = typeof (ICollection<>).MakeGenericType(typeGeneric);
if(typeCollection.IsAssignableFrom(type))
typeInner = typeGeneric;
}
else if(typeof (ICollection).IsAssignableFrom(type)
&& type.HasElementType)
{
typeInner = type.GetElementType();
}
// yeepeeh ! if we are working with a collection
if(typeInner != null)
{
var attributes = typeInner.GetCustomAttributes(typeof (XmlTypeAttribute), true);
if((attributes != null)
&& (attributes.Length > 0))
{
var typeName = (attributes[0] as XmlTypeAttribute).TypeName + 's';
result = new XmlRootAttribute(typeName);
}
}
return result;
}
3) 将该 XmlRootAttribute 推送到序列化器中
// hack : get the XmlRootAttribute if the item is a collection
var root = XmlRootForCollection(type);
// create the serializer
var serializer = new XmlSerializer(type, root);
我告诉过你这很棘手 ;)
要改善这一点,您可以:
A) 创建一个 XmlTypeInCollectionAttribute 以指定自定义根名称(如果基本复数形式不符合您的需要)
[XmlType("link")]
[XmlTypeInCollection("links")]
public class LinkFinalVersion
{
}
B) 如果可能,缓存您的 XmlSerializer(例如在静态字典中)。
在我的测试中,实例化一个没有 XmlRootAttributes 的 XmlSerializer 需要 3 毫秒。 如果您指定 XmlRootAttribute,大约需要 80 毫秒(只是为了拥有自定义根节点名称!)
关于C# Xml序列化、集合和根元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11781606/