C# 创建XML子节点,忽略父节点
根据MSDN文件: 还可以将数组序列化为XML元素的平面序列,方法是对返回数组的字段应用XmlElementAttribute,如下所示 所需的XML架构:C# 创建XML子节点,忽略父节点,c#,xml,soap,C#,Xml,Soap,根据MSDN文件: 还可以将数组序列化为XML元素的平面序列,方法是对返回数组的字段应用XmlElementAttribute,如下所示 所需的XML架构: <xs:element minOccurs="0" maxOccurs="unbounded" name="ResponseLineTest" type="OrderLn" /> 但是:使用.NET 4.51,我得到以下模式: <xs:element minOccurs="0" name="ResponseLine"
<xs:element minOccurs="0" maxOccurs="unbounded" name="ResponseLineTest" type="OrderLn" />
但是:使用.NET 4.51,我得到以下模式:
<xs:element minOccurs="0" name="ResponseLine" nillable="true" type="tns:ArrayOfOrderLn"/>
如何标记我的C#类和属性,使WSDL输出看起来与上面(和文档)一样?对Web服务定义进行必要的更改 首先,必要的复杂类型
PlaceOrder
的定义没有提到类型tns:OrderLine
的元素。我想你必须改变元素的定义。因为您的Web服务的定义非常松散,所以它的工作方式与您在问题中所展示的一样
以下是PlaceOrder
请求的当前定义。这意味着,PlaceOrder
元素是请求参数所必需的
<wsdl:message name="IService_PlaceOrder_InputMessage">
<wsdl:part name="parameters" element="tns:PlaceOrder"/>
</wsdl:message>
这意味着,您还可以发送所有内容。您的Web服务不知道PlaceOrder
上下文中的OrderLine
元素,因为此处未定义该元素。您必须将PlaceOrder
元素的定义更改为以下符号
<xs:element name="PlaceOrder">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="userToken" nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="ediOrder" nillable="true" type="q1:order"/>
<xs:element minOccurs="0" name="OrderLine" nillable="true" type="q1:ArrayOfOrderLine"/>
</xs:sequence>
</xs:complexType>
</xs:element>
此定义表示,您需要具有父节点的OrderLine
复杂类型。因此,父节点完全按照wsdl文件中的定义发生。要省略父节点,必须按如下方式重新定义PlaceOrder
复杂类型:
<xs:complexType name="ArrayOfOrderLine">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="unbounded" name="OrderLine" nillable="true" type="tns:OrderLine"/>
</xs:sequence>
</xs:complexType>
<xs:element name="PlaceOrder">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="userToken" nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="ediOrder" nillable="true" type="q1:order"/>
<xs:element minOccurs="0" maxOccurs="unbounded" name="OrderLine" nillable="true" type="tns:OrderLine"/>
</xs:sequence>
</xs:complexType>
</xs:element>
上面显示的PHP代码显示了PlaceOrder
value对象。正如您所看到的,在webservice定义中定义的所有元素都作为此类的属性出现。这个类是PlaceOrder
复杂类型的精确php实现。可以说,所有复杂类型都是PHP类。此外,可接受的方法参数主要是SoapVar
实例。这对于soap客户机很重要,因为这保证了最终的xml结构是正确的
OrderLine值对象
<?php
namespace Webservice\Entity;
class OrderLine
{
protected $AdditionalCustomerReferenceNumber;
protected $LineID;
protected $OrderedArticle;
protected $PortalReference;
// getters and setters here
}
";
}catch(SoapFault$fault){
回声“;
var_dump($故障);
回声“;
}
结论
您的web服务定义非常不精确。因此,您只需重新考虑参数的定义,并在WSDL文件中更精确地定义它们。然后它也与PHP一起工作。PHP在其soap扩展中严格使用web标准。TL;DR您的问题是.NET有两个单独的XML序列化程序,并且,您正在创建一个WCF服务,您需要通过应用到您的应用程序切换到使用
XmlSerializer
详情如下。假设您拥有以下WCF服务合同(您的问题中未显示):
您希望此服务生成的XML具有包含重复序列的
元素的架构,例如,如下所示:
因此,您可以将该属性应用于ResponseLine
,以强制以这种方式将其序列化为XML。但是,当您为服务生成WSDL时,您会得到一个如下所示的模式:
架构包括一个额外的中间类型ArrayOfOrderLn
,并且未使用重写的元素名“ResponseLineTest”
。显然,[xmlementattribute(“ResponseLineTest”)]
属性被完全忽略。为什么会这样
事实证明,此行为记录在中,这说明您的服务根本没有使用XmlSerializer
,而是使用一个忽略[XmlElement]
属性的不同序列化程序:
默认情况下,WCF使用DataContractSerializer类序列化数据类型
有时,您可能必须手动切换到XmlSerializer。例如,在以下情况下会发生这种情况:
- 当对消息中显示的XML进行精确控制非常重要时
ResponseLine
属性。但是,在不使用外部容器元素的情况下序列化集合是非常困难的,因此必须通过应用[XmlSerializerFormat]
到您的服务合同:
现在,为您的服务生成的WSDL将符合要求。有任何具有xsd定义的WSDL可用吗?PHP使用自己的SoapClient类有点棘手。通常您必须使用PHP SoapVar对象才能获得正确的xml结构。有来自worldstandard创建者(ediWheel btw)的xsd,但正如我在使用XSD2代码时所学到的,这些xsd可以用很多方式进行解释。我只是想澄清一下。我不是做PHP部分的人。我们的客户端是。我返回强类型数据类。我使用WSDLBrowser.com进行测试,或者你问过我WSDL是否在某个地方可用吗?如果是的话。你甚至可以从我发布的网站上自己获取它服务:)上面显示的PHP输出是您的客户正在创建的请求,还是您的服务发送回您的客户的响应?我关注的是响应。然而,我尝试调用我的SOAP服务,无论是否使用父订单行。无论哪种方式都可以。哦,天哪,恐怕我整天都在胡闹WSDL,有蜜蜂n许多版本,从一分钟到一分钟,一些定义了orderline,一些没有,以调试不同的场景。orderline现在就在那里。为了调试,我添加了一个非常简单的方法,称为PlaceOrderTestNoInput。你能检查一下这个方法是否有相同的不精确定义吗?你认为它状态不好,我为此道歉。
<xs:element name="PlaceOrder">
<xs:complexType>
<xs:sequence>
<xs:element minOccurs="0" name="userToken" nillable="true" type="xs:string"/>
<xs:element minOccurs="0" name="ediOrder" nillable="true" type="q1:order"/>
<xs:element minOccurs="0" maxOccurs="unbounded" name="OrderLine" nillable="true" type="tns:OrderLine"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<?php
namespace Webservice\Entity;
use ArrayObject;
use SoapVar;
class PlaceOrder
{
protected $userToken;
protected $ediOrder;
protected $OrderLine;
public function __construct()
{
$this->OrderLine = new ArrayObject();
}
public function getUserToken(): ?SoapVar
{
return $this->userToken;
}
public function setUserToken(SoapVar $userToken): self
{
$this->userToken = $userToken;
return $this;
}
public function getEdiOrder() : ?SoapVar
{
return $this->ediOrder;
}
public function setEdiOrder(SoapVar $ediOrder): self
{
$this->ediOrder = $ediOrder;
return $this;
}
public function getOrderLine(): ArrayObject
{
return $this->OrderLine;
}
public function attachOrderLine(SoapVar $orderLine): self
{
$this->orderLine->append($orderLine);
return $this;
}
public function setOrderLine(ArrayObject $orderLine): self
{
$this->OrderLine = $orderLine;
return $this;
}
}
<?php
namespace Webservice\Entity;
class OrderLine
{
protected $AdditionalCustomerReferenceNumber;
protected $LineID;
protected $OrderedArticle;
protected $PortalReference;
// getters and setters here
}
<?php
namespace Wesbervice;
use Webservice\Entity\Order;
use Webservice\Entity\OrderLine;
use Webservice\Entity\PlaceOrder;
use SoapFault;
use SoapVar;
try {
// this url contains the wrong defined PlaceOrder complex type
$wsdl = 'https://uat-salesapi.ndias.com/service.svc?singlewsdl&version=27';
$client = new SoapClient($wsdl, [
'cache_wsdl' => WSDL_CACHE_NONE, // as long as you work on your wsdl
'encoding' => 'utf-8',
'exceptions' => true,
'soap_version' => SOAP_1_1,
'trace' => true, // enables tracing and __getLastRequest() / __getLastResponse()
'classmap' => [
'order' => Order::class,
'OrderLine' => OrderLine::class,
'PlaceOrder' => PlaceOrder::class,
],
]);
// user token
$usertoken = new SoapVar('bla', XSD_STRING, '', '', 'userToken', 'http://tempuri.org/');
// edi order
$order = (new Order())
->setBlanketOrderReference(new SoapVar(...))
->setBuyerParty(new SoapVar(...);
$order = new SoapVar($order, SOAP_ENC_OBJECT, '', '', 'ediOrder', 'http://tempuri.org/');
// compile our request parameter
$parameter = (new PlaceOrder())
->setUserToken($usertoken)
->setEdiOrder($order);
// order list objects
$orderLine1 = (new OrderLine())
->setAdditionalCustomerReferenceNumber(new SoapVar(...))
->setLineID(new SoapVar(...));
$orderLine1 = new SoapVar($orderLine1, SOAP_ENC_OBJECT, '', '', 'OrderLine', 'http://tempuri.org/');
$parameter->attachOrderLine($orderLine1);
$orderLine2 = (new OrderLine())
->setAdditionalCustomerReferenceNumber(new SoapVar(...))
->setLineID(new SoapVar(...));
$orderLine2 = new SoapVar($orderLine2, SOAP_ENC_OBJECT, '', '', 'OrderLine', 'http://tempuri.org/');
$parameter->attachOrderLine($orderLine2);
$parameter = new SoapVar($parameter, SOAP_ENC_OBJECT, '', '', 'PlaceOrder', 'http://tempuri.org/');
// the client knows the PlaceOrder method from the wsdl
$result = $client->PlaceOrder($parameter);
// the result is a stdClass structure, als long as the classmap parameter does not contain definitions of type to php entity classes
echo "<pre>";
var_dump($result);
echo "</pre>";
} catch (SoapFault $fault) {
echo "<pre>";
var_dump($fault);
echo "</pre>";
}
public class Output
{
[XmlElementAttribute("ResponseLineTest")]
public OrderLn[] ResponseLine { get; set; }
}
public class OrderLn
{
public string Order { get; set; }
}
[ServiceContract(Namespace = "Question59659046")]
[XmlSerializerFormat]
public interface IQuestion59659046Service
{
[OperationContract]
Output GetOutput(string input);
}
[ServiceContract(Namespace = "Question59659046")]
[XmlSerializerFormat]
public interface IQuestion59659046Service
{
[OperationContract]
Output GetOutput(string input);
}