C#调用Oracle SOA Soap服务时空值处理及反序列化异常排查
调用代码
CallCenterServiceReference.CallCenterService_pttClient client = new CallCenterServiceReference.CallCenterService_pttClient(); client.ClientCredentials.UserName.UserName = ""; client.ClientCredentials.UserName.Password = ""; CallCenterServiceReference.isPowerCutForNotPay obj = new CallCenterServiceReference.isPowerCutForNotPay(); CallCenterServiceReference.authStruct auth = new CallCenterServiceReference.authStruct(); obj.agreeNo = serviceIdTextBox.Text; obj.DEVID = null; obj.FRF = null; auth.systemID = 123; auth.authKey = ""; auth.systemIDSpecified = true; obj.auth = auth; var response = client.isPowerCutForNotPay(obj).isPowerCutForNotPayResult; MessageBox.Show("ResultCode is :-"+response.resultCode);
异常触发于 var response = client.isPowerCutForNotPay(obj).isPowerCutForNotPayResult; 行。
SOAP请求报文
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <isPowerCutForNotPay xmlns="http://www.J.com/SampleApplication/CallCenterSystem"> <agreeNo>3456</agreeNo> <DEVID xsi:nil="true"/> <FRF xsi:nil="true"/> <auth> <authKey>1234565</authKey> <systemID>2005</systemID> </auth> </isPowerCutForNotPay> </s:Body> </s:Envelope>
SOAP响应报文
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://www.w3.org/2005/08/addressing"> <env:Header> <wsa:Action>isPowerCutForNotPay</wsa:Action> <wsa:MessageID>urn:1d0f3f85-8963-11ee-a4bb-5254003edad5</wsa:MessageID> <wsa:ReplyTo> <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address> <wsa:ReferenceParameters> <instra:tracking.ecid xmlns:instra="http://xmlns.oracle.com/sca/tracking/1.0">f99958a9-fe21-4f48-871e-eef4aadfc448-007192ac </instra:tracking.ecid> <instra:tracking.FlowEventId xmlns:instra="http://xmlns.oracle.com/sca/tracking/1.0">70056562 </instra:tracking.FlowEventId> <instra:tracking.FlowId xmlns:instra="http://xmlns.oracle.com/sca/tracking/1.0">122084 </instra:tracking.FlowId> <instra:tracking.CorrelationFlowId xmlns:instra="http://xmlns.oracle.com/sca/tracking/1.0">0000OjsFbzE6iKG6yzvX6G1_8zCF0000ax </instra:tracking.CorrelationFlowId> <instra:tracking.quiescing.SCAEntityId xmlns:instra="http://xmlns.oracle.com/sca/tracking/1.0">70096 </instra:tracking.quiescing.SCAEntityId> </wsa:ReferenceParameters> </wsa:ReplyTo> <wsa:FaultTo> <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address> </wsa:FaultTo> </env:Header> <env:Body> <isPowerCutForNotPayResponse xmlns:ns1="http://xmlns.oracle.com/CallCenter/JDECOCallCenter/JDECOCallCenterService" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsp="http://www.w3.org/ns/ws-policy" xmlns:wssutil="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ouaf="http://ouaf.oracle.com/" xmlns:tns="http://www.J.com/SampleApplication/CallCenterSystem" xmlns="http://www.J.com/SampleApplication/CallCenterSystem"> <tns:isPowerCutForNotPayResult> <tns:resultCode>-1</tns:resultCode> <tns:resultStringA/> <tns:resultStringE>Invalid Account ID</tns:resultStringE> <tns:resultStringH/> <tns:customMessageA/> <tns:customMessageE/> <tns:customMessageH/> <tns:trans> <tns:DEVID/> <tns:FRF/> <tns:JRF>1373</tns:JRF> <tns:systemID>2005</tns:systemID> </tns:trans> <tns:value/> </tns:isPowerCutForNotPayResult> </isPowerCutForNotPayResponse> </env:Body> </env:Envelope>
异常信息
See the end of this message for details on invoking just-in-time (JIT) debugging instead of this dialog box.
************** Exception Text ************** System.ServiceModel.CommunicationException: Error in deserializing body of reply message for operation 'isPowerCutForNotPay'. ---> System.InvalidOperationException: There is an error in XML document (1, 2104). ---> System.FormatException: The string '' is not a valid Boolean value. at System.Xml.XmlConvert.ToBoolean(String s) at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderJDECOCallCenterService_ptt.Read5_Utils_ResultClassOfboolean(Boolean isNullable, Boolean checkType) at
问题分析
异常核心是空字符串无法转换为布尔值:从响应报文可见,<tns:value/>是空节点,但服务引用自动生成的实体类中,value字段被定义为非可空的bool类型。反序列化时,空节点被解析为空字符串,无法匹配布尔值的有效取值(仅true/false),因此抛出格式异常。
解决方案
方案1:修改服务引用生成规则
重新添加服务引用时,进入高级设置,勾选「允许生成可空值类型」。生成的实体类中value字段会变为bool?(可空布尔类型),空节点会被解析为null,避免转换错误。
方案2:手动修改生成的实体类
找到自动生成的结果实体类(如Utils_ResultClassOfboolean),将value字段的类型从bool改为bool?,确保序列化配置支持空值解析。
方案3:拦截SOAP响应修正XML
实现IClientMessageInspector接口,在反序列化前拦截响应报文,将空的<tns:value/>节点替换为<tns:value xsi:nil="true"/>(标记为空值)或<tns:value>false</tns:value>(赋予默认布尔值)。
示例拦截代码片段:
public class SoapResponseInterceptor : IClientMessageInspector { public void AfterReceiveReply(ref Message reply, object correlationState) { var buffer = reply.CreateBufferedCopy(int.MaxValue); var msg = buffer.CreateMessage(); var reader = msg.GetReaderAtBodyContents(); var xml = reader.ReadOuterXml(); // 替换空value节点为带nil标记的节点 xml = xml.Replace("<tns:value/>", "<tns:value xsi:nil=\"true\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"/>"); var newMsg = Message.CreateMessage(reply.Version, null, XmlReader.Create(new StringReader(xml))); newMsg.Headers.CopyHeadersFrom(reply.Headers); newMsg.Properties.CopyProperties(reply.Properties); reply = newMsg; buffer.Close(); } public object BeforeSendRequest(ref Message request, IClientChannel channel) { return null; } }
之后通过行为扩展将拦截器绑定到客户端端点即可生效。
内容的提问来源于stack exchange,提问作者M Muneer

