【问题标题】:Returning a complex type from its XML representation in a .NET web service从 .NET Web 服务中的 XML 表示返回复杂类型
【发布时间】:2009-04-23 20:22:07
【问题描述】:

我在数据库中有一个 XML 文档,它是某个类 Foo 的实例的 XML 序列化表示:

<?xml version="1.0" encoding="utf-16"?>
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  <StringProp>hello</StringProp>
  <IntProp>5</IntProp>
</Foo>

假设现在我想编写一个返回 Foo 实例的 ASP.NET Web 服务方法,如下所示:

[WebMethod]
public Foo GimmeFoo()
{
}

问题:我想返回序列化的 Foo,但是没有先在 GimmeFoo() 方法中反序列化它(现实生活中的 Foo 相当大,反序列化它会感觉很愚蠢DB 然后很快就会被 SOAP 精灵自动重新序列化)。

是否有任何属性可以让我在 Web 服务(或我可以在其主体中编写的一些代码)上添加任何属性,让我无需先反序列化就可以发送 Foo 的实例?

【问题讨论】:

  • Guido - 如果你切换到 WCF,你可以(如果你真的想)使用可能会实现这一点的无类型“消息”类型参数 - 你可以手动编写你的消息体(作为 XML)。不过,我认为 ASP.NET Web 服务不会提供这种功能。

标签: asp.net xml web-services serialization soap


【解决方案1】:

这假定只有已知的客户端会调用此服务,因为其他语言可能无法反序列化此 xml 文件。

在我看来,如果是这样的话,那么为什么不使用一个 REST 服务来传回数据,因为您没有正确使用 Web 服务,IMO。例如,如果我使用 GSOAP(C 语言)或 PHP 作为客户端,您的 xml 文件是否有用,因为我需要自己反序列化它。

反序列化然后让 Web 服务重新序列化可能会很痛苦,但是 IMO,如果您必须使用 Web 服务,那又是正确的方法。

此外,如果您将这个序列化的 XML 文件传回,那么客户端和服务器之间的耦合非常强,这在 IMO 中也是一个糟糕的设计。

【讨论】:

  • 我拥有的 XML 文件是使用 System.Xml.Serialization.XmlSerializer 序列化的,这与 SOAP 基础结构使用的相同。难道真的没有办法告诉ASP.NET“看,这东西是为你预序列化的,随便用吧”?
  • 您可能想查看此内容,了解有关 XmlSerializer 的更多信息以及常规序列化和 SOAP 序列化之间的区别。 codeproject.com/KB/XML/Serialization_Samples.aspx 看来您可能需要使用自己的类来返回soap响应,因为我不知道有人预先生成soap响应然后返回它。
【解决方案2】:

您可以从 Web 服务返回 XmlElement 或 XmlDocument 类型。只需返回您的序列化 XML。

【讨论】:

    【解决方案3】:

    我找到了一种可能不是最优雅的方法,但可以完成工作。这个想法是将所有工作委托给自定义的 SoapExtension 派生类,实际上在 WebMethod 本身中什么也不做 - WebMethod 只是作为调用的端点:

    [WebMethod]
    public Foo GimmeFoo()
    {
         return null;
    }
    

    但神奇之处在于:您编写了一个拦截所有 SOAP 流量的 SoapExtension,当 SoapMessageStage.AfterSerialize 阶段到达时,您将已经序列化的有效负载插入其中:

    public class SerializationPassThrough : SoapExtension
    {
        private Stream oldStream;
        private Stream newStream;
    
        // Other overrides...
    
        public override void ProcessMessage(SoapMessage message)
        {
            switch (message.Stage)
            {
                case SoapMessageStage.BeforeSerialize:
                    // ...
                    break;
                case SoapMessageStage.AfterSerialize:
                    string newOutput = ReadPreCookedResponseFromDB();
                    RewriteOutput(newOutput);
                    break;
                case SoapMessageStage.BeforeDeserialize:
                    // ...
                    break;
                case SoapMessageStage.AfterDeserialize:
                    // ...
                    break;
                default:
                    throw new Exception("invalid stage");
            }
        }
    
        private void RewriteOutput(string output)
        {
            newStream.Position = 0;
            StreamWriter sw = new StreamWriter(newStream);
            sw.Write(output);
            sw.Flush();
            newStream.Position = 0;
            Copy(newStream, oldStream);
            newStream.Position = 0;
        }
    
        private void Copy(Stream from, Stream to)
        {
            TextReader reader = new StreamReader(from);
            TextWriter writer = new StreamWriter(to);
            string toWrite = reader.ReadToEnd();
    
            writer.WriteLine(toWrite);
            writer.Flush();
        }
    
    }
    

    最后一点:您需要指示 ASP.NET 运行时使用您的 SoapExtension。您可以通过 WebMethod 或 web.config 中的属性来执行此操作(这就是我所做的):

    <system.web>
      <webServices>
        <soapExtensionTypes>
           <add type="SoapSerializationTest.SerializationPassThrough, SoapSerializationTest" priority="1" />
        </soapExtensionTypes>
      </webServices>
    <system.web>
    

    我从中导出这些 sn-ps 的原始代码示例位于 http://msdn.microsoft.com/en-us/library/7w06t139(VS.85).aspx

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多