C#Xml序列化,集合和根元素

我的应用程序序列化流中的对象。 以下是我需要的样本:

    

在这种情况下,对象是“链接”对象的集合。

———–第一版

起初我使用了DataContractSerializer ,但是你无法将成员序列化为属性( 源代码 )

这是对象:

 [DataContract(Name="link")] public class LinkV1 { [DataMember(Name="href")] public string Url { get; set; } [DataMember(Name="rel")] public string Relationship { get; set; } } 

这是结果:

   /users users   /features features   

———–第二版

好吧,不要安静我想要的东西,所以我尝试了经典的XmlSerializer ,但是……哦,不,如果根元素是一个集合,你不能指定根元素和集合元素的名称……

这是代码:

 [XmlRoot("link")] public class LinkV2 { [XmlAttribute("href")] public string Url { get; set; } [XmlAttribute("rel")] public string Relationship { get; set; } } 

结果如下:

      

———–第三版

使用XmlSerializer +一个根元素:

 [XmlRoot("trick")] public class TotallyUselessClass { [XmlArray("links"), XmlArrayItem("link")] public List Links { get; set; } } 

结果如下:

         

很好,但我不想要那个根节点!! 我希望我的集合成为根节点。

以下是对比:

  • 序列化代码是通用的,它适用于任何可序列化的
  • 逆操作(反序列化)也必须工作
  • 我不想正则表达式结果(我直接在输出流中序列化)

我现在有什么解决方案:

  1. 编码我自己的XmlSerializer
  2. 当它与集合一起工作时欺骗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需要3ms。 如果指定XmlRootAttribute,则需要大约80ms(只是为了拥有自定义根节点名称!)

XmlSerializer应该能够做你需要的,但它高度依赖于初始结构和设置。 我在自己的代码中使用它来生成非常相似的东西。

 public class Links : BaseArrayClass //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的问题是当你给它generics时,它会用generics作为响应。 List的实现Array在那里的某个地方,序列化的结果几乎总是ArrayOf 。 要解决这个问题,您可以命名属性或类根。 您需要的内容可能是您示例中的第二版。 我假设您尝试直接序列化对象列表链接。 这不起作用,因为您没有指定根节点。 现在,可以在这里找到类似的方法。 在这一个中,他们在声明序列化程序时指定XmlRootAttribute。 你的看起来像这样:

 XmlSerializer xs = new XmlSerializer(typeof(List), 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(); } [XmlElement] public List Link { get; set; } } [XmlType("Link")] public class Link { [XmlAttribute("Name")] public string Name { get; set; } [XmlAttribute("Href")] public string Url { get; set; } }