在尝试分析我们的响应时,我们的wcf web服务的一些使用者遇到异常:
System.InvalidOperationException:XML文档(5,-349)中有错误。
在system.xml.serialization.xmlserializer.deserialize(xmlreader xmlreader、string encodingstyle、xmldeserializationevents事件)
在system.xml.serialization.xmlserializer.deserialize(xmlreader xmlreader,字符串编码样式)
位于system.web.services.protocols.soapHttpClientProtocol.readResponse(soapClientMessage消息、webResponse响应、stream响应团队、布尔异步调用)
位于system.web.services.protocols.soapHttpClientProtocol.invoke(字符串methodName,对象[]参数)
AT[消费者代码]
内部异常如下所示:
“”十六进制值0x0b是无效字符。5号线,位置-349。
在system.xml.xmlTextReaderImpl.throw处(异常E)
在system.xml.xmlTextReaderImpl.throw处(string 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上
在microsoft.xml.serialization.generatedassembly.arrayofobjectserializer141.deserialize(XmlSerializationReader读取器)
在system.xml.serialization.xmlserializer.deserialize(xmlreader xmlreader、string encodingstyle、xmldeserializationevents事件)
以某种方式返回的客户数据中包含垂直制表符。查看我们的xml,我们可以看到这些字符被正确地呈现为
实体。在google进行快速搜索时,我们发现存在一个XmlSerializer
错误,它无法处理某些实体,必须通过更改自动生成的代理的xml阅读器中的选项来解决这个问题。
消费者承认他们需要修复他们的客户端代码,但是他们无法用补丁快速响应这个问题。他们希望我们在自己的代码中应用一个补丁来过滤掉这些被禁止的字符。XmlSerializer
的问题字符列表是否记录在任何地方?
有没有一种干净的方法可以让我们更改wcf服务,这样我们就可以自动去掉字符,而不必在所有web方法中进行字符串替换?
更新:
我找到了答案。根据the XML spec,仅允许某些字符代码:
字符:=x9 xa xd[x20-xd7ff][xe000-xffd][x10000-x10ffff]
所以我们服务器上的DataContractSerializer
似乎是这里的错误所在。我正在研究如何自定义序列化程序。
更新2:
看来DataContractSerializer
问题是已知的并且logged in Microsoft Connect。
最佳答案
这是我的解决方法代码。我对此不太满意;它并没有涵盖所有的情况(尽管它照顾到了我的需求),而且感觉应该有一个更简单的解决方案。我会把它贴在这里,希望有人能做得更好,或者有人有一个更简单的答案。
为了解决这个问题,我创建了一个新的操作行为属性,将序列化程序更改为一个自定义序列化程序,该序列化程序将去掉将呈现为无效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);
}
}
要将行为应用于我的操作,现在只需添加我创建的属性。