使用 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; 

当我运行程序时,出现如下所示的异常:

所以,要更改 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>");

但是,由于反序列化的 rss 对象是 null,因此它在 foreach 循环中出现了另一个异常。那么,我应该如何使用Servicestack.Text 以正确的方式反序列化它?

注意:

我很了解如何使用其他库进行反序列化,我只想使用 ServiceStack 进行反序列化。

【问题讨论】:

【参考方案1】:

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


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

使用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&lt;&gt; - 它并不比 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("&lt;rss version=\"2.0\"&gt;", "&lt;Response&gt;&lt;rss version=\"2.0\"&gt;");

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

    的节点中

【讨论】:

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 开头)

2021-07-07 .NET高级班 95-Redis分布式缓存 ServiceStack的破解

使用Redis的基本操作

如何让 ServiceStack 序列化/反序列化具有正确类型的 expando 对象