跨 WCF Web 服务请求序列化通用 XML 数据

Posted

技术标签:

【中文标题】跨 WCF Web 服务请求序列化通用 XML 数据【英文标题】:serializing generic XML data across WCF web service requests 【发布时间】:2010-10-30 10:08:36 【问题描述】:

我有一个向 WCF 服务发送请求的 Web 应用程序。 WCF 服务获取一个 LINQ 结果集(anon.ilist)并将其作为回复发送回 Web 应用程序。 为了让它快速运行,在 WCF 应用程序中,我使用 copytodatatable 并将其发送到 DataSet 中的我的网络应用程序。

然后,我的 Web 应用程序获取 DataSet 并将其写入 xml,执行一些 xslt 并将结果数据显示在屏幕上。完美的。 ...嗯,不是真的。

我还是(相对)WCF 新手。我知道发送 DataTables/DataSets 有点麻烦。 Web 应用程序需要 xml 格式的数据(用于 xslt 操作),所以我想我会让 WCF Web 服务为我完成 DataTable -> xml 工作,然后简单地用一个漂亮的 XmlDocument 回复客户端 webapp。 但是不能序列化 XmlDocument。

通过 wcf 将 XML 数据发送到客户端的最佳方式是什么?

数据表包含很多列,并且会经常更改,所以我不想创建自己的对象类(具有无数属性)并发送它(因为这是我所做的以前在 wcf 中的大部分时间,它运行良好......但不适合这种情况)。

编辑:将其作为字符串发送也可以....但这肯定不是最佳解决方案吗?

【问题讨论】:

【参考方案1】:

通过 WCF 发送数据集的唯一问题是它不可互操作 - 即您无法从非 .NET 客户端使用数据集。

我在几个大型应用程序中工作过,这些应用程序通过 WCF 通过网络发送数据集就很好。只要发送方和接收方都在 .NET 上运行,您应该没问题。

如果您仍然不想使用数据集,请考虑创建一个 DataContract 类来保存您的数据以通过网络发送。这是跨 WCF 传输数据的经典方式。除非您的 XML 非常复杂(很可能是这样),否则这可能是一个不错的选择。

如果您还没有,我强烈建议您阅读 Juval Lowy 出色的“Programming WCF Services 2nd Edition”的副本。他有一整章是关于跨 WCF 传输信息的。这是 WCF 圣经。

【讨论】:

这本书在我面前... :) 这是一本超级密集的书。我建议把第 1 章读几遍,尽管我认为第 3 章是你感兴趣的。不过,不要把我的副本放在我面前。 :)【参考方案2】:

您可以简单地返回一个 XmlElement - 可能是 XmlDocument 实例的 DocumentElement 属性。

您不必use the XmlSerializer to do this。 DataContractSerializer 是默认值,并且会很好地发回 XmlElement。您不需要实现 IXmlSerializable。

下面是一些示例代码

服务接口:

using System;
using System.Xml;
using System.ServiceModel;
using System.Runtime.Serialization;

namespace Cheeso.Samples.Webservices

    [ServiceContract(Namespace="urn:Cheeso.Samples" )]
    public interface IService
    
        [OperationContract]
        XmlElement Register(String request);
    
 

请注意,我没有 DataContract(因此也没有 DataMembers),因为我正在发回一个预定义类 (XmlElement) 的实例。

这是服务实现:

using System;
using System.Xml;
using System.ServiceModel;

namespace Cheeso.Samples.Webservices._2009Jun01


    public class Results
    
        public int Id;
        public Int64 WorkingSet;
        public String Name;
        public String Title;
    

    [ServiceBehavior(Name="WcfXmlElementService",
                     Namespace="urn:Cheeso.Samples",
                     IncludeExceptionDetailInFaults=true)]

    public class WcfXmlElementService : IService
    
        int index = 0;
        public XmlElement Register(string request)
        
            XmlDocument doc = new XmlDocument();

            // can get the doc from anywhere. We use a LINQ-to-Objects result.

       // do the LINQ thing
       var processInfo =
       from p in System.Diagnostics.Process.GetProcesses()
       select new Results 
           Id = p.Id,
           WorkingSet = p.WorkingSet64,
           Name = p.ProcessName,
           Title = p.MainWindowTitle
           ;

       // Note: cannot use an anonymous ilist if we will use XmlSerializer

       // serialize that list into the XmlDocument
       using (XmlWriter writer = doc.CreateNavigator().AppendChild())
       
       var L = processInfo.ToList();
       XmlSerializer s1 = new XmlSerializer(L.GetType());
       s1.Serialize(writer, L);
       

       index++;

       // Append some additional elements to the in-memory document.
       XmlElement elem = doc.CreateElement("id");
       elem.InnerText = System.Guid.NewGuid().ToString();
       doc.DocumentElement.AppendChild(elem);

       elem = doc.CreateElement("stamp");
       elem.InnerText = DateTime.Now.ToString("G");
       doc.DocumentElement.AppendChild(elem);

       elem = doc.CreateElement("in-reply-to");
       elem.InnerText = request;
       doc.DocumentElement.AppendChild(elem);

            return doc.DocumentElement;
        
    

如果您使用的是 .NET,客户端会获取一个 XmlElement。如果您使用的是其他堆栈,则它将只是该堆栈中的 XmlElement 或 XmlNode。

回复消息的 XSD 是通用的,如下所示:

<xs:element name="RegisterResponse">
  <xs:complexType>
    <xs:sequence>
      <xs:element minOccurs="0" name="RegisterResult" nillable="true">
        <xs:complexType>
          <xs:sequence>
            <xs:any minOccurs="0" processContents="lax" /> 
          </xs:sequence>
        </xs:complexType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
</xs:element>

【讨论】:

【参考方案3】:

方法返回的类需要实现IXmlSerializable。

public XmlContent XmlContent()

    return new XmlContent();


[XmlRoot(ElementName = "div")]
public class XmlContent : IXmlSerializable


    #region IXmlSerializable Members

    public System.Xml.Schema.XmlSchema GetSchema()
    
        return null;
    

    public void ReadXml(XmlReader reader)
    
        throw new NotImplementedException();
    

    public void WriteXml(XmlWriter writer)
    

    writer.WriteRaw("<p>some text</p>");

    
    #endregion

我还没有找到如何避免序列化根元素(例如,“div”)。

我也尝试将 XML 作为字符串发送,但它没有按预期输出,即“字符串”是 html 编码并包装在标签中。这使得解析非常困难。

【讨论】:

【参考方案4】:

为什么不尝试将 xml 作为字符串发送?

【讨论】:

发生在我身上,但我只是假设由于数据是 xml,所以 xmldocument 将是最好的数据类型....我已经尝试了几种数据类型工作,我的应用程序确实工作。我只是在寻找最好的、最佳实践的方法。

以上是关于跨 WCF Web 服务请求序列化通用 XML 数据的主要内容,如果未能解决你的问题,请参考以下文章

具有跨域 Ajax 的 WCF Web 服务不起作用

C# 中 WCF 和 MVC Web API 的通用请求响应拦截器

C# Restful WCF 服务。无法在帖子正文中反序列化 XML

如何自定义 WCF XML 序列化

Web Service 或 WCF调用时读取 XML 数据时,超出最大字符串内容长度配额(8192)解决方法

WCF XML反序列化不填充数组