欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

c#中XML解析文件出错解决方法

程序员文章站 2024-02-14 09:26:16
1.内容中含有xml预定好的实体,如“<”和“&”,对xml来说是禁止使用的,针对这种字符,解决方式是使用cdata部件以"

1.内容中含有xml预定好的实体,如“<”和“&”,对xml来说是禁止使用的,针对这种字符,解决方式是使用cdata部件以"<![cdata[" 标记开始,以"]]>"标记结束,是cdata内部内容被解析器忽略。具体说明参考《xml cdata是什么?》。

2.内容中含有低位非打印字符,解析时会报错:""(十六进制值 0x1d)是无效的字符.加载或保存xml时引发的异常.system.argumentexception: “”(十六进制值 0x1d)是无效的字符。

出错的原因是内容中含有低位非打印字符,处理方法是对其进行过滤,过滤方法为:

return system.text.regularexpressions.regex.replace(str,@"[\x00-\x08]|[\x0b-\x0c]|[\x0e-\x1f]";

以上两种情况,第一种较为普遍,第二种遇到情况比较少,在面对一些用户输入数据时生成xml,可以对xml结点内容执行上述过滤,以保证xml文件使用者可以正确解析xml文档。

以下是详细解释:

“”(十六进制值 0x1d)是无效的字符

加载或保存xml时引发的异常.system.argumentexception: “”(十六进制值 0x1d)是无效的字符。
产生原因是xml文件中包含低位非打印字符造成的
处理方法:在产生xml文件的时候,过滤低位非打印字符

把一个字符串中的 低序位 ascii 字符 替换成 字符
转换 ascii 0 - 8 -> -
转换 ascii 11 - 12 -> -
转换 ascii 14 - 31 -> -


简单的处理方法
return system.text.regularexpressions.regex.replace(httputility.htmlencode(str),@"[\x00-\x08]|[\x0b-\x0c]|[\x0e-\x1f]", "");

======================================================================================================================================================

复杂处理

获取xml时,出现“(十六进制值 0x1f)是无效的字符之类xml异常的解决办法2008-12-19 10:44最近做新闻采集器,需要获取很多站点的xml,加载个别站点经常出现“(十六进制值 0x1f)是无效的字符”问题,百思不的其解。对于问题站点xml的处理,开始的思路是既然直接用 xmldocument对象的load()方法不行,就用loadxml() ,用httpwebrequest 获取url读到流里再转为xml,中间可以加一些非有效字符的过滤处理,但仍然无效,仅仅解决了请求超时的问题...

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

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

xmldocument doc = new xmldocument();

doc.normalize();

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

以下是转一位仁兄的贴:

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

system.invalidoperationexception was unhandled
message="xml 文档(1, 823)中有错误。"
source="system.xml"
 message="“”(十六进制值 0x0e)是无效的字符。 行 1,位置 823。"
 source="system.xml"

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

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

会导致这些问题的 低序位非打印 ascii 字符包含以下字符:
#x0 - #x8 (ascii 0 - 8)
#xb - #xc (ascii 11 - 12)
#xe - #x1f (ascii 14 - 31)

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

using system;
using system.xml.serialization;
using system.io;
using system.text;
using system.globalization;
namespace textserialize
{
[serializable]
public class myclass
{
public string value { get; set; }
}
class program
{
static void main(string[] args)
{
string filename = "d:\\1.txt";
myclass c = new myclass();
c.value = string.format("在神奇{0}天地裏誰叱咤風雨", convert.tochar(14));
saveasxml(c, filename, encoding.utf8);
object o = convertfiletoobject(filename, typeof(myclass), encoding.utf8);
myclass d = o as myclass;
if (d != null) console.writeline(d.value);
else console.writeline("null");
console.readline();
}
/// <summary>
/// 序列化
/// </summary>
/// <param name="objecttoconvert"></param>
/// <param name="path"></param>
/// <param name="encoding"></param>
public static void saveasxml(object objecttoconvert, string path, encoding encoding)
{
if (objecttoconvert != null)
{
type t = objecttoconvert.gettype();
xmlserializer ser = new xmlserializer(t);
using (streamwriter writer = new streamwriter(path, false, encoding))
{
ser.serialize(writer, objecttoconvert);
writer.close();
}
}
}
/// <summary>
/// 反序列化
/// </summary>
/// <param name="path"></param>
/// <param name="objecttype"></param>
/// <param name="encoding"></param>
/// <returns></returns>
public static object convertfiletoobject(string path, type objecttype, encoding encoding)
{
object convertedobject = null;
if (!string.isnullorempty(path))
{
xmlserializer ser = new xmlserializer(objecttype);
using (streamreader reader = new streamreader(path, encoding))
{
convertedobject = ser.deserialize(reader);
reader.close();
}
}
return convertedobject;
}
}
}

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

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

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


如果解决这个问题呢?

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

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

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

       

/// <summary>
/// 把一个字符串中的 低序位 ascii 字符 替换成  字符
/// 转换 ascii 0 - 8 ->  - 
/// 转换 ascii 11 - 12 ->  - 
/// 转换 ascii 14 - 31 ->  - 
/// </summary>
/// <param name="tmp"></param>
/// <returns></returns>
public static string replaceloworderasciicharacters(string tmp)
{
stringbuilder info = new stringbuilder();
foreach (char cc in tmp)
{
int ss = (int)cc;
if (((ss >= 0) && (ss <= 8)) || ((ss >= 11) && (ss <= 12)) || ((ss >= 14) && (ss <= 32)))
info.appendformat("{0:x};", ss);
else info.append(cc);
}
return info.tostring();
}
/// <summary>
/// 把一个字符串中的下列字符替换成 低序位 ascii 字符
/// 转换  -  -> ascii 0 - 8
/// 转换  -  -> ascii 11 - 12
/// 转换  -  -> ascii 14 - 31
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public static string getloworderasciicharacters(string input)
{
if (string.isnullorempty(input)) return string.empty;
int pos, startindex = 0, len = input.length;
if (len <= 4) return input;
stringbuilder result = new stringbuilder();
while ((pos = input.indexof("", startindex)) >= 0)
{
bool needreplace = false;
string roldv = string.empty, rnewv = string.empty;
int le = (len - pos < 6) ? len - pos : 6;
int p = input.indexof(";", pos, le);
if (p >= 0)
{
roldv = input.substring(pos, p - pos + 1);
// 计算 对应的低位字符
short ss;
if (short.tryparse(roldv.substring(3, p - pos - 3), numberstyles.allowhexspecifier, null, out ss))
{
if (((ss >= 0) && (ss <= 8)) || ((ss >= 11) && (ss <= 12)) || ((ss >= 14) && (ss <= 32)))
{
needreplace = true;
rnewv = convert.tochar(ss).tostring();
}
}
pos = p + 1;
}
else pos += le;
string part = input.substring(startindex, pos - startindex);
if (needreplace) result.append(part.replace(roldv, rnewv));
else result.append(part);
startindex = pos;
}
result.append(input.substring(startindex));
return result.tostring();
}


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

 static void main(string[] args)
{
console.writeline(getloworderasciicharacters("123456????"));
console.writeline(getloworderasciicharacters("123456"));
console.writeline(getloworderasciicharacters(""));
console.writeline(getloworderasciicharacters("0123 456789"));
console.writeline(getloworderasciicharacters("\f"));
console.writeline(getloworderasciicharacters(" =-1"));
console.writeline(getloworderasciicharacters(" "));
console.writeline(getloworderasciicharacters(" "));
string filename = "d:\\1.txt";
myclass c = new myclass();
c.value = string.format("在神奇{0}天地裏誰叱咤風雨", convert.tochar(14));
c.value = replaceloworderasciicharacters(c.value);
saveasxml(c, filename, encoding.utf8);
object o = convertfiletoobject(filename, typeof(myclass), encoding.utf8);
myclass d = o as myclass;
if (d != null)
{
d.value = getloworderasciicharacters(d.value);
console.writeline(d.value);
}
else console.writeline("null");
console.readline();
}