XmlElement 参数接受简单格式 null
我正在构建一个 WCF 服务(.Net 3.5,IIS 托管)来替换旧的 ASMX 样式服务。它必须与旧式界面非常兼容,以避免编写调用它的软件的供应商付出努力。 (他们中的一些人构建了一个简单的 XML 数据结构,将其放入预先准备好的 SOAP“模板”中,然后将其交给我的服务。我需要接受他们现有的 XML 结构)。
为了与这些客户端调用服务的方式兼容,我必须将操作定义为:
[ServiceContract(Namespace = "urn:namespacex")]
public interface IServices
{
[OperationContract]
System.Xml.XmlElement OperationA(int parmB, System.Xml.XmlElement parmC);
}
即参数位于OperationContract 中,而不是提取到DataContract 中单独的DataMember 元素中。此处使用的 XmlElement 替换了旧 ASMX 服务中使用的 XmlNode 参数。
它的实现是:
[ServiceBehavior(Namespace = "urn:namespacey")]
public class TheService : IServices
{
public System.Xml.XmlElement OperationA(int parmB, System.Xml.XmlElement parmC)
{
... code to handle call
}
}
当有数据要发送时,它工作正常。
我遇到的问题是当 parmC 输入为空时,这是允许的。有时有数据,有时没有。一个调用者在 SOAP 消息中发送此内容以获取 null parmC:
<parmC/>
即一个简单的空 XML 元素。
这会从 WCF 引发以下错误:
期望状态“Element”..遇到带有名称的“EndElement” “parmC”,命名空间“urn:namespacex”。
所以它似乎不喜欢简单的空元素输入。 (如果其中确实有一些数据,则该服务工作正常。)
跟踪我自己的测试调用者(无论有没有 parmC 参数中的信息都可以工作),我可以看到对于 null,我的 (.Net WCF) 测试程序正在发送:
<parmC xmlns:i="http://www.w3.org/2001/XMLSchema-instance" i:nil="true"/>
尝试1:
在寻求帮助时,我注意到“[XmlSerializerFormat]”属性应该使服务的行为更像旧式 ASMX,而不使用较新的数据契约序列化器。然后,我的测试调用者(.Net、WCF)根本不发送任何 null 值。但是这个客户端仍然收到错误,尽管现在它是一条不同的消息,引用该元素:
没有打开相应的开始元素。
尝试 2:
然后我记得原始 ASMX 服务的最旧版本使用字符串来接受该数据。 (它将该字符串加载到服务器端的 XML 对象中,位于操作的处理程序中)。因此,我更改了约定和操作,将 parmC 定义为字符串,而不是 XmlElement。
突然,服务开始接受各种形式的空 parmC。 (使用一个快速测试应用程序将原始 SOAP 发布到我的服务,这样我就可以模拟这些供应商所做的事情)。
但是 - 如果那里确实有数据,它就会失败,并显示:
反序列化操作请求消息正文时出错 '操作A'。 ---> System.InvalidOperationException:出现错误 在 XML 文档 (99, 99) 中。 ---> System.Xml.XmlException:结束元素 预期来自命名空间“urn:namespacex”的“parmC”。找到元素“a” 来自命名空间 ''
(当 XML 数据在 parmC 中发送时,它在 SOAP 中看起来像这样:)
<parmC>
<a xmlns="">bbb</a>
</parmC>
显然
,在这种情况下,不希望在字符串('a' 元素)中找到 XML;但这曾经在 ASMX 中工作过,所以“值得一试”。
因此,我找不到允许将 XML 数据发送到服务,同时还允许客户端样式为空或 null 数据的设置组合。
我是否必须实现自定义反序列化器?有什么地方可以提供帮助吗?
(到目前为止,我已经设法将与 WCF 相关的所有内容纯粹保留在配置中,如果可以的话,我希望保持这种方式。)
总而言之,我需要一个 WCF 服务来处理通过网络传输的这些内容:
<s:Body>
<OperationA xmlns="urn:namespacex">
<parmB>1</parmB>
<parmC> { this works fine }
<a xmlns="">
<b>bbb</b>
</a>
</parmC>
</OperationA>
</s:Body>
<s:Body>
<OperationA xmlns="urn:namespacex">
<parmB>1</parmB>
<parmC/> { I need this to be accepted }
</OperationA>
</s:Body>
I'm building a WCF service (.Net 3.5, IIS Hosted) to replace an old ASMX style service. It has to be very compatible with the old style interface, to avoid effort by the vendors who write the software that calls it. (Some of them build a simple XML data structure, bang it inside a pre-prepared SOAP "template" and throw it at my service. I need to accept their existing XML structure).
For compatibility with the way these clients call the service I've had to define the operation as:
[ServiceContract(Namespace = "urn:namespacex")]
public interface IServices
{
[OperationContract]
System.Xml.XmlElement OperationA(int parmB, System.Xml.XmlElement parmC);
}
i.e. the parameters are in the OperationContract, not pulled out into seperate DataMember elements in a DataContract. The XmlElement used here, replaces an XmlNode parameter used in the old ASMX service.
It's implemented as:
[ServiceBehavior(Namespace = "urn:namespacey")]
public class TheService : IServices
{
public System.Xml.XmlElement OperationA(int parmB, System.Xml.XmlElement parmC)
{
... code to handle call
}
}
It works fine... when there is data to send.
The issue I'm having is when the parmC input is null, which is allowed. Sometimes it has data, sometimes it doesn't. One caller is sending this in the SOAP message for a null parmC:
<parmC/>
i.e. a simple empty XML element.
This is raising the following error from WCF:
Expecting state 'Element'.. Encountered 'EndElement' with name
'parmC', namespace 'urn:namespacex'.
So it seems it isn't liking the simple null element input. (The service works fine if there is actually some data in there.)
Tracing my own test caller (which works with and without information in the parmC parameter), I can see that for null my (.Net WCF) tester is sending:
<parmC xmlns:i="http://www.w3.org/2001/XMLSchema-instance" i:nil="true"/>
Attempt 1:
Looking for help, I've noted the "[XmlSerializerFormat]" attribute that is supposed to make the service act more like old style ASMX, by not using the newer data contract serialiser. My test caller (.Net, WCF) then sends nothing at all for the null. But this client still gets an error, though it is a different message now, in reference to that element:
No corresponding start element is open.
Attempt 2:
Then I remembered the oldest version of the original ASMX service used a string to accept that data. (It loaded that string into an XML object server-side, within the handler for the operation). So I changed the contract and operation to define parmC as string, instead of XmlElement.
Suddenly the service is accepting various forms of empty parmC. (Using a quick test app that posts raw SOAP to my service, so I can simulate what these vendors do).
But - if there is actually data in there, it fails, with:
Error in deserializing body of request message for operation
'OperatonA'. ---> System.InvalidOperationException: There is an error
in XML document (99, 99). ---> System.Xml.XmlException: End element
'parmC' from namespace 'urn:namespacex' expected. Found element 'a'
from namespace ''
(when XML data is sent in parmC, it looks like this in the SOAP:
<parmC>
<a xmlns="">bbb</a>
</parmC>
)
Clearly, it's not expecting in this case to find XML inside the string (the 'a' element); but this used to work in ASMX so was "worth a shot".
So I can't find a combination of settings that allows both XML data to be sent to the service, while also allowing that clients style of empty or null data.
Do I have to implement a custom deserialiser? Is there a setting somewhere that'll help?
(So far I've managed to keep everything WCF related purely in configuration, I'd like to keep it that way if I can.)
In summary, I need a WCF service that will cope with either of these coming over the wire:
<s:Body>
<OperationA xmlns="urn:namespacex">
<parmB>1</parmB>
<parmC> { this works fine }
<a xmlns="">
<b>bbb</b>
</a>
</parmC>
</OperationA>
</s:Body>
<s:Body>
<OperationA xmlns="urn:namespacex">
<parmB>1</parmB>
<parmC/> { I need this to be accepted }
</OperationA>
</s:Body>
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您提供的两个选项的 XML 不等效。两者之间存在非常微妙的差异,这可能是导致您的问题的原因。
选项将元素 a 及其子元素设置为没有默认的 XML 命名空间。另一个选项(“null”参数选项)不包含默认 XML 命名空间从“urn:namespacex”到“”的“重新定义”,因此当反序列化过程解析 parmC 元素时
< a xmlns="">
它找不到它。如果你仔细阅读异常消息,它实际上是在告诉你这一点。至于如何支持这两种情况,请尝试让您的客户发送此内容而不是他们现在发送的内容:
或者可能:
The XML for the two options you present is not equivalent. There is a very subtle difference between the two that is probably the cause of your problem. The option with
<a xmlns="">
is setting the element a and its child elements to have no default XML namespace. The other option (the "null" parameter option) doesn't contain the "re-definition" of the default XML namespace from "urn:namespacex" to "" so when the deserialization process is parsing parmC for element<a xmlns="">
it can't find it. If you read the exception message carefully, it is actually telling you this.As to how to support the two scenarios, try getting your client to send this instead of what they're sending now:
or possibly: