如何在C#中移除XML中的<xsi:type="xsd:string">属性
解决C#调用SOAP服务时object属性自动添加xsi:type导致的验证错误
当调用SOAP服务时,服务定义了一个object类型的属性,传递XML字符串时,WCF序列化器会自动为该节点添加xsi:type="xsd:string"属性,但服务端未声明xsd命名空间,引发以下验证错误:
UndeclaredPrefix: Cannot resolve 'xsd:string' as a QName: the prefix 'xsd' is not declared.
cvc-elt.4.1: The value 'xsd:string' of attribute 'http://www.w3.org/2001/XMLSchema-instance,type' of element 'DatosEspecificos' is not a valid QName.
在SOAPUI中移除该属性后请求可正常执行,因此需要在C#调用时避免或移除这个xsi:type属性。
方案1:将object属性替换为XmlElement类型(优先推荐)
如果可以重新生成或修改服务引用,将Item的类型从object改为XmlElement,这样序列化时会直接嵌入XML节点,不会自动添加xsi:type属性。
代码示例:
XmlDocument xmlDoc = new XmlDocument(); XmlNode nameNode = xmlDoc.CreateElement("Name"); XmlNode genderNode = xmlDoc.CreateElement("Gender"); genderNode.InnerText = "M"; nameNode.AppendChild(genderNode); xmlDoc.AppendChild(nameNode); // 此时Item类型为XmlElement p_SolicitudTransmision[0].Item = xmlDoc.DocumentElement;
方案2:自定义消息检查器移除xsi:type属性
如果无法修改服务引用的类型,通过实现IClientMessageInspector拦截SOAP请求,手动移除多余的xsi:type属性。
步骤1:实现消息检查器
using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; using System.Xml; public class RemoveXsiTypeInspector : IClientMessageInspector { public object BeforeSendRequest(ref Message request, IClientChannel channel) { // 将请求消息转换为XmlDocument进行修改 var doc = new XmlDocument(); using (var reader = request.GetReaderAtBodyContents()) { doc.Load(reader); } // 定位需要移除xsi:type的节点(根据实际节点名称调整) var nsManager = new XmlNamespaceManager(doc.NameTable); nsManager.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"); var targetNodes = doc.SelectNodes("//DatosEspecificos[@xsi:type='xsd:string']", nsManager); foreach (XmlNode node in targetNodes) { // 移除xsi:type属性 node.Attributes.RemoveNamedItem("type", "http://www.w3.org/2001/XMLSchema-instance"); } // 重新构造请求消息 var newMessage = Message.CreateMessage(request.Version, request.Headers.Action, new XmlNodeReader(doc)); newMessage.Headers.CopyHeadersFrom(request.Headers); newMessage.Properties.CopyProperties(request.Properties); request = newMessage; return null; } public void AfterReceiveReply(ref Message reply, object correlationState) { // 无需处理响应 } }
步骤2:实现端点行为
public class RemoveXsiTypeBehavior : IEndpointBehavior { public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { clientRuntime.ClientMessageInspectors.Add(new RemoveXsiTypeInspector()); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { } public void Validate(ServiceEndpoint endpoint) { } }
步骤3:给服务客户端添加行为
var soapClient = new YourSoapServiceClient(); soapClient.Endpoint.Behaviors.Add(new RemoveXsiTypeBehavior()); // 正常调用服务方法 soapClient.YourServiceMethod(p_SolicitudTransmision);
方案3:手动构造SOAP请求(绕过WCF序列化)
如果服务结构简单,可直接使用HttpWebRequest发送原始XML,完全控制请求内容,避免自动添加的属性。
代码示例:
var soapRequest = @"<soapenv:Envelope xmlns:soapenv=""http://schemas.xmlsoap.org/soap/envelope/""> <soapenv:Header/> <soapenv:Body> <DatosEspecificos> <Name> <Gender>M</Gender> </Name> </DatosEspecificos> <!-- 其他请求内容 --> </soapenv:Body> </soapenv:Envelope>"; var request = (HttpWebRequest)WebRequest.Create("http://your-soap-service-url"); request.Method = "POST"; request.ContentType = "text/xml;charset=utf-8"; request.Headers.Add("SOAPAction", "http://your-soap-action-uri"); // 写入请求内容 using (var stream = request.GetRequestStream()) { var bytes = Encoding.UTF8.GetBytes(soapRequest); stream.Write(bytes, 0, bytes.Length); } // 获取响应 using (var response = (HttpWebResponse)request.GetResponse()) using (var reader = new StreamReader(response.GetResponseStream())) { var responseContent = reader.ReadToEnd(); // 处理响应结果 }
内容的提问来源于stack exchange,提问作者Adrian Tornero Ortiz
相关产品推荐
相关产品推荐

