您的位置:首页 > 其它

获取xml时,出现“(十六进制值 0x1F)是无效的字符之类Xml异常的解决办法

2009-06-06 13:58 423 查看
http://hi.baidu.com/zeratul_bb/blog/item/3e2a44cf085cf33af8dc61de.html
获取xml时,出现“(十六进制值0x1F)是无效的字符之类Xml异常的解决办法
2008-12-1910:44
最近做新闻采集器,需要获取很多站点的xml,加载个别站点经常出现“(十六进制值0x1F)是无效的字符”问题,百思不的其解。对于问题站点xml的处理,开始的思路是既然直接用XmlDocument对象的Load()方法不行,就用LoadXML(),用HttpWebRequest获取url读到流里再转为xml,中间可以加一些非有效字符的过滤处理,但仍然无效,仅仅解决了请求超时的问题...

问题搁置了1周后,终于在今天解决了。

其实很简单,只加一条语句就搞定了

XmlDocumentdoc=newXmlDocument();

doc.Normalize();

//摘要:
//将此XmlNode下子树完全深度中的所有XmlText节点都转换成“正常”形式,在这种形式中只有标记(即标记、注释、处理指令、CDATA
//节和实体引用)分隔XmlText节点,也就是说,没有相邻的XmlText节点。


以下是转一位仁兄的贴:

最近碰到一个问题,我的一个把数据库中记录的信息暴露出来的WebService调用时候出问题了。报下面的错误信息:

System.InvalidOperationExceptionwasunhandled
Message="XML文档(1,823)中有错误。"
Source="System.Xml"
Message="“”(十六进制值0x0E)是无效的字符。行1,位置823。"
Source="System.Xml"

当这个错误发生时,WebService服务器端不会有任何错误,而调用这个WebService的客户端则会报上述错误。
是何原因导致的这个问题呢?
答案很简单,是WEBService暴露的XML文档中存在低序位非打印ASCII字符所致。
我们查看WebService返回的XML文档文档中,会有下面的XML文档节:其中的就是低序位ASCII字符。对应的字符如后:

<Value>在神奇天地裏誰叱咤風雨</Value>

会导致这些问题的低序位非打印ASCII字符包含以下字符:
#x0-#x8(ASCII0-8)
#xB-#xC(ASCII11-12)
#xE-#x1F(ASCII14-31)

下面就是一个简单演示这个问题的控制台程序,
为了简单起见,这里没有建立WebService,而是把一个类XML序列化存储到文件,然后再把这个文件反序列化读取出来:
其中的这个类的Value值中,放了一个低序位非打印ASCII字符。
执行这个控制台程序,就会报异常。“XML文档(3,12)中有错误。”

usingSystem;
usingSystem.Xml.Serialization;
usingSystem.IO;
usingSystem.Text;
usingSystem.Globalization;
namespaceTextSerialize
{
[Serializable]
publicclassMyClass
{
publicstringValue{get;set;}
}
classProgram
{
staticvoidMain(string[]args)
{
stringfileName="d://1.txt";
MyClassc=newMyClass();
c.Value=string.Format("在神奇{0}天地裏誰叱咤風雨",Convert.ToChar(14));
SaveAsXML(c,fileName,Encoding.UTF8);
objecto=ConvertFileToObject(fileName,typeof(MyClass),Encoding.UTF8);
MyClassd=oasMyClass;
if(d!=null)Console.WriteLine(d.Value);
elseConsole.WriteLine("null");
Console.ReadLine();
}
///<summary>
///序列化
///</summary>
///<paramname="objectToConvert"></param>
///<paramname="path"></param>
///<paramname="encoding"></param>
publicstaticvoidSaveAsXML(objectobjectToConvert,stringpath,Encodingencoding)
{
if(objectToConvert!=null)
{
Typet=objectToConvert.GetType();
XmlSerializerser=newXmlSerializer(t);
using(StreamWriterwriter=newStreamWriter(path,false,encoding))
{
ser.Serialize(writer,objectToConvert);
writer.Close();
}
}
}
///<summary>
///反序列化
///</summary>
///<paramname="path"></param>
///<paramname="objectType"></param>
///<paramname="encoding"></param>
///<returns></returns>
publicstaticobjectConvertFileToObject(stringpath,TypeobjectType,Encodingencoding)
{
objectconvertedObject=null;
if(!string.IsNullOrEmpty(path))
{
XmlSerializerser=newXmlSerializer(objectType);
using(StreamReaderreader=newStreamReader(path,encoding))
{
convertedObject=ser.Deserialize(reader);
reader.Close();
}
}
returnconvertedObject;
}
}
}

上面提到的WebService的那个问题,跟这个演示程序是一样的。

我们需要被序列化的内容中,存在低序位非打印ASCII字符时,.net会给我们正常序列化,会自动把低序位非打印ASCII字符转换成&#x编码的字符(这个XML规范中要求这么做的)。

但是,反序列化时候,如果需要反序列化的内容如果存在&#x编码的字符(映射到低序位非打印ASCII字符),则反序列化就会出错。

如果解决这个问题呢?

当然,最彻底的解决方法是修改反序列化的代码,让这些字符不会出错。但这个东西很多时候不归我们控制。这个方案不可行。

下一个方案就是剔除这些捣乱的字符。

我这里要给出的方案,是对这些字符序列化时作一次预处理,反序列化时,作一次反向处理。
这里为了演示的更有意义,我这里处理逻辑就是把低序位非打印ASCII字符转换成&#x编码的字符,和把&#x编码的字符转换成低序位非打印ASCII字符。
这样就可以使用我这里提供的函数,实现更多的处理逻辑。这两个函数的代码如下:

///<summary>
///把一个字符串中的低序位ASCII字符替换成&#x字符
///转换ASCII0-8->�-
///转换ASCII11-12->-
///转换ASCII14-31->-
///</summary>
///<paramname="tmp"></param>
///<returns></returns>
publicstaticstringReplaceLowOrderASCIICharacters(stringtmp)
{
StringBuilderinfo=newStringBuilder();
foreach(charccintmp)
{
intss=(int)cc;
if(((ss>=0)&&(ss<=8))||((ss>=11)&&(ss<=12))||((ss>=14)&&(ss<=32)))
info.AppendFormat("&#x{0:X};",ss);
elseinfo.Append(cc);
}
returninfo.ToString();
}
///<summary>
///把一个字符串中的下列字符替换成低序位ASCII字符
///转换�-->ASCII0-8
///转换-->ASCII11-12
///转换-->ASCII14-31
///</summary>
///<paramname="input"></param>
///<returns></returns>
publicstaticstringGetLowOrderASCIICharacters(stringinput)
{
if(string.IsNullOrEmpty(input))returnstring.Empty;
intpos,startIndex=0,len=input.Length;
if(len<=4)returninput;
StringBuilderresult=newStringBuilder();
while((pos=input.IndexOf("&#x",startIndex))>=0)
{
boolneedReplace=false;
stringrOldV=string.Empty,rNewV=string.Empty;
intle=(len-pos<6)?len-pos:6;
intp=input.IndexOf(";",pos,le);
if(p>=0)
{
rOldV=input.Substring(pos,p-pos+1);
//计算对应的低位字符
shortss;
if(short.TryParse(rOldV.Substring(3,p-pos-3),NumberStyles.AllowHexSpecifier,null,outss))
{
if(((ss>=0)&&(ss<=8))||((ss>=11)&&(ss<=12))||((ss>=14)&&(ss<=32)))
{
needReplace=true;
rNewV=Convert.ToChar(ss).ToString();
}
}
pos=p+1;
}
elsepos+=le;
stringpart=input.Substring(startIndex,pos-startIndex);
if(needReplace)result.Append(part.Replace(rOldV,rNewV));
elseresult.Append(part);
startIndex=pos;
}
result.Append(input.Substring(startIndex));
returnresult.ToString();
}


这样,我们这个演示程序的Main函数修改为下面的代码,也不会有任何错误发生。

staticvoidMain(string[]args)
{
Console.WriteLine(GetLowOrderASCIICharacters("123456񐀀"));
Console.WriteLine(GetLowOrderASCIICharacters("123456"));
Console.WriteLine(GetLowOrderASCIICharacters(""));
Console.WriteLine(GetLowOrderASCIICharacters("0123456789"));
Console.WriteLine(GetLowOrderASCIICharacters("/f"));

Console.WriteLine(GetLowOrderASCIICharacters("=-1"));
Console.WriteLine(GetLowOrderASCIICharacters(""));
Console.WriteLine(GetLowOrderASCIICharacters(""));
stringfileName="d://1.txt";
MyClassc=newMyClass();
c.Value=string.Format("在神奇{0}天地裏誰叱咤風雨",Convert.ToChar(14));
c.Value=ReplaceLowOrderASCIICharacters(c.Value);
SaveAsXML(c,fileName,Encoding.UTF8);
objecto=ConvertFileToObject(fileName,typeof(MyClass),Encoding.UTF8);
MyClassd=oasMyClass;
if(d!=null)
{
d.Value=GetLowOrderASCIICharacters(d.Value);
Console.WriteLine(d.Value);
}
elseConsole.WriteLine("null");
Console.ReadLine();
}
小结:低序位非打印ASCII字符在很多时候会给我们的系统带来问题,这部分字符必须作特殊处理。



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐