9

我正在学习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; }
}

当我运行程序时,我得到一个异常,如下所示:

在此处输入图像描述

因此,要更改Elementnamespace,我做了以下解决方法:

我把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 来做。

4

2 回答 2

24

TLDR:用于XmlSerializer反序列化您无法控制的 xml 方言;ServiceStack专为代码优先开发而设计,不能适应通用 xml 解析。


ServiceStack.Text没有实现自定义 Xml 序列化程序 - 它DataContractSerializer在后台使用。 FromXml只是语法糖。

DataContractSerializer用于解析Xml

正如您所注意到的,对命名空间DataContractSerializer很挑剔。一种方法是在类上显式指定命名空间,但如果这样做,则需要在任何地方指定,因为它假定如果有任何内容是显式的,那么一切都是。您可以使用程序集级属性(例如在 AssemblyInfo.cs 中)声明默认命名空间来解决此问题:[DataMember]

[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

于 2013-02-21T18:19:13.167 回答
0

一些东西:

  1. 由于您使用的是 [DataContract] 属性。您必须在 [DataMember] 属性中包含 DTO 属性,否则它们将在序列化/反序列化过程中被跳过。使用 XML 反序列化中指定的程序集属性仅适用于 xml 中的命名空间

  2. 您的 xml 操作需要更改以将 rss 包装在响应中或替换它。

    xml = xml.Replace("<rss version=\"2.0\">", "<Response><rss version=\"2.0\">");

  3. 我建议您自己构建一个测试响应对象,使用 ServiceStack 的 .ToXml() 方法将其序列化为 XML 以查看它所期望的格式。您将看到服务堆栈将频道项目作为子项目列表处理,这不是 RSS 格式化频道项目的方式。您必须将所有项目包装到一个名为<ItemClass>

于 2013-02-18T18:50:30.867 回答