c++语言 xml数据绑定技术简介
The C++ Source
An Introduction to XML
Data Binding in C++
by Boris Kolpackov
May 4, 2007
原文:http://www.artima.com/cppsource/xml_data_binding.html
一个c++应用程序需要处理 xml 格式的数据,一般的不外乎有两种存取 xml 的API:文档对象模型(Document Object Model,dom)或者 xml 简单 API。(SAX)。DOM 将xml描述为一个树状的数据结构,以供程序遍历和存取。SAX 是一个事件驱动的 解析的XML API。应用程序注册它感兴趣的事件--诸如元素结点、属性、文本之类的东东。当解析 xml时,会触发应用程序注册的这些事件。
DOM 是先将整个 xml文档读入内存,然后再解析数据,而 SAX 是在解析过程中传递这些数据。
无论使用 DOM还是 SAX,要处理大量的 xml 数据都非易事。毕竟,DOM 和 sax 都是 xml 结构,和对元素、属性、文本的操作,在内存中的表达。程序员不得不写大量的代码,将xml编码翻译为程序可使用的逻辑。例如,下面是一个简单的xml文档,它描述了一个人。
<person>
<name>John Doe</name>
<gender>male</gender>
<age>32</age>
</person>
如果我们想确保这个人的年龄大于某个阀值,那么,无论是使用 DOM 还是 SAX,我们都必须先找到“年龄”这个元素,然后将文本 “32” 转换为 int 类型。这时才能做比较。通用API的另一个显著缺点是基于字串流的控制。在上面的例子中,要查找“年龄”元素,我们需要先顺序地解析 name 元素,如果元素拼写错误,那我们只有在运行时才能发现这个 bug。字串流的代码也影响了代码的可读性和可维护性。此外,通用 api 缺乏类型安全机制,因为所有的类型都表现为文本类型。例如,我们可以堂而皇之地将表示“性别”的元素和其它的任意东西做比较,而编译器不会给出任何警告。
DOMElement* gender = ...
if (gender->getTextContent () == "man")
{
...
}
近年来,出现了一种处理 xml的新手段。叫做 xml 数据绑定。xml数据绑定技术的出现,得益于 xml 语意描述语言 ( xml shemas )的发展。 xml 数据绑定技术的核心思想是:将数据解析到一个对象(而不再是内存中)。这样,程序员就不需要再处理那些 xml底层操作,因为这些对象可以被程序逻辑直接识别和执行。还是拿上面的例子来说,我们不再搜索表示年龄的文本串,把它手工转成 int,而是简单地调用 person对象的 age()方法,age 方法返回的结果是一个 int 。所谓“绑定”,就是说,“对象”必须是,而是只能是用于表达xml数据块。
对象,以及用于操作对象的相关代码--比如解析和序列化函数--是由 数据绑定工具 生成的。数据绑定工具 是一种 来自于 xml schema。 schema 是一种语法规范--规定了元素的名称、属性、内容,以及元素之间的结构关系。大多数的 数据绑定工具都使用了 W3c 的W3C XML Schema 规范。一来因为它用的比较普遍。二来因为它的良好的面向对象特性。下面一段 代码描述了我们前面的 person 文件。使用的是 w3c xml schema 规范。
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="gender_t">
<xs:restriction base="xs:string">
<xs:enumeration value="male"/>
<xs:enumeration value="female"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="person_t">
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="gender" type="gender_t"/>
<xs:element name="age" type="xs:short"/>
</xs:sequence>
</xs:complexType>
<xs:element name="person" type="person_t"/>
</xs:schema>
即使你不熟悉 xml schema,你也能看明白上面这些代码干了些什么。gender_t 是一种枚举类型。其值有两种可能: male 或 female。 person_t 被定义为一个序列,其中嵌套了 name, gender, age 元素。注意:术语“序列”,在xml schema 的意思是:in 元素按指定的顺序出现。a particular order as opposed to appearing multiple times. 最后,全局定义 person 指明了 结构体的根元素。关于 xml schema的更多资料,请参考 w3c规范:xml schema part0: primer。
像其它的直接解析 xml的 api一样, xml 数据绑定也支持 内存模式和 事件驱动模式。下一节中,我们将对比 xml 数据绑定技术和 dom 及 sax 的性能表现。本例中使用的 DOM 和 SAX 例子是基于 Apache 的一款C++ 开源 XML 解析器 Xerces。为了简单起见,不对数据做类型识别。
内存式的 xml 绑定。
In-Memory XML Data Binding
Based on an XML schema, a data binding compiler generates C++ classes that represent the given vocabulary as a tree-like in-memory data structure as well as parsing and serialization functions. The parsing functions are responsible for creating the in-memory representation from an XML instance while the serialization functions save the in-memory representation back to XML. For the schema presented in the introduction, a data binding compiler could generate the following code:
利用 xml schema,数据绑定工具生成了 c++ 类。这些类把 xml数据表现内存中的树状结构的数据。和一些解析、序列化的函数。解析函数负责从xml实例中抽取数据并表达为内存对象,而序列化函数则把内存对象还原为 xml。根据前面所给的 schema,数据绑定工具会生成下面的代码:
class gender_t
{
public:
enum value {male, female};
gender_t (value);
operator value () const;
private:
...
};
class person_t
{
public:
person_t (const string& name,
gender_t gender,
short age);
// name
//
string& name ();
const string& name () const;
void name (const string&);
// gender
//
gender_t& gender ();
gender_t gender () const;
void gender (gender_t);
// age
//
short& age ();
short age () const;
void age (short);
private:
...
};
std::auto_ptr<person_t> person (std::istream&);
void person (std::ostream&, const person_t&);
对比代码和 xml schema声明,可以看出 schema 编译工具将 schema 类型映射为c++ 中的类,将本地元素映射为一组操作,将全局元素映射为一组解析和序列化的函数。
下面,我们将深入观察三个普通的 xml 处理任务。使用 dom和 xml 数据绑定。这三个任务是:访问xml数据,修改现有的数据,创建新数据。通过这些试验,我们将证实 xml数据绑定比较于 dom 的优越性。
下面的代码使用数据绑定技术来读取 xml 文件中保存的某人的信息,如果此人的姓名大于30,则打印其姓名。简单起见,没有考虑错误处理。
ifstream ifs ("person.xml");
auto_ptr<person_t> p = person (ifs);
if (p->age () > 30)
cerr << p->name () << endl;
上面的例子简单明了。一旦xml文件读到内存中,代码便不再依赖 xml,代码和程序中的其它的对象模型配合得天衣无缝。 同时要注意 c++ class表现的数据是静态的。
下面的代码使用 dom 来完成相同的任务。
ifstream ifs ("person.xml");
DOMDocument* doc = read_dom (ifs);
DOMElement* p = doc->getDocumentElement ();
string name;
short age;
for (DOMNode* n = p->getFirstChild ();
n != 0;
n = n->getNextSibling ())
{
if (n->getNodeType () != DOMNode::ELEMENT_NODE)
continue;
string el_name = n->getNodeName ();
DOMNode* text = n->getFirstChild ();
if (el_name == "name")
{
name = text->getNodeValue ();
}
else if (el_name == "age")
{
istringstream iss (text->getNodeValue ());
iss >> age;
}
}
if (age > 30)
cerr << name << endl;
doc->release ();
范晨鹏
------------------
软件是一种态度
成功是一种习惯