我的应用程序序列化流中的对象。这是我需要的样品:
<links>
<link href="/users" rel="users" />
<link href="/features" rel="features" />
</links>
在这种情况下,该对象是"链接"对象的集合。
-----------第一个版本
起初,我使用DataContractSerializer,但是不能将成员序列化为属性(源)
目标是:
[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<>,哈希集<>。。。
为了实现这一点,我们需要告诉序列化程序要使用哪个根节点,这有点棘手。。。
1) 在可序列化对象上使用"XmlType"
[XmlType("link")]
public class LinkFinalVersion
{
[XmlAttribute("href")]
public string Url { get; set; }
[XmlAttribute("rel")]
public string Relationship { get; set; }
}
2) 编写一个"集合的智能根检测器"方法,该方法将返回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,大约需要80ms(只需要自定义根节点名称!)
XmlSerializer应该能够执行您需要的操作,但它高度依赖于初始结构和设置。我在自己的代码中使用它来生成非常相似的东西。
public class Links<Link> : BaseArrayClass<Link> //use whatever base collection extension you actually need here
{
//...stuff...//
}
public class Link
{
[XmlAttribute("href")]
public string Url { get; set; }
[XmlAttribute("rel")]
public string Relationship { get; set; }
}
现在,序列化Links
类应该会生成您想要的内容。
XmlSerializer的问题在于,当您给它提供泛型时,它会用泛型进行响应。将implemets数组列在其中的某个位置,序列化的结果几乎总是ArrayOf<X>
。为了避免这种情况,您可以命名属性或类根。您所需要的可能是示例中的第二个版本。我假设您尝试直接序列化一个对象列表链接。这不起作用,因为您没有指定根节点。现在,可以在这里找到类似的方法。在本例中,它们在声明序列化程序时指定XmlRootAttribute。你的应该是这样的:
XmlSerializer xs = new XmlSerializer(typeof(List<Link>), new XmlRootAttribute("Links"));
给你。。。
class Program
{
static void Main(string[] args)
{
Links ls = new Links();
ls.Link.Add(new Link() { Name = "Mike", Url = "www.xml.com" });
ls.Link.Add(new Link() { Name = "Jim", Url = "www.xml.com" });
ls.Link.Add(new Link() { Name = "Peter", Url = "www.xml.com" });
XmlSerializer xmlSerializer = new XmlSerializer(typeof(Links));
StringWriter stringWriter = new StringWriter();
xmlSerializer.Serialize(stringWriter, ls);
string serializedXML = stringWriter.ToString();
Console.WriteLine(serializedXML);
Console.ReadLine();
}
}
[XmlRoot("Links")]
public class Links
{
public Links()
{
Link = new List<Link>();
}
[XmlElement]
public List<Link> Link { get; set; }
}
[XmlType("Link")]
public class Link
{
[XmlAttribute("Name")]
public string Name { get; set; }
[XmlAttribute("Href")]
public string Url { get; set; }
}