【问题标题】:SOAP client not handling XML entities properly; encountering "There is an error in XML document"SOAP 客户端没有正确处理 XML 实体;遇到“XML 文档中有错误”
【发布时间】:2011-06-28 14:38:51
【问题描述】:

我们的 WCF Web 服务的一些消费者在尝试解析我们的响应时遇到异常:

System.InvalidOperationException:XML 文档中存在错误 (5, -349)。 在 System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader,字符串 encodingStyle,XmlDeserializationEvents 事件) 在 System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader,字符串 encodingStyle) 在 System.Web.Services.Protocols.SoapHttpClientProtocol.ReadResponse(SoapClientMessage 消息,WebResponse 响应,流 responseStream,布尔 asyncCall) 在 System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(字符串方法名,对象 [] 参数) 在 [消费者代码]

内部异常如下所示:

'',十六进制值 0x0B,是无效字符。第 5 行,位置 -349。 在 System.Xml.XmlTextReaderImpl.Throw(异常 e) 在 System.Xml.XmlTextReaderImpl.Throw(字符串 res,String[] args) 在 System.Xml.XmlTextReaderImpl.ThrowInvalidChar(Int32 pos,Char invChar) 在 System.Xml.XmlTextReaderImpl.ParseNumericCharRefInline(Int32 startPos,布尔扩展,BufferBuilder internalSubsetBuilder,Int32& charCount,EntityType& entityType) 在 System.Xml.XmlTextReaderImpl.ParseCharRefInline(Int32 startPos,Int32& charCount,EntityType& entityType) 在 System.Xml.XmlTextReaderImpl.ParseText(Int32& startPos,Int32& endPos,Int32& outOrChars) 在 System.Xml.XmlTextReaderImpl.ParseText() 在 System.Xml.XmlTextReaderImpl.ParseElementContent() 在 System.Xml.XmlTextReaderImpl.Read() 在 System.Xml.XmlTextReader.Read() 在 System.Xml.XmlReader.ReadElementString() 在 Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReader1.Read43_TextWidgetConfig (Boolean isNullable, Boolean checkType) 在 Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReader1.Read45_TextWidgetInfo (Boolean isNullable, Boolean checkType) 在 Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReader1.Read49_WidgetInfo (Boolean isNullable, Boolean checkType) 在 Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReader1.Read50_InstantPageData(Boolean isNullable, Boolean checkType) 在 Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReader1.Read128_GetInstantPageDataResponse() 在 Microsoft.Xml.Serialization.GeneratedAssembly.ArrayOfObjectSerializer141.Deserialize(XmlSerializationReader 阅读器) 在 System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader,字符串 encodingStyle,XmlDeserializationEvents 事件)

返回的客户数据不知何故包含垂直制表符。查看我们的 XML,我们可以看到这些字符被正确地呈现为  实体。通过 Google 快速搜索,我们发现 XmlSerializer 存在一个错误,它无法处理某些实体,必须通过更改自动生成的代理的 XML 阅读器中的选项来修复。

消费者承认他们需要修复其客户端代码,但他们无法通过补丁快速响应此问题。他们希望我们在自己的代码中应用补丁来过滤掉这些禁用字符。

  1. XmlSerializer 的问题字符列表是否记录在任何地方?
  2. 我们是否有一种干净的方法来更改我们的 WCF 服务,以便我们可以自动删除字符,而无需在我们所有的 Web 方法中进行字符串替换?

更新:

我找到了#1 的答案。根据the XML spec,只允许某些字符代码:

字符 ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]

所以看起来我们服务器上的DataContractSerializer 是这里的错误。我正在研究如何自定义该序列化程序。

更新 2:

看起来DataContractSerializer 问题是已知的并且logged in Microsoft Connect

【问题讨论】:

  • 那么,您的客户向您提供了格式错误的 XML,并希望您实施 hack 来解决他们的问题?这只会给他们一个理由不解决他们的问题。
  • 不,我们将由DataContractSerializer 生成的格式有些错误的 XML(包含超出允许字符范围的实体)提供给我们 Web 服务的消费者。甚至 WebService Studio 也会对输出感到窒息。

标签: .net xml wcf xmlserializer soap-client


【解决方案1】:

这是我的解决方法代码。我对此并不满意。它并没有涵盖所有情况(尽管它可以满足我的需求),并且感觉应该有一个更简单的解决方案。我会在这里发布它,希望其他人可以做得更好或者有人有更简单的答案。

为了解决这个问题,我创建了一个新的操作行为属性,将序列化程序更改为自定义序列化程序,该序列化程序会去除将呈现为无效 XML 实体的字符:

public class StripInvalidXmlCharactersBehaviorAttribute 
    : Attribute, IOperationBehavior
{
    public void AddBindingParameters(
        OperationDescription operationDescription, 
        BindingParameterCollection bindingParameters)
    {
    }

    public void ApplyClientBehavior(
        OperationDescription operationDescription, 
        ClientOperation clientOperation)
    {
        IOperationBehavior behavior =
            new StripInvalidXmlCharactersBehavior(operationDescription);
        behavior.ApplyClientBehavior(operationDescription, clientOperation);
    }

    public void ApplyDispatchBehavior(
        OperationDescription operationDescription, 
        DispatchOperation dispatchOperation)
    {
        IOperationBehavior behavior =
            new StripInvalidXmlCharactersBehavior(operationDescription);
        behavior.ApplyDispatchBehavior(
            operationDescription, dispatchOperation);
    }

    public void Validate(OperationDescription operationDescription)
    {
    }
}

行为本身如下所示:

internal class StripInvalidXmlCharactersBehavior 
    : DataContractSerializerOperationBehavior
{
    public StripInvalidXmlCharactersBehavior(OperationDescription opDesc)
        : base(opDesc)
    {
    }

    public override XmlObjectSerializer CreateSerializer(
        Type type, string name, string ns, IList<Type> knownTypes)
    {
        return new InvalidXmlStrippingSerializer(type, name, ns, knownTypes);
    }

    public override XmlObjectSerializer CreateSerializer(
        Type type, XmlDictionaryString name, XmlDictionaryString ns, 
        IList<Type> knownTypes)
    {
        return new InvalidXmlStrippingSerializer(type, name, ns, knownTypes);
    }
}

这是序列化程序:

internal class InvalidXmlStrippingSerializer : XmlObjectSerializer
{
    private DataContractSerializer _innerSerializer;

    public InvalidXmlStrippingSerializer(
        Type type, string name, string ns, IList<Type> knownTypes)
    {
        _innerSerializer = 
            new DataContractSerializer(type, name, ns, knownTypes);
    }

    public InvalidXmlStrippingSerializer(
        Type type, XmlDictionaryString name, XmlDictionaryString ns, 
        IList<Type> knownTypes)
    {
        _innerSerializer =
            new DataContractSerializer(type, name, ns, knownTypes);
    }

    public override bool IsStartObject(XmlDictionaryReader reader)
    {
        return _innerSerializer.IsStartObject(reader);
    }

    public override object ReadObject(
        XmlDictionaryReader reader, bool verifyObjectName)
    {
        return _innerSerializer.ReadObject(reader, verifyObjectName);
    }

    public override void WriteEndObject(XmlDictionaryWriter writer)
    {
        _innerSerializer.WriteEndObject(writer);
    }

    public override void WriteObjectContent(
        XmlDictionaryWriter writer, object graph)
    {
        graph = fixBadStringsRecursive(graph);
        _innerSerializer.WriteObjectContent(writer, graph);
    }

    private object fixBadStringsRecursive(object graph)
    {
        var objType = graph.GetType();
        if (objType == typeof(string))
        {
            graph = removeInvalidCharacters(graph as string);
        }
        else if (graph is IEnumerable)
        {
            foreach (var item in graph as IEnumerable)
            {
                fixBadStringsRecursive(item);
            }
        }
        else if (objType.IsClass)
        {
            // Look through the properties of the object 
            foreach (var prop in graph.GetType().GetProperties())
            {
                var propParams = prop.GetIndexParameters();
                if ((propParams == null || propParams.Length == 0)
                    && prop.GetGetMethod() != null)
                {
                    var propVal = prop.GetValue(graph, null);
                    if (propVal != null)
                    {
                        propVal = fixBadStringsRecursive(propVal);
                        if (prop.GetSetMethod() != null)
                        {
                            prop.SetValue(graph, propVal, null);
                        }
                    }
                }
            }
        }
        return graph;
    }

    private static string removeInvalidCharacters(string source)
    {
        // This is per the W3C XML spec:
        // http://www.w3.org/TR/xml/#NT-Char
        return new string(
            (
                from ch in source
                where
                    ch == '\u0009' || ch == '\u000a' || ch == '\u000d'
                    || (ch >= '\u0020' && ch <= '\ud7ff')
                    || (ch >= '\ue000' && ch <= '\ufffd')
                select ch
            ).ToArray()
        );
    }

    public override void WriteStartObject(
        XmlDictionaryWriter writer, object graph)
    {
        _innerSerializer.WriteStartObject(writer, graph);
    }
}

要将行为应用于我的操作,我现在可以添加我创建的属性。

【讨论】:

    猜你喜欢
    • 2012-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多