以文本方式查看主题

-  中文XML论坛 - 专业的XML技术讨论区  (http://bbs.xml.org.cn/index.asp)
--  『 DTD/XML Schema 』  (http://bbs.xml.org.cn/list.asp?boardid=23)
----  技巧:如何利用Xerces-C++解析包含中文字符的XML文档  (http://bbs.xml.org.cn/dispbbs.asp?boardid=23&rootid=&id=12026)


--  作者: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)。


W 3 C h i n a ( since 2003 ) 旗 下 站 点
苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
7,255.859ms