生成的 WCF 代理为列表返回 null

Posted

技术标签:

【中文标题】生成的 WCF 代理为列表返回 null【英文标题】:Generated WCF proxy returns null for a list 【发布时间】:2021-03-06 02:38:41 【问题描述】:

我们正在尝试为 php SOAP 服务实现 WCF 代理/客户端。 我们检索了 WSDL 并在命令行应用程序中创建了一个服务引用。 我们能够检索 SOAP 操作的结果,但事实证明,返回的列表总是以 null 结尾,当使用 fiddler 敲击线路或在 SoapUI 中对其进行测试时,我们可以清楚地看到正在返回的数据。

我在类似的问题帖子中遵循了一些建议,并提出了一个似乎存在相同问题的简约反序列化示例。

Message m = Message.CreateMessage(XmlReader.Create("response.xml"), int.MaxValue, MessageVersion.Soap11);
SoapReflectionImporter importer = new SoapReflectionImporter(new SoapAttributeOverrides(), "http://brokenns");
XmlTypeMapping mapping = importer.ImportTypeMapping(typeof(getDeviceListResponse));
XmlSerializer serializer = new XmlSerializer(mapping);
getDeviceListResponse response = (getDeviceListResponse)serializer.Deserialize(m.GetReaderAtBodyContents());
Console.WriteLine(response.@return.deviceList == null  ? "List is null" : "List is not null");
< List is null

service.wsdl

<?xml version="1.0" encoding="utf-8"?>
<definitions xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
  xmlns:tns="http://brokenns" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns="http://schemas.xmlsoap.org/wsdl/"
  targetNamespace="http://brokenns">
  <types>
    <xsd:schema targetNamespace="http://brokenns">
      <xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/" />
      <xsd:import namespace="http://schemas.xmlsoap.org/wsdl/" />
      <xsd:complexType name="Device">
        <xsd:sequence>
          <xsd:element name="id" type="xsd:int" minOccurs="1" maxOccurs="1" />
        </xsd:sequence>
      </xsd:complexType>
      <xsd:complexType name="DeviceListResult">
        <xsd:sequence>
          <xsd:element name="message" type="xsd:string" minOccurs="0" maxOccurs="1" />
          <xsd:element name="deviceList" type="tns:Device" minOccurs="0" maxOccurs="unbounded" />
        </xsd:sequence>
      </xsd:complexType>
    </xsd:schema>
  </types>
  <message name="getDeviceListRequest">
    <part name="loadSimCard" type="xsd:boolean" />
  </message>
  <message name="getDeviceListResponse">
    <part name="return" type="tns:DeviceListResult" />
  </message>
  <portType name="DevicesServicePortType">
    <operation name="getDeviceList">
      <documentation>Returns device list</documentation>
      <input message="tns:getDeviceListRequest" />
      <output message="tns:getDeviceListResponse" />
    </operation>
  </portType>
  <binding name="DevicesServiceBinding" type="tns:DevicesServicePortType">
    <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" />
    <operation name="getDeviceList">
      <soap:operation soapAction="https://myservice.com/ws/devices.php/getDeviceList" style="rpc" />
      <input>
      <soap:body use="literal" namespace="http://brokenns" /></input>
      <output>
        <soap:body use="literal" namespace="http://brokenns" /></output>
    </operation>
  </binding>
  <service name="DevicesService">
    <port name="DevicesServicePort" binding="tns:DevicesServiceBinding">
      <soap:address location="https://myservice.com/ws/devices.php" />
    </port>
  </service>
</definitions>

response.xml

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
    <SOAP-ENV:Body>
        <ns1:getDeviceListResponse xmlns:ns1="http://brokenns">
            <return>
                <message>works</message>
                <deviceList>
                    <id>48</id>
                </deviceList>
                <deviceList>
                    <id>41</id>
                </deviceList>
                <deviceList>
                    <id>42</id>
                </deviceList>
            </return>
        </ns1:getDeviceListResponse>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

任何人都可以发现服务 wsdl 中的错误会在反序列化后使我的列表为空吗?

我尝试对生成的合约进行简单的来回序列化,但无法在其中发现问题,这让我相信它与命名空间有关,但省略它们并没有帮助。

<?xml version="1.0" encoding="utf-16"?>
<getDeviceListResponse xmlns:ns1="http://brokenns">
  <return>
    <message>sd</message>
    <deviceList>
      <id>2323</id>
    </deviceList>
    <deviceList>
      <id>67</id>
    </deviceList>
  </return>
</getDeviceListResponse>

【问题讨论】:

原因可能是 WCF 将通用列表序列化为数组以通过网络发送。该配置只是告诉 svcutil 创建一个代理并将它们转换回一个通用列表以方便使用。您可以在创建服务引用时尝试修改集合类型。更多相关信息可以参考这个链接:***.com/questions/63702018/… 在 wcf 代理中反序列化后,任何集合类型都不会改变列表为空的事实。 您想在.net 中调用PHP SOAP 服务吗?如果你想在.net中调用PHP SOAP服务可以参考这个链接:forums.asp.net/t/… 该服务确实在另一端使用 NuSoap,但该链接对我没有答案。 【参考方案1】:

以下适用于序列化:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace ConsoleApplication1

    class Program
    
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        
            XmlReader reader = XmlReader.Create(FILENAME);
            XmlSerializer serializer = new XmlSerializer(typeof(Envelope));
            Envelope envelope = (Envelope)serializer.Deserialize(reader);

        
    
    [XmlRoot(ElementName = "Envelope", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
    public class Envelope
    
        [XmlElement(ElementName = "Body", Namespace = "http://schemas.xmlsoap.org/soap/envelope/")]
        public Body Body  get; set; 
    
    public class Body
    
        [XmlElement(ElementName = "getDeviceListResponse", Namespace = "http://brokenns")]
        public getDeviceListResponse getDeviceListResponse  get; set; 
    
    public class getDeviceListResponse
    
        [XmlElement(ElementName = "return", Namespace = "")]
        public Return Return  get; set; 
    
    public class Return
    
        public string message  get; set; 
        [XmlElement()]
        public List<deviceList> deviceList  get; set; 
    
    public class deviceList
    
        public int id  get; set; 
    


 

【讨论】:

正如我所提到的,我知道合同在普通的 XMLSerializer 中工作。它不适用于为所述 WSDL 自动生成的服务代理。我发现github.com/dotnet/runtime/issues/25245 有类似的问题,这让我想知道是否根本不可能在没有包装元素的情况下将重复元素与 wcf 合同中的其他属性混合... WSDL 不完整。 WSDL 中有错误。例如没有 type="tns:Device" 复杂元素 实际上我确实修剪了 wsdl,但是在 DeviceListResult 类型的正上方有一个 Device 复杂元素。 wsdl-analyzer.com/service/service/1766269158?version=1wsdl-analyzer.com/schema/show/… 带有不同的前缀。一种是在 WSDL 中使用 TNS。我尝试从 WSDL Schema 创建类,但失败了。 从 Visual Studio 2019 Enterprise 命令提示符生成使用“svcutil file.wsdl”的类成功地生成了一个包含类 DeviceListResult 并在其属性 deviceList 中引用类 Device 的 DevicesService.cs。

以上是关于生成的 WCF 代理为列表返回 null的主要内容,如果未能解决你的问题,请参考以下文章

为啥 WCF/JSON 不为 null 返回值返回“null”?

WCF 响应不是映射总是返回 null

如何从安静的 WCF 服务返回对象列表

Angular 4 POST 到 WCF REST 服务返回 NULL 响应

Columns子代不能返回null,但是返回将停止循环

WCF REST JSON 返回动态列表