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

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的兄弟,可以查看一下这方面的资料。

 

 

 

你可能感兴趣的:(xml,exception,String,File,byte,encoding)