翻译下Mini-XML官方提供的帮助文档。链接:Getting Started with Mini-XML。
来源:http://www.fuzhijie.me/?p=283
这章讲述如果使用Mini-XML操作XML文件。Mini-XML提供了如下功能:
1、在内存中创建和操作XML文档的函数
2、读取UTF-8/UTF-16编码的XML文件或字符串
3、输出UTF-8编码的XML文件或字符串
4、在内存足够的前提下,支持任意的元素名称、属性和属性值
5、支持叶子节点中的整型、实数、cdata和文本类型
6、”Find”、”index”、”walk”等函数用于便捷地存取XML文档中的数据
The Bascis
使用Mini-XML,只需要包含一个头文件:
1
|
#include <mxml.h>
|
当使用”-lmxml”选项时,Mini-XML库将被你的程序包含。(注:编译使用mxml 2.6的程序居然要链接pthread库,不知为何)
1
|
gcc -o myprogram myprogram.c -lmxml ENTER
|
如果你安装了pkg-config软件,你可以使用它来决定适合的编译和链接选项。
1
2
|
pkg-config --cflags mxml ENTER
pkg-config --libs mxml ENTER
|
Nodes
XML文件的每块信息(元素、文本、数字)都以内存中的”Nodes”表示。Nodes被定义为mxml_node_t结构体。mxml_node_t结构体中的type成员定义了节点了类型(元素、整型、opaque、实数或者文本),它决定了你想获取的数据。
每个节点都有一个user_data成员,它用于将程序指定的数据同节点关联起来。
使用mxmlNewElement, mxmlNewInteger, mxmlNewOpaque, mxmlNewReal, mxmlNewText mxmlNewTextf mxmlNewXML函数可以创建新的节点。只有元素有子节点,顶级的node必须是一个元素,它通常是mxmlNewXML()创建的<?xml version=”1.0″?>。
每个节点有很多指针指向父节点、孩子节点、左右兄弟节点。如果你有一个如下的XML文件:
01
02
03
04
05
06
07
08
09
10
11
12
13
|
<?
xml
version
=
"1.0"
?>
<
data
>
<
node
>val1</
node
>
<
node
>val2</
node
>
<
node
>val3</
node
>
<
group
>
<
node
>val4</
node
>
<
node
>val5</
node
>
<
node
>val6</
node
>
</
group
>
<
node
>val7</
node
>
<
node
>val8</
node
>
</
data
>
|
该文件在内存中的节点树如下(注:一次载入XML,这是DOM方式;Mini-XML也支持SAX方式):
01
02
03
04
05
06
07
08
09
10
11
|
?xml
|
data
|
node - node - node - group - node - node
| | | | | |
val1 val2 val3 | val7 val8
|
node - node - node
| | |
val4 val5 val6
|
“-”表示下一个节点,”|”指向第一个孩子节点。
一旦XML数据使用完毕,使用mxmlDelete函数递归地释放一个节点或者整棵树的内存。
1
|
mxmlDelete(tree);
|
创建XML文档
通过大量的mxmlNew函数可以创建和更新内存中的XML文档。下面的代码创建了一个上面例子所使用XML文档。(注:使用者不需要自己申请内存)
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
mxml_node_t *xml;
/* <?xml ... ?> */
mxml_node_t *data;
/* <data> */
mxml_node_t *node;
/* <node> */
mxml_node_t *group;
/* <group> */
xml = mxmlNewXML(
"1.0"
);
data = mxmlNewElement(xml,
"data"
);
node = mxmlNewElement(data,
"node"
);
mxmlNewText(node, 0,
"val1"
);
node = mxmlNewElement(data,
"node"
);
mxmlNewText(node, 0,
"val2"
);
node = mxmlNewElement(data,
"node"
);
mxmlNewText(node, 0,
"val3"
);
group = mxmlNewElement(data,
"group"
);
node = mxmlNewElement(group,
"node"
);
mxmlNewText(node, 0,
"val4"
);
node = mxmlNewElement(group,
"node"
);
mxmlNewText(node, 0,
"val5"
);
node = mxmlNewElement(group,
"node"
);
mxmlNewText(node, 0,
"val6"
);
node = mxmlNewElement(data,
"node"
);
mxmlNewText(node, 0,
"val7"
);
node = mxmlNewElement(data,
"node"
);
mxmlNewText(node, 0,
"val8"
);
|
我们通过mxmlNewXML创建所有XML文件都需要的<?xml version=”1.0″?>节点。
1
|
xml = mxmlNewXML(
"1.0"
);
|
接着使用mxmlNewElement函数创建节点。第一个参数指定父节点,第二个参数指定元素名字。
1
|
data = mxmlNewElement(xml,
"data"
);
|
文件中的每个<node>…</node>是用mxmlNewElement和mxmlNewText函数创建的。mxmlNewText第一个参数指明了父节点。第二个节点指明文本之前是否需要空格(本例中是0或者false)。最后一个参数指明实际要添加的文本。
1
2
|
node = mxmlNewElement(data,
"node"
);
mxmlNewText(node, 0,
"val1"
);
|
最后的XML文档可以被保存或者处理,就象从硬盘和字符串加载而来的一样。
加载XML
你可以使用mxmlLoadFile函数加载一个XML文件。
1
2
3
4
5
6
7
|
FILE
*fp;
mxml_node_t *tree;
fp =
fopen
(
"filename.xml"
,
"r"
);
tree = mxmlLoadFile(NULL, fp,
MXML_TEXT_CALLBACK);
fclose
(fp);
|
第一个参数指定了一个存在的XML父节点(如果存在的话)。除非你想将许多XML数据连接起来,一般情况这个参数传递一个NULL。如果node不为空,XML文件必须包含一个拥有?xml元素的完整XML文档。
第二个参数指明使用fopen和popen打开文件后得到的文件句柄。XML过滤程序可以使用标准输入。
第三个参数指明了回调函数。这个回调函数返回孩子数值类型:MXML_CUSTOM, MXML_IGNORE, MXML_INTEGER, MXML_OPAQUE, MXML_REAL, or MXML_TEXT。加载的回调函数在Chapter 3中有详细介绍。示例代码中使用MXML_TEXT_CALLBACK常量指明了所有的数据节点包含的以空格分隔的文本值。另外一些标准的回调函数包括MXML_IGNORE_CALLBACK, MXML_INTEGER_CALLBACK, MXML_OPAQUE_CALLBACK, 和MXML_REAL_CALLBACK。(注:Mini-XML生成的xml没有格式,所有的数据都放在一行,可以在保存的时候调用一个回调函数,生成格式美观的xml文件。)
mxmlLoadString函数从一个字符串中加载XML节点。
1
2
3
4
5
6
|
char
buffer[8192];
mxml_node_t *tree;
...
tree = mxmlLoadString(NULL, buffer,
MXML_TEXT_CALLBACK);
|
第一个和第三个参数同mxmlLoadFile是一样的。第二个参数指明了XML来源字符串和字符缓存。如果父节点为空的话,它们必须包含?xml元素。
保存XML
使用mxmlSaveFile保存XML文件。
1
2
3
4
5
6
|
FILE
*fp;
mxml_node_t *tree;
fp =
fopen
(
"filename.xml"
,
"w"
);
mxmlSaveFile(tree, fp, MXML_NO_CALLBACK);
fclose
(fp);
|
第一个参数是要保存的XML节点。它一般是一个指向XML文档的顶层?xml节点的指针。
第二个参数是用fopen或popen打开文件后得到的文件句柄。XML过滤程序可以使用标准输出。
第三个参数是保存文件时的空格回调函数。空格回调函数在Chapter 3已经介绍过了。前面的例子使用MXML_NO_CALLBACK常量指明了没有特殊的空格需要处理。
mxmlSaveAllocString和mxmlSaveString 函数把XML节点保存为字符串:
01
02
03
04
05
06
07
08
09
10
|
char
buffer[8192];
char
*ptr;
mxml_node_t *tree;
...
mxmlSaveString(tree, buffer,
sizeof
(buffer),
MXML_NO_CALLBACK);
...
ptr = mxmlSaveAllocString(tree, MXML_NO_CALLBACK);
|
第一个和最后一个参数同mxmlSaveFile是一样的。mxmlSaveString需要一个固定大小的字符串缓冲来保存XML文档,需要指明该缓冲的大小。mxmlSaveAllocString()则使用malloc()自行申请字符串缓冲。
控制行包装
当保存XML文档时,Mini-XML通常将输出的列设置为75,这样文本在终端中比较适合查看。mxmlSetWrapMargin函数可以覆盖默认的边距。
查找和遍历节点
mxmlWalkPrev和mxmlWalkNext函数用于迭代XML节点数。
1
2
3
4
5
6
7
|
mxml_node_t *node;
node = mxmlWalkPrev(current, tree,
MXML_DESCEND);
node = mxmlWalkNext(current, tree,
MXML_DESCEND);
|
另外,通过mxmlFindElement可以通过名字查找元素或者节点。
1
2
3
4
5
|
mxml_node_t *node;
node = mxmlFindElement(tree, tree,
"name"
,
"attr"
,
"value"
,
MXML_DESCEND);
|
名字,属性和值可以传递NULL指针,这样将从匹配所有类型(如果”name”、”attr”为空时,通过”value”似乎搜不到节点)。
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
/* Find the first "a" element */
node = mxmlFindElement(tree, tree,
"a"
,
NULL, NULL,
MXML_DESCEND);
/* Find the first "a" element with "href"
attribute */
node = mxmlFindElement(tree, tree,
"a"
,
"href"
, NULL,
MXML_DESCEND);
/* Find the first "a" element with "href"
to a URL */
node = mxmlFindElement(tree, tree,
"a"
,
"href"
,
"http://www.easysw.com/"
,
MXML_DESCEND);
/* Find the first element with a "src"
attribute */
node = mxmlFindElement(tree, tree, NULL,
"src"
, NULL,
MXML_DESCEND);
/* Find the first element with a "src"
= "foo.jpg" */
node = mxmlFindElement(tree, tree, NULL,
"src"
,
"foo.jpg"
,
MXML_DESCEND);
|
可以使用mxmlFindElement函数进行循环访问:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
mxml_node_t *node;
for
(node = mxmlFindElement(tree, tree,
"name"
,
NULL, NULL,
MXML_DESCEND);
node != NULL;
node = mxmlFindElement(node, tree,
"name"
,
NULL, NULL,
MXML_DESCEND))
{
...
do
something ...
}
|
MXML_DESCEND参数出现的这个位置事实上还可以是以下三种:
1、MXML_NO_DESCEND表示不要查找任何孩子节点,仅仅在同层的兄弟节点和父节点中查找,直到文档顶层。
The previous node from “group” would be the “node” element to the left, while the next node from “group” would be the “node” element to the right. (没大看明白这句话的意思)
2、MXML_DESCEND_FIRST表示仅仅查找到第一层孩子节点。你可能经常使用它来迭代一个父节点的直接孩子。比如”?xml”父节点的所有”group”元素和”node”元素。
这种模式仅仅适用于搜索函数。walk函数把它当成MXML_DESCEND,因为每次调用都是第一次。
3、MXML_DESCEND表示持续查找,直到文档最底部。”group”前一个节点是”val3″,下一个节点是”group”下面的第一个节点元素。
如果你想使用mxmlWalkNext()从”?xml”根节点遍历到树的底层,节点顺序应该是这样的:
?xml data node val1 node val2 node val3 group node val4 node val5 node val6 node val7 node val8
如果你从”val8″开始使用mxmlWalkPrev()遍历树,顺序将是反的,到”?xml”结束。
最后附上一个测试的例子,这段代码要解释一个XML文件。从代码可以看出Mini-XML提取元素值还是比较麻烦的,文本被当作是孩子节点,然后空格分隔的每个word都会成为节点,这些节点可以通过next遍历。XML文件格式如下:
1
2
3
4
5
6
7
8
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
config
version
=
|