您的位置:首页 > 其它

解决xml解析中报文格式和encoding不一致的解析错误

2009-07-03 21:04 736 查看
1.错误现象

我遇到过这样的xml文件,用c++解析的时候,报如下的错误:

Fatal Error at file "d:/test2.xml", line 1, column 40
Message: An exception occurred! Type:UTFDataFormatException, Message:invalid byte 2 (? of a 2-byte sequence.

用java解析这个文件的时候,报如下错误

Invalid byte 1 of 1-byte UTF-8 sequence.
java.io.UTFDataFormatException: Invalid byte 1 of 1-byte UTF-8 sequence.
at org.apache.xerces.impl.io.UTF8Reader.invalidByte(Unknown Source)
at org.apache.xerces.impl.io.UTF8Reader.read(Unknown Source)
at org.apache.xerces.impl.XMLEntityScanner.load(Unknown Source)
at org.apache.xerces.impl.XMLEntityScanner.skipChar(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
at org.apache.xerces.parsers.DOMParser.parse(Unknown Source)
at org.apache.xerces.jaxp.DocumentBuilderImpl.parse(Unknown Source)
at javax.xml.parsers.DocumentBuilder.parse(Unknown Source)

2.程序内容

java程序内容如下:

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class XMLTest {

/**
* @param args
*/
public static void main(String[] args) {
try {
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();

Document doc = docBuilder.parse("d:/test2.xml");
Element root = doc.getDocumentElement();
System.out.println("root --> "+root.getTagName());   //根节点名

}catch(Exception e){
System.out.println(e.getMessage());
e.printStackTrace();
}
}

}


c++程序用到了xerces-c,代码段如下:

XercesDOMParser *parser = NULL;
DOMTreeErrorReporter *errReporter = NULL;
DOMDocument *document = NULL;

DOMNode 		*DOMRootNode = NULL;

try
{
//初始化
parser = new XercesDOMParser;
errReporter = new DOMTreeErrorReporter();
parser->setErrorHandler(errReporter);
parser->parse("d//test2.xml");
if(errReporter->getSawErrors())
{
printf("xml格式错误/n");
delete errReporter;
delete parser;
return -1;
}
}catch (const OutOfMemoryException& outEx)
{
}catch (const XMLException& e)
{
}
catch (const DOMException& DomE)
{	}
catch (...)
{	}


3.原因分析

文件test2.xml内容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<InterBOSS>
<OrderMemberInfo>
<OrderNumber/>
<ProductID>1</ProductID>
<OrderSource>1</OrderSource>
<ProductOrderMembers>
<ProductOrderMember>
<ww>1</ww>
<Action>1</Action>
<MemberTypeID>1</MemberTypeID>
<EffDate>20090512161449</EffDate>
<Extends>
<Extend>
<CharacterID>95105</CharacterID>
<CharacterName>定位方式</CharacterName>
<CharacterValue>A</CharacterValue>
</Extend>
<Extend>
<CharacterID>95108</CharacterID>
<CharacterName>终端ID</CharacterName>
<CharacterValue>TEST</CharacterValue>
</Extend>
<Extend>
<CharacterID>95106</CharacterID>
<CharacterName>终端车牌号</CharacterName>
<CharacterValue>123456</CharacterValue>
</Extend>
<Extend>
<CharacterID>95107</CharacterID>
<CharacterName>终端型号</CharacterName>
<CharacterValue>TEST</CharacterValue>
</Extend>
<Extend>
<CharacterID>95109</CharacterID>
<CharacterName>终端类型</CharacterName>
<CharacterValue>2</CharacterValue>
</Extend>
</Extends>
</ProductOrderMember>
</ProductOrderMembers>
</OrderMemberInfo>
</InterBOSS>


我把这个文件放在本地,用ie打开该文件,解析有问题,不能正常显示。

修改encoding="gbk",然后说用ie打开,就没有问题了。

再次运行java程序和c++程序,正常运行,没有报告错。

说明文件格式有问题,最简单的解决办法就是修改文件的encoding段。

但是有些时候这样做起来可操作性能不强。

我遇到的问题是,别人发给我一个错误的文件,我打电话给对方,告诉他,文件格式有问题,对方不承认,还说别的厂商为什么没有找他,“晕”,死活不修改,而且程序一直会自动运行,手工去修改显然不显示,用脚本修改文件的额内容也不好。

放心,程序是“万能”的,平时遇到的所谓bug,看似不可能的问题,认真分析之后,都能通过程序来解决问题。

4.分析、解决问题

既然xml文件的内容没有被编码为encoding指定的内容,我们可以把文件的内容读出来,然后在进行编码,将编码后的内容进行解析,问题就解决了。

再不修改文件内容的情况下,修改java程序,内容如下:

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class XMLTest {

/**
* @param args
*/
public static void main(String[] args) {
try {
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
File file =  new File("d:/test2.xml");
if(!file.exists())
{
System.out.println(file.getAbsolutePath() + "文件不存在");
return;
}
//先读文件
StringBuffer strBuffer = new StringBuffer();
FileInputStream fileIn = new FileInputStream(file);
byte[] buf = new byte[1024];
int len = 0;
while( (len = fileIn.read(buf)) > 0)
{
String str = new String(buf, 0, len);
strBuffer.append(str);
}
System.out.print(strBuffer.toString());

ByteArrayInputStream stream = new ByteArrayInputStream((strBuffer.toString()).getBytes("utf-8"));
//InputStream inStream =

Document doc = docBuilder.parse(stream);
Element root = doc.getDocumentElement();
System.out.println("root --> "+root.getTagName());   //根节点名
}
catch(Exception e){
System.out.println(e.getMessage());
e.printStackTrace();
}
}

}


运行正确,成功打印出根节点名字。

程序先将文件的内容读出,然后转换成utf-8的编码,组装成一个字符流进行解析。

c++程序也是一样,先读出文件内容,然后用icu转换成utf-8编码,组装成内容结构进行解析。

c++程序段内容如下:

XercesDOMParser *parser = NULL;
DOMTreeErrorReporter *errReporter = NULL;
DOMDocument *document = NULL;

DOMNode 		*DOMRootNode = NULL;

try
{
//初始化
parser = new XercesDOMParser;
errReporter = new DOMTreeErrorReporter();
parser->setErrorHandler(errReporter);

//1.读取文件
FILE *fReadFile = NULL;
if ((fReadFile = fopen(fullFileName.c_str(), "r"))==NULL)
{
printf("读取文件出错,文件不存在或者没有读取权限!/n");
return -1;
}

AISTD string strBuf;//存放文件内容
char line[g_line_size];
int read_len = 0;
while(!feof(fReadFile))
{
char *s = fgets(line, g_line_size, fReadFile);
if(s == NULL)
{
if(ferror(fReadFile))
{
errdesc = strerror(ferror(fReadFile));
return -1;
}
else
break;
}

strBuf += line;
}

//2.将文件内容进行编码
UnicodeString unicodeStr(strBuf.c_str());//得到unicode
UConverter *conv = NULL;
UErrorCode status = U_ZERO_ERROR;
conv = ucnv_open("utf-8", &status);//打开转换服务的函数
if(U_FAILURE(status))
{
errdesc = "创建ICU转换器句柄失败.";
return -1;
}

//得到目标字符串
uint32_t len = 0, len2 = 0;
len2 = unicodeStr.length();
uint32_t xmlLen = unicodeStr.length() * 3;
char*		xmlBuf = new char[xmlLen];
UChar*	uchar_buf = unicodeStr.getBuffer(unicodeStr.length());//得到Unicode缓冲地址
len = ucnv_fromUChars(conv, xmlBuf, xmlLen, uchar_buf, len2, &status);
if(U_FAILURE(status))
{
fprintf(stderr, "status == %s/n", u_errorName(status));
return -1;
}
//得到目标字符串
xmlBuf[len] = 0;//将编码后的xml报文截断
MemBufInputSource *memBufIS = new MemBufInputSource( (const XMLByte*)xmlBuf, strlen(xmlBuf), "STRBUF",false);

//开始解析报文
parser->parse(*memBufIS);

if(errReporter->getSawErrors())
{
printf("xml格式错误/n");			         delete errReporter;
delete parser;
return -1;
}

//得到文档对象
document = parser->getDocument();
//得到dom根节点
DOMRootNode = (DOMNode*)document->getDocumentElement();

//delete []xmlBuf;

}catch(otl_exception& p){
}catch (const OutOfMemoryException& outEx)
{
}catch (const XMLException& e)
{
}
catch (const DOMException& DomE)
{
}
catch (...)
{
}


上面用到了一些icu的函数,没有用过icu的兄弟,可以查看一下这方面的资料。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐