使用 Servicestack.Text 进行 XML 反序列化
Posted
技术标签:
【中文标题】使用 Servicestack.Text 进行 XML 反序列化【英文标题】:XML Deserialization with Servicestack.Text 【发布时间】:2013-02-03 04:07:57 【问题描述】:我正在学习 Servicestack.Text 库,因为它具有一些最好的功能。我正在尝试将 XML 反序列化为我的 DTO 之一,如下所示;
C#代码:[此处带有控制台应用程序的相关代码]
class Program
static void Main(string[] args)
string str = "http://static.cricinfo.com/rss/livescores.xml";
WebClient w = new WebClient();
string xml = w.DownloadString(str);
Response rss = xml.FromXml<Response>();
foreach (var item in rss.rss.Channel.item)
Console.WriteLine(item.title);
Console.Read();
您可以在str
[Given in the program] 处浏览 XML 文件。我已经为反序列化准备了 DTO。它们如下:
public class Response
public RSS rss get; set;
public class RSS
public string Version get; set;
public ChannelClass Channel get; set;
public class ChannelClass
public string title get; set;
public string ttl get; set;
public string description get; set;
public string link get; set;
public string copyright get; set;
public string language get; set;
public string pubDate get; set;
public List<ItemClass> item get; set;
public class ItemClass
public string title get; set;
public string link get; set;
public string description get; set;
public string guid get; set;
当我运行程序时,出现如下所示的异常:
所以,要更改 Element
和 namespace
,我做了以下解决方法:
我将DataContractAttribute
放在我的Response
类中,如下所示:
[DataContract(Namespace = "")]
public class Response
public RSS rss get; set;
我通过在反序列化之前添加以下两行来更改Element
的名称,如下所示
//To change rss Element to Response as in Exception
xml = xml.Replace("<rss version=\"2.0\">","<Response>");
//For closing tag
xml = xml.Replace("</rss>","</Response>");
但是,由于反序列化的 rss
对象是 null
,因此它在 foreach 循环中出现了另一个异常。那么,我应该如何使用Servicestack.Text
以正确的方式反序列化它?
注意:
我很了解如何使用其他库进行反序列化,我只想使用 ServiceStack 进行反序列化。
【问题讨论】:
【参考方案1】:TLDR: 使用XmlSerializer
反序列化您无法控制的xml 方言; ServiceStack
专为代码优先开发而设计,不能适应通用 xml 解析。
ServiceStack.Text 不 实现了自定义 Xml 序列化程序 - 它在后台使用 DataContractSerializer
。 FromXml
只是语法糖。
使用DataContractSerializer
解析Xml
如您所见,DataContractSerializer
对 命名空间 很挑剔。一种方法是在类上显式指定名称空间,但如果这样做,则需要在任何地方指定[DataMember]
,因为它假定如果有任何内容是显式的,那么一切都是。您可以使用程序集级属性(例如在 AssemblyInfo.cs 中)声明默认命名空间来解决此问题:
[assembly: ContractNamespace("", ClrNamespace = "My.Namespace.Here")]
这解决了命名空间问题。
但是,您无法使用 DataContractSerializer 解决其他 2 个问题:
它不会使用属性(在你的情况下,version
)
它要求 item
等集合同时具有包装名称和元素名称(类似于项目和项目)
您无法解决这些限制,因为DataContractSerializer
不是通用 XML 解析器。它旨在轻松生成和使用 API,而不是将任意 XML 映射到 .NET 数据结构。您将永远无法解析 RSS;所以因此 ServiceStack.Text (它只是包装它)也不能解析它。
改为使用XmlSerializer
。
使用XmlSerializer
这是相当直截了当的。您可以使用以下内容解析输入:
var serializer = new XmlSerializer(typeof(RSS));
RSS rss = (RSS)serializer.Deserialize(myXmlReaderHere);
诀窍是注释各个字段,使它们与您的 xml 方言匹配。例如,在您的情况下,这将是:
[XmlRoot("rss")]
public class RSS
[XmlAttribute]
public string version get; set;
public ChannelClass channel get; set;
public class ChannelClass
public string title get; set;
public string ttl get; set;
public string description get; set;
public string link get; set;
public string copyright get; set;
public string language get; set;
public string pubDate get; set;
[XmlElement]
public List<ItemClass> item get; set;
public class ItemClass
public string title get; set;
public string link get; set;
public string description get; set;
public string guid get; set;
因此,一些明智的属性足以让它根据需要解析 XML。
总而言之:您不能为此使用 ServiceStack,因为它使用 DataContractSerializer
.ServiceStack/DataContractSerializer
是为您控制架构的场景而设计的。 请改用XmlSerializer
。
【讨论】:
你的意思是我无法用servicestack
做到这一点?
您不能为此使用 ServiceStack.Text。但是要意识到 servicestack 的重点是 Web 服务;特别是您提供的服务。 .net 框架已经有一个很好的 xml 序列化器;它只是使用它。仅将 servicestack 用于其 XML 序列化并不会增加太多 - 您可以在几行代码中实现 FromXml<>
- 它并不比 new DataContractSerializer(typeof(T)).ReadObject(reader)
更多。因此,如果您无论如何都在使用 servicestack,那就太好了,但这并不是一个关键特性。
ServiceStack.Text 可以用 JSON 做到这一点——而且非常容易。
@DanEsparza ...当然,但是,RSS 不是 JSON?
@EamonNerbonne sheepish grin ...这会教会我注意【参考方案2】:
一些事情:
因为您使用的是 [DataContract] 属性。您必须在 [DataMember] 属性中包含 DTO 属性,否则它们将在序列化/反序列化过程中被跳过。使用 XML deserializing only works with namespace in xml
中指定的程序集属性您的 xml 操作需要更改以将 rss 包含在响应中或替换它。
xml = xml.Replace("<rss version=\"2.0\">", "<Response><rss version=\"2.0\">");
我建议您自己构建一个测试响应对象,使用 ServiceStack 的 .ToXml() 方法将其序列化为 XML 以查看它所期望的格式。您将看到服务堆栈将频道项目作为项目的子列表处理,这不是 RSS 格式化频道项目的方式。您必须将所有项目包装到一个名为 <ItemClass>
【讨论】:
1.试过了,结果还是一样 2.我没看懂 3.你建议的结构和我在问题中提出的一样,不是吗? 确保您的属性具有正确的大小写。您需要将“频道”更改为“频道”。另请参阅此要点gist.github.com/jokecamp/4979703,了解在您使用程序集时 ServiceStack 将如何序列化您的 DTO:ContractNamespace 属性。 如果我对某些大小写有误,那么它们应该只为空,对吗?我得到整个 rss 对象为空。 我更新了要点以包含一些测试代码gist.github.com/jokecamp/4979703。将其与您的比较并运行我的。 谢谢,但它不起作用。它没有正确反序列化它,它只给出版本和频道的空值以上是关于使用 Servicestack.Text 进行 XML 反序列化的主要内容,如果未能解决你的问题,请参考以下文章
更改 ServiceStack.Text JSON 反序列化器的输出
解决ServiceStack.Redis的6000次限制问题
ServiceStack.Text JsonSerializer 无法反序列化其自己的序列化模式(类型定义应以 '' SerializationException 开头)