从.NET Web服务中的XML表示返回复杂类型

时间:2009-04-23 20:22:08

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

我在数据库中有一个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相当大,将它从反序列化中感到愚蠢之后不久由SOAP精灵自动重新编译数据库。

我是否可以在Web服务(或者我可以在其正文中编写的某些代码)上发布任何属性,这些属性允许我发送Foo实例而无需首先反序列化它?

3 个答案:

答案 0 :(得分:1)

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

在我看来,如果是这种情况,那么为什么不只是有一个REST服务将数据传回,因为你没有正确使用网络服务,IMO。例如,如果我使用GSOAP(在C中)或PHP作为客户端,你的xml文件是否有用,因为我需要自己反序列化它。

反序列化然后让Web服务重新序列化可能是一种痛苦,但IMO,这是正确的方法,如果你必须使用web服务。

此外,如果您传回这个序列化的XML文件(即IMO,这是一个糟糕的设计),那么客户端和服务器之间会有很强的耦合。

答案 1 :(得分:0)

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

答案 2 :(得分:0)

我发现了一种可能不是最优雅的方式,但却完成了工作。我们的想法是将所有工作委托给一个自定义的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>

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