什么是 MiniXML?
MiniXML (mxml) 是一个非常轻量级、开源的 C 语言库,用于解析和生成 XML (eXtensible Markup Language) 数据。

它的核心特点是 “小” 和 “简单”:
- 轻量级:代码库非常小,编译后的库文件也小,适合资源受限的环境(如嵌入式系统)。
- 简单易用:API 设计直观,没有复杂的回调机制或复杂的对象模型,上手非常快。
- 单文件:整个库通常只有一个
.c文件和一个.h文件,极易集成到任何 C 项目中。
MiniXML 并不是一个功能齐全的 XML 解析器(像 libxml2 那样),它不支持 DTD (文档类型定义)、XML Schema 或 XPath 等高级特性,但对于简单的配置文件、数据交换等场景,它绰绰有余。
核心概念
在使用 MiniXML 之前,需要理解它的几个核心数据结构:
-
mxml_node_t:这是 MiniXML 中最重要的结构体,代表 XML 文档中的一个节点,一个节点可以是:
(图片来源网络,侵删)MXML_ELEMENT:元素节点,<book>。MXML_TEXT:文本节点,"C Programming"。MXML_INTEGER:整数节点。MXML_REAL:浮点数节点。MXML_CUSTOM:自定义类型节点,非常灵活。MXML_IGNORE:被忽略的节点。
-
*`mxml_node_t
**:所有 MiniXML 的操作都是通过指向节点的指针来完成的,XML 文档的根节点就是一个mxml_node_t *`。 -
节点关系:节点之间通过
child和next指针构成树状结构。child:指向第一个子节点。next:指向下一个兄弟节点。
对于 XML 片段 <a><b>hello</b><c>world</c></a>:
<a>是根节点。<b>和<c>是<a>的子节点,也是兄弟节点。- "hello" 是
<b>的子节点(文本节点)。 - "world" 是
<c>的子节点(文本节点)。
主要 API 函数
MiniXML 的 API 主要分为三类:加载/保存、查询、创建/修改。

加载和保存 XML
mxmlLoadFile():从文件流(FILE*)加载 XML 文档,返回根节点指针。mxmlLoadString():从 C 字符串加载 XML 文档,返回根节点指针。mxmlSaveFile():将 XML 树保存到文件流(FILE*)。mxmlSaveString():将 XML 树保存到 C 字符串(需要提供一个缓冲区)。mxmlDelete():非常重要! 释放整个 XML 树占用的内存,防止内存泄漏。
查询和遍历节点
这是最常用的部分,MiniXML 提供了非常方便的查询函数。
mxmlFindElement():按名称查找元素节点,这是最常用的查找函数。// 从 parent 节点开始查找名为 "name" 的元素 // 可以指定父节点名和兄弟节点名进行精确查找 mxml_node_t *mxmlFindElement(mxml_node_t *parent, const char *name, const char *attr, const char *value, int descend);
mxmlWalkNext():遍历 XML 树,获取下一个节点。mxmlGetText():从一个元素节点中获取文本内容。
创建和修改节点
mxmlNewElement():创建一个新的元素节点。mxmlNewText():创建一个新的文本节点。mxmlNewInteger():创建一个新的整数节点。mxmlNewReal():创建一个新的浮点数节点。mxmlLinkNode():将一个节点链接到父节点下。
实践示例
下面通过两个完整的例子来展示 MiniXML 的用法:解析(读取) 和 生成(写入)。
准备工作
你需要下载 MiniXML,它通常包含在一个名为 mxml-<version>.tar.gz 的压缩包中。
解压后,你会看到 mxml.h 和 mxml.c 这两个核心文件。
编译方法:
在你的 C 项目中,直接将 mxml.c 和你的源文件一起编译即可。
# 编译一个名为 my_app.c 的程序 gcc my_app.c mxml.c -o my_app
示例 1:解析 XML 文件
假设我们有如下一个名为 config.xml 的配置文件:
config.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<server>
<host>192.168.1.100</host>
<port>8080</port>
<timeout>30</timeout>
</server>
<database>
<name>myapp_db</name>
<user>admin</user>
<password>secret</password>
</database>
</config>
下面的 C 程序将读取这个文件,并提取出配置信息。
parse_example.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mxml.h" // 包含 MiniXML 头文件
int main() {
mxml_node_t *tree = NULL;
mxml_node_t *node = NULL;
// 1. 从文件加载 XML
FILE *fp = fopen("config.xml", "r");
if (fp == NULL) {
perror("无法打开 config.xml");
return 1;
}
tree = mxmlLoadFile(NULL, fp, MXML_NO_CALLBACK);
fclose(fp);
if (tree == NULL) {
fprintf(stderr, "无法解析 XML 文件\n");
return 1;
}
// 2. 查询并提取数据
// 查找 <server> 节点
node = mxmlFindElement(tree, tree, "server", NULL, NULL, MXML_DESCEND);
if (node != NULL) {
// 在 <server> 节点下查找 <host>
mxml_node_t *host_node = mxmlFindElement(node, node, "host", NULL, NULL, MXML_DESCEND);
if (host_node != NULL) {
printf("Server Host: %s\n", mxmlGetText(host_node, 0));
}
// 查找 <port>
mxml_node_t *port_node = mxmlFindElement(node, node, "port", NULL, NULL, MXML_DESCEND);
if (port_node != NULL) {
printf("Server Port: %s\n", mxmlGetText(port_node, 0));
}
}
// 查找 <database> 节点
node = mxmlFindElement(tree, tree, "database", NULL, NULL, MXML_DESCEND);
if (node != NULL) {
// 在 <database> 节点下查找 <name>
mxml_node_t *name_node = mxmlFindElement(node, node, "name", NULL, NULL, MXML_DESCEND);
if (name_node != NULL) {
printf("Database Name: %s\n", mxmlGetText(name_node, 0));
}
}
// 3. 释放内存
mxmlDelete(tree);
return 0;
}
编译与运行:
gcc parse_example.c mxml.c -o parse_example ./parse_example
预期输出:
Server Host: 192.168.1.100
Server Port: 8080
Database Name: myapp_db
示例 2:生成 XML 文件
下面的 C 程序将创建一个 XML 树,并将其写入到一个新的文件中。
generate_example.c
#include <stdio.h>
#include <stdlib.h>
#include "mxml.h"
int main() {
mxml_node_t *xml = NULL;
mxml_node_t *root = NULL;
mxml_node_t *item_node = NULL;
mxml_node_t *text_node = NULL;
FILE *fp = NULL;
// 1. 创建 XML 树
// 创建根节点 <library>
root = mxmlNewElement(NULL, "library");
xml = root;
// 创建第一个 <book> 节点
item_node = mxmlNewElement(xml, "book");
mxmlNewElement(item_node, "title");
text_node = mxmlNewText(mxmlFindElement(item_node, item_node, "title", NULL, NULL, MXML_DESCEND), 0);
mxmlSetText(text_node, 0, "The C Programming Language");
mxmlNewElement(item_node, "author");
text_node = mxmlNewText(mxmlFindElement(item_node, item_node, "author", NULL, NULL, MXML_DESCEND), 0);
mxmlSetText(text_node, 0, "Kernighan & Ritchie");
mxmlNewInteger(item_node, "year", 1978);
// 创建第二个 <book> 节点
item_node = mxmlNewElement(xml, "book");
mxmlNewElement(item_node, "title");
text_node = mxmlNewText(mxmlFindElement(item_node, item_node, "title", NULL, NULL, MXML_DESCEND), 0);
mxmlSetText(text_node, 0, "Clean Code");
mxmlNewElement(item_node, "author");
text_node = mxmlNewText(mxmlFindElement(item_node, item_node, "author", NULL, NULL, MXML_DESCEND), 0);
mxmlSetText(text_node, 0, "Robert C. Martin");
mxmlNewInteger(item_node, "year", 2008);
// 2. 将 XML 树保存到文件
fp = fopen("library.xml", "w");
if (fp == NULL) {
perror("无法创建 library.xml");
mxmlDelete(xml); // 出错前记得释放内存
return 1;
}
mxmlSaveFile(fp, xml, MXML_NO_CALLBACK);
fclose(fp);
// 3. 释放内存
mxmlDelete(xml);
printf("成功生成 library.xml 文件\n");
return 0;
}
编译与运行:
gcc generate_example.c mxml.c -o generate_example ./generate_example
生成的 library.xml 文件内容:
<?xml version="1.0"?>
<library>
<book>The C Programming Language</title>
<author>Kernighan & Ritchie</author>
<year>1978</year>
</book>
<book>Clean Code</title>
<author>Robert C. Martin</author>
<year>2008</year>
</book>
</library>
MiniXML vs. libxml2
| 特性 | MiniXML | libxml2 |
|---|---|---|
| 复杂度 | 非常简单,API 直观 | 非常复杂,功能强大但学习曲线陡峭 |
| 大小 | 极小,单文件,易于集成 | 较大,依赖其他库 |
| 功能 | 基本的解析和生成,无高级特性 | 支持完整的 XML 标准(DTD, Schema, XPath, XSLT 等) |
| 性能 | 对于简单任务足够 | 性能极高,经过高度优化 |
| 适用场景 | 嵌入式系统、配置文件、简单数据交换 | 桌面应用、Web 服务器、需要完整 XML 支持的复杂项目 |
MiniXML 是一个在 C 语言处理 XML 时非常出色的“瑞士军刀”,如果你需要的只是读取和写入简单的 XML 结构,并且希望项目保持轻量、易于维护,MiniXML 是你的不二之选,它的简洁性和易用性是其最大的优势。
如果你的项目需要处理复杂的 XML 规范、进行 XPath 查询或 XSLT 转换,libxml2 这样的全功能库才是更合适的选择,但对于绝大多数日常任务,MiniXML 完全够用且非常高效。
