-- 作者:anchen0617
-- 发布时间:11/13/2004 2:31:00 PM
-- 技巧:如何利用Xerces-C++解析包含中文字符的XML文档
Xerces-C++是目前广泛使用的XML解析器,在利用它编写XML应用以处理包含中文字符的XML文档时会出现字符编码错误,如何正确解决这个问题,对于我们开发XML的应用程序非常有用。本文给出了正确处理的方法和实现类的代码,希望能对大家有所帮助。 1.背景介绍 随着XML技术的普及应用,我国的各行各业有可能会生成大量的包含中文字符的XML文档。尽管基于Java技术的XML Parser能较好处理这些XML文档,但是,它不能解决所有的问题,原因是应用开发的复杂性,尤其在我国更为明显.我们知道,企业的许多应用系统的开发采用了C、C++、VB、Delphi、FoxPro等等语言,这些应用不可能用Java语言重新实现,那么,问题就出现了,如何让这些应用也能处理XML文档,甚至是包含中文字符的XML文档呢? 目前有关XML Parser C++语言的实现,非常著名的有Apache组织的Xerces和IBM的XML4C。Aparche的Xerces来源于IBM的XML4C,所以,它们的编程接口是一致的。二者关键的不同在于字符编码。Apache的Xerces1.6目前只支持少数的字符编码,如ASCII, UTF-8, UTF-16, UCS4, EBCDIC IBM037 和 IBM1140, ISO-8859-1 和Windows-1252。因而,采用Apache的Xerces C++解析器不能处理包含中文字符的XML文档。IBM的XML4C支持多达100种字符编码,它将Xerces和International Components for Unicode (ICU)结合了起来。因而,我们可以选用XML4C作为XML的解析器。在使用XML4C之前,我们需要首先确认XML4C的bin目录是否包含在系统的Path之中,bin目录必须要有这些dll:xerces-c_1_6_0D.dll,icudt20.dll,icuuc20.dll,icuuc20d.dll。 2. 问题描述 在XML4C应用过程中,我发现调用XML4C提供的一些API并不能很好解决中文问题。如解析XML时生成DOM_Document, 并利用DOM方法得到某一DOM_Node节点时,为了获得DOM_Node的名字或值,需要调用DOM_Node类的getNodeName()或getNodeValue()方法,并得到DOMString对象。根据API文档描述,DOMString类的transcode()方法,返回字符串的拷贝,并依照本地代码页对此字符串进行编码处理。因而,我在解析图1的XML文档并试图获得"爱国的人们"节点的名字时,transcode方法返回不完整的节点名"爱国",而不是完整的"爱国的人们",这样,我们就无法利用这些信息来进行字符串比较等等操作,XML的处理就会出现问题。因而,DOMString的transcode方法并不能处理XML的中文字符。 <?xml version='1.0' encoding='GB2312' ?> … … <爱国的人们> … … </爱国的人们> … … 图 1 3.解决的方法 针对这种情况,我仔细分析了XML4C提供的DOMPrint例子,此例子能顺利解析含中文字符的XML文档,并能打印出XML解析后生成的DOM_Document。它利用了Xerces的XMLFormatter和XMLFormatTarget类: XMLFormatter类,提供基本的格式化字符串功能,将解析器生成的基于Unicode的XML数据转换为非Unicode环境中使用的数据,如本地字符编码等; XMLFormatTarget类,为XMLFormatter格式化字符串提供目的地,它需要派生,并利用它的writeChars()方法得到格式化后的字符串。 基于以上分析,我改进了DOMPrint的例子程序,实现了一个XMLFormatTarget类的子类,代码如下: //类定义 #include <framework/XMLFormatter.hpp> class StrFormatTarget : public XMLFormatTarget { public: char * GetResult(); StrFormatTarget() {}; ~StrFormatTarget () {}; void writeChars(const XMLByte* const toWrite, const unsigned int count, XMLFormatter * const formatter); private: char * buffer; StrFormatTarget (const StrFormatTarget & other); void operator=(const StrFormatTarget & rhs); }; //类的实现 void StrFormatTarget::writeChars(const XMLByte* const toWrite, const unsigned int count, XMLFormatter * const formatter) { buffer = (char *)malloc(count + 1); memset(buffer,0,count+1); memcpy(buffer, (char *) toWrite, count); }; char * StrFormatTarget::GetResult() { char * ret = (char *)malloc(strlen(buffer) +1); strcpy(ret, buffer); ret[strlen(buffer)] = 0; free(buffer); return ret; } XML文档的编码定义由文档头<?xml version='1.0' encoding='…' ?>给出,支持中文字符编码,我们选用'GB2312'。Encoding信息的获取可以有两种方式,第一,可以直接分析xml字符串,得到encoding的值;第二,利用DOMParser解析XML文档时,设定parser能创建XMLDeclType节点,即: DOMParser parser; … parser.setToCreateXMLDeclTypeNode(true). 在利用XML4C编程时,我们常遇见的类型是DOMString,XMLCh *,我们需要将这些类型的数据都转换为char *,然后,进行字符串的各种处理。我这里给出了一个类StrTransformer,专门解决有关字符编码的问题,它不仅仅支持中文字符编码,而且,可支持XML4C的其它字符编码。核心代码如下: // header file class StrTransformer { public: char * ChangeStr(const DOMString& s); char * ChangeStr(const XMLCh * str); StrTransformer (char * encoding); XMLFormatter * format; StrFormatTarget * target; ~StrTransformer(); DOMString * m_dom; }; // implement file #include "StrTransformer.h" #include <stdlib.h> #include <util/XMLString.hpp> // Construction/Destruction StrTransformer::StrTransformer(char * encoding) { target = new StrFormatTarget(); m_dom = new DOMString(encoding); format = new XMLFormatter(m_dom->rawBuffer(), target, XMLFormatter::NoEscapes, XMLFormatter::UnRep_CharRef); } StrTransformer::~StrTransformer() { delete target; delete m_dom; delete format; } char * StrTransformer::ChangeStr(const DOMString &s) { unsigned int lent = s.length(); if (lent <= 0) return NULL; XMLCh* buf = new XMLCh[lent + 1]; XMLString::copyNString(buf, s.rawBuffer(), lent); buf[lent] = 0; *format<< buf; delete [] buf; return target->GetResult(); } char * StrTransformer::ChangeStr(const XMLCh * str) { DOMString dom(str); return ChangeStr(dom); } 4 结束语 正确处理含有中文字符的XML文档,对于我们开发XML的应用程序非常有用,希望我的方法能为你提供帮助,如果有其它建议或疑问,欢迎与我联系(f_jian@sina.com)。
|