简体   繁体   English

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

[英]Returning a complex type from its XML representation in a .NET web service

I have an XML document in a database, which is the XML-serialized representation of an instance of a certain class Foo: 我在数据库中有一个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>

Suppose now I want to write an ASP.NET web service method that returns an instance of Foo, like so: 假设现在我想编写一个返回Foo实例的ASP.NET Web服务方法,如下所示:

[WebMethod]
public Foo GimmeFoo()
{
}

The question: I want to return the serialized Foo, but without deserializing it first within the GimmeFoo() method (Foo in real life is fairly large, and it would feel silly to deserialize it from the DB to then have it reserialized automatically shortly thereafter by the SOAP fairies). 问题:我想返回序列化的Foo,但不首先在GimmeFoo()方法中反序列化(现实中的Foo相当大,从数据库反序列化然后在不久之后自动重新序列化会很愚蠢。由SOAP精灵)。

Is there any attribute I can slap on the web service (or some code I can write in its body) that allows me to send the instance of Foo without needing to deserialize it first? 是否可以在Web服务上添加任何属性(或可以在其主体中编写一些代码),使我能够发送Foo实例而无需先反序列化?

This presupposes that only a known client will call this service, as other languages may not be able to deserialize this xml file. 前提是只有已知的客户端会调用此服务,因为其他语言可能无法反序列化此xml文件。

In my mind, if that is the case, then why not just have a REST service that will pass the data back, since you aren't using a web service correctly, IMO. 在我看来,如果是这样,那为什么不仅仅拥有一个REST服务来将数据传回,因为IMO您并未正确使用Web服务。 For example, if I use GSOAP (in C) or PHP to serve as a client, would your xml file be useful, as I will need to deserialize it myself. 例如,如果我使用GSOAP(用C语言)或PHP作为客户端,那么您的xml文件是否有用,因为我需要自己反序列化它。

It may be a pain to deserialize and then have the web service reserialize, but IMO, that is the correct way, again, if you have to use a webservice. 反序列化然后重新序列化Web服务可能很痛苦,但是,如果必须使用Web服务,那么IMO,这也是正确的方法。

Besides, you have a very strong coupling between the client and server if you pass back this serialized XML file, which is, again IMO, a bad design. 此外,如果将这个序列化的XML文件传回,则客户与服务器之间的耦合非常紧密,这也是IMO的一个不好的设计。

You can return the XmlElement or XmlDocument types from the web service. 您可以从Web服务返回XmlElement或XmlDocument类型。 Just return your serialized XML. 只需返回序列化的XML。

I found a way that might not be the most elegant, but gets the job done. 我发现了一种可能不是最优雅的方法,但是可以完成工作。 The idea is to delegate all the work to a custom SoapExtension-derived class, and actually do nothing in the WebMethod itself - the WebMethod is just there as an endpoint for the call: 这个想法是将所有工作委托给一个自定义的SoapExtension派生的类,而实际上在WebMethod本身中什么也不做-WebMethod只是作为调用的端点而存在:

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

But here's the magic: you write a SoapExtension that intercepts all SOAP traffic and, when the SoapMessageStage.AfterSerialize stage arrives, you stick in there your already-serialized payload: 但这就是魔术:您编写了一个SoapExtension来拦截所有SOAP流量,并且当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();
    }

}

The final touch: you need to instruct the ASP.NET runtime to use your SoapExtension. 最后一点:您需要指示ASP.NET运行时使用您的SoapExtension。 You can do so via an attribute on the WebMethod, or in web.config (which is what I did): 您可以通过WebMethod或web.config中的属性来做到这一点(这是我所做的):

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

The original code samples from which I derived these snippets are at http://msdn.microsoft.com/en-us/library/7w06t139(VS.85).aspx . 我从中得出这些摘要的原始代码示例位于http://msdn.microsoft.com/zh-cn/library/7w06t139(VS.85).aspx

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM