XML不可能比过Json或Yaml的,因为太复杂了。但是有些老旧项目还在使用XML解析器(expat),所以顺便学习了一下。
实验环境,在CentOS操作系统上安装expat开发库:
sudo yum install expat-devel
从 https://libexpat.github.io/doc/getting-started/ 下载
- elements.c
- outline.c
编译运行:
gcc elements.c && cat a.xml | ./a.out
gcc outline.c && cat a.xml | ./a.out
如https://www.xml.com/pub/a/1999/09/expat/index.html所说,学会4个expat函数,就可以应付80%的场景,接下来我们来体验一下。
(1) 创建一个解析器
XML_Parser parser = XML_ParserCreate(NULL);
(2) 挂钩子函数,处理起始和结束标签
void startElement(void *userData, const XML_Char *name, const XML_Char **atts) {...}
void endElement(void *userData, const XML_Char *name) {...}
XML_SetElementHandler(parser, startElement, endElement);
(3) 挂钩子函数,处理内容
void handle_data(void *data, const char *content, int length) {...}
XML_SetCharacterDataHandler(parser, handle_data);
(4) 开始解析
if (XML_Parse(parser, buf, (int)len, done) == XML_STATUS_ERROR) {
举个例子,假设现在有a.xml如下:
111
222
333
444
我们想把它转换成yaml格式,显示在屏幕上,如下:
note:
aaa: '111'
bbb: '222'
ccc: '333'
ddd: '444'
代码可以是这样的:
#include
#include
#include
#include
#include
#include
struct node {
size_t depth {0};
std::string name {""};
std::string value {""};
};
std::vector root;
node current;
void startElement(void *userData, const XML_Char *name, const XML_Char **atts)
{
int *depthPtr = (int *)userData;
if (current.name.length() != 0)
{
root.push_back(current);
}
current.name = name;
current.depth = *depthPtr;
*depthPtr += 1;
}
void endElement(void *userData, const XML_Char *name)
{
int *depthPtr = (int *)userData;
if (current.name.length() != 0)
{
root.push_back(current);
current.depth = 0;
current.name = "";
current.value = "";
}
*depthPtr -= 1;
}
void handle_data(void *data, const char *content, int length)
{
current.value = content;
current.value.erase(length);
}
void show_data()
{
for (auto e : root)
{
std::cout << std::string(e.depth,' ') << e.name << ":" << e.value << std::endl;
}
}
int main(int argc, const char *argv[])
{
XML_Parser parser = XML_ParserCreate(NULL);
XML_SetElementHandler(parser, startElement, endElement);
XML_SetCharacterDataHandler(parser, handle_data);
int depth = 0;
XML_SetUserData(parser, &depth);
std::ifstream ifs(argv[1]);
std::string line;
while (std::getline(ifs, line))
{
XML_Parse(parser, line.c_str(), line.length(), 0);
}
XML_ParserFree(parser);
show_data();
return 0;
}
运行:
$ g++ a.c --std=c++11 -lexpat && ./a.out a.xml
note:
aaa:111
bbb:222
ccc:333
ddd:444