我正在学习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
[在程序中指定]浏览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>");
但是,它在foreach循环上给出了另一个例外,因为反序列化的rss
对象是null
。那么,我应该如何使用Servicestack.Text
?
注意:
我很清楚如何使用其他库进行反序列化,我只想使用ServiceStack。
答案 0 :(得分:21)
TLDR:使用XmlSerializer
从您无法控制的xml方言反序列化; ServiceStack
专为代码优先开发而设计,无法适用于通用xml解析。
ServiceStack.Text 不实现自定义Xml序列化程序 - 它使用了DataContractSerializer
。 FromXml
仅仅是语法糖。
DataContractSerializer
解析Xml 正如您所注意到的,DataContractSerializer
对于命名空间非常挑剔。一种方法是在类上明确指定命名空间,但如果这样做,则需要在任何地方指定[DataMember]
,因为它假定如果有任何内容是明确的,那么一切都是。您可以使用程序集级属性(例如在AssemblyInfo.cs中)来解决此问题,以声明默认命名空间:
[assembly: ContractNamespace("", ClrNamespace = "My.Namespace.Here")]
这解决了命名空间问题。
但是,您无法解决DataContractSerializer的其他两个问题:
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
。
答案 1 :(得分:0)
一些事情:
由于您使用的是[DataContract]属性。必须将DTO属性包含在[DataMember]属性中,否则将在序列化/反序列化过程中跳过它们。使用XML deserializing only works with namespace in xml
您的xml操作需要更改以将rs包装在响应中或替换它。
xml = xml.Replace("<rss version=\"2.0\">", "<Response><rss version=\"2.0\">");
我建议您自己构建一个测试Response对象,使用ServiceStack的.ToXml()方法将其serilialize为XML,以查看它所期望的格式。您将看到服务堆栈将通道项目处理为项目的子列表,而不是RSS格式化通道项目的方式。您必须将所有项目包装到名为<ItemClass>