从XML数据中反序列化数组(在ServiceStack中)

5

我有以下一段XML数据:

<ArrayOfRESTDataSource xmlns="http://SwitchKing.Common/Entities/RESTSimplified/2010/07" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<RESTDataSource>
    <Description>MyTest</Description>
    <Enabled>true</Enabled>
 </RESTDataSource>
</ArrayOfRESTDataSource>

RESTDataSource 可以出现 0-n 次。

以下是我的类:

[DataContract( Namespace = "http://SwitchKing.Common/Entities/RESTSimplified/2010/07" )]
public class ArrayOfRESTDataSource
{
    public RESTDataSource[] Data { set; get; }  
}


[DataContract( Namespace = "http://SwitchKing.Common/Entities/RESTSimplified/2010/07" )]
public class RESTDataSource
{
    [DataMember]
    public bool Enabled { get; set; }
    [DataMember]
    public string Description { get; set; }
}

我从服务器上读取了上述XML数据,方法如下:

WebRequest client = WebRequest.Create( "http://server:80/datasources" );
using( StreamReader sr = new StreamReader( client.GetResponse().GetResponseStream()) ) {
    string xml = sr.ReadToEnd();
var response = ServiceStack.Text.XmlSerializer.DeserializeFromString<ArrayOfRESTDataSource>( xml );
}

我的问题是:我需要更改或修饰public RESTDataSource[] Data的什么内容,才能使数组反序列化正常工作?对单个RESTDataSource项进行序列化可以正常工作,只有数组无法正常工作。

提前致谢。

更新1

根据@mythz的建议,我将代码更新为这样,但是response.Data仍然为空。我没有理解什么?

[DataContract( Namespace = "http://SwitchKing.Common/Entities/RESTSimplified/2010/07" )]
 public class ArrayOfRESTDataSource
{
    [DataMember]
    public DataSource Data { set; get; }
}

[CollectionDataContract( ItemName = "RESTDataSource" )]
public class DataSource : List<RESTDataSource>
{
    public DataSource() { }
    public DataSource( IEnumerable<RESTDataSource> collection ) : base( collection ) { }
}

更新2

解决方案在下面@mythz的答案中,但为了完整性和清晰度:我做错的是在我的DTOs中添加了另一个级别 - 顶级类ArrayOfRESTDataSource实际上有XML中的子项,因此它应该是集合类型。

1个回答

8

您可以使用[CollectionDataContract(...)]来修改数组/集合的序列化输出,详见前面提供的示例

调试序列化和集成问题

解决此类集成问题的第一步是隔离问题。即移除其他因素,只专注于问题本身。例如,在这种情况下,我会只关注 XML 和 DTO,并将它们与您的服务解耦。

ServiceStack 在底层使用 .NET 的 Xml DataContractSerializer,不会添加任何额外的转换或字节开销(只是原始 DTO 以原样进行序列化),因此如果您可以在服务之外使其正常工作,那么将相同的 DTO 放回到服务中也会在网络上传输。

方便的 ServiceStack 扩展方法

ServiceStack 提供了方便的扩展方法,用于序列化/反序列化和分析数据模型:

序列化/反序列化扩展方法

T.ToJson() / string.FromJson<T>()  //Serialize JSON
T.ToJsv() / string.FromJsv<T>()    //Serialize JSV  
T.ToXml() / string.FromXml<T>()    //Serialize XML

便捷的转储工具

漂亮的JSV转储格式递归打印对象图。

T.PrintDump()      

将一个字符串打印到控制台,并解析 string.Format() 参数(如果有的话)

string.Print(args)    

你的序列化DTO是什么样子的

当尝试创建DTO的形状时,首先应该做的一步是填充并打印它们,以查看其外观。查看XML输出块后,我得出了以下DTO:

[DataContract(Namespace = "http://SwitchKing.Common/Entities/RESTSimplified/2010/07")]
public class RESTDataSource
{
    [DataMember]
    public bool Enabled { get; set; }
    [DataMember]
    public string Description { get; set; }
}

[CollectionDataContract(ItemName = "RESTDataSource", Namespace = "http://SwitchKing.Common/Entities/RESTSimplified/2010/07")]
public class ArrayOfRESTDataSource : List<RESTDataSource>
{
    public ArrayOfRESTDataSource() { }
    public ArrayOfRESTDataSource(IEnumerable<RESTDataSource> collection) : base(collection) { }
}

然后填充并转储它们:
var dto = new ArrayOfRESTDataSource { 
    new RESTDataSource { Enabled = true, Description = "MyTest" } };

dto.ToXml().Print();

打印到控制台的内容:

<?xml version="1.0" encoding="utf-8"?><ArrayOfRESTDataSource xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://SwitchKing.Common/Entities/RESTSimplified/2010/07"><RESTDataSource><Description>MyTest</Description><Enabled>true</Enabled></RESTDataSource></ArrayOfRESTDataSource>

这看起来就是我们想要的。如果不是,请调整上述数据传输对象(DTO),直到您得到与预期XML相同的块。

当您拥有与XML相同形状的DTO时,可以开始尝试对它们进行序列化:

var dto = xml.FromXml<ArrayOfRESTDataSource>();
dto.PrintDump();

这将打印出漂亮的对象图:

[
    {
        Enabled: True,
        Description: MyTest
    }
]

如果所有字段都填充了预期值,那么您就完成了,并且可以更新ServiceStack Web服务DTO。

为什么会填充Data?它在上面的XML块中的哪里? - mythz
你的意思是将数据重命名为RESTDataSource吗? 我试过了,DataSource类会被实例化,但是没有项目添加到集合中。 - Per
已更新使用DTOs + 调试未来的序列化/集成问题的过程。 - mythz
哇,现在我感觉好蠢啊。当然应该是顶级类应该有CollectionDataContract,否则它就不匹配XML布局。非常感谢您写得非常好的答案mythz,我相信这些信息也会帮助其他人。 - Per

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接