解析
解析xml的思路,也很清晰,就是先获取文件指针,找到根节点,再找到子节点去遍历。
还是仿照之前网上的例子修改的。
#include <stdio.h>
#include <assert.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#define DEFAULT_XML_FILE "osd.xml"
static int parse_osd(xmlDocPtr doc, xmlNodePtr cur)
{
assert(doc || cur);
xmlChar *key;
cur = cur->xmlChildrenNode;
while (cur != NULL)
{
if ((!xmlStrcmp(cur->name, (const xmlChar *)"str")))
{
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
printf("str: %s\t", key);
xmlFree(key);
}
if ((!xmlStrcmp(cur->name, (const xmlChar *)"size")))
{
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
printf("size: %s\t", key);
xmlFree(key);
}
if ((!xmlStrcmp(cur->name, (const xmlChar *)"color")))
{
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
printf("color: %s\n", key);
xmlFree(key);
}
if ((!xmlStrcmp(cur->name, (const xmlChar *)"x")))
{
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
printf("x: %s\n", key);
xmlFree(key);
}
if ((!xmlStrcmp(cur->name, (const xmlChar *)"y")))
{
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
printf("y: %s\n", key);
xmlFree(key);
}
cur = cur->next;
}
return 0;
}
static int parse_osd_block(const char *file_name)
{
assert(file_name);
xmlDocPtr doc; //xml file tree
xmlNodePtr cur; //xml node
xmlChar *id;
//parse xml file tree
// doc = xmlParseFile(file_name);
doc = xmlReadFile(file_name, NULL, XML_PARSE_NOBLANKS);
if (doc == NULL)
{
fprintf(stderr, "Failed to parse xml file:%s\n", file_name);
goto FAILED;
}
//parse xml node
cur = xmlDocGetRootElement(doc);
if (cur == NULL)
{
fprintf(stderr, "Root is empty.\n");
goto FAILED;
}
if ((xmlStrcmp(cur->name, (const xmlChar *)"osd_block")))
{
fprintf(stderr, "The root is not osd_block.\n");
goto FAILED;
}
//node path traversal
cur = cur->xmlChildrenNode;
while (cur != NULL)
{
if ((!xmlStrcmp(cur->name, (const xmlChar *)"osd")))
{
id = xmlGetProp(cur, "num");
printf("num:%s\t",id);
parse_osd(doc, cur);
}
cur = cur->next;
}
xmlFreeDoc(doc);
return 0;
FAILED:
if (doc)
{
xmlFreeDoc(doc);
}
return -1;
}
int main(int argc, char*argv[])
{
char *xml_file = DEFAULT_XML_FILE;
if (argc == 2)
{
xml_file = argv[1];
}
if (parse_osd_block(xml_file) != 0)
{
fprintf(stderr, "Failed to parse osd block.\n");
return -1;
}
return 0;
}
运行结果
num:1 str: 初始化中 size: 4 color: 1
x: 50
y: 50
num:1 str: 初始化中 size: 4 color: 1
x: 50
y: 50
num:1 str: 初始化中 size: 4 color: 1
x: 50
y: 50
修改
也许已经注意到上面三个osd子节点的属性num都是 1,这在实际应用的很不合理,所以我们要改成自然顺序。
可以直接在查找子节点的地方加上
sprintf(tmpc,"%d",tmp);
xmlSetProp(cur,(const xmlChar *)"num",(const xmlChar *)tmpc);
tmp+=1;
编译运行会发现解析的打印和xml文件osd子节点已经被修改成自然顺序了
<?xml version="1.0" encoding="UTF-8"?>
<osd_block>
<osd num="1">
<str>初始化中</str>
<size>4</size>
<color>1</color>
<x>50</x>
<y>50</y>
</osd>
<osd num="2">
<str>初始化中</str>
<size>4</size>
<color>1</color>
<x>50</x>
<y>50</y>
</osd>
<osd num="3">
<str>初始化中</str>
<size>4</size>
<color>1</color>
<x>50</x>
<y>50</y>
</osd>
</osd_block>
XPath
上面的方法是我们一步步的找到文件指针,子节点,全部遍历找到修改的文件,其实实际运用中,往往用不到我们去全部遍历,经常用到的是找到特定的几个节点,修改几个特定的节点内容。这里有个便捷的方法:XPath。
XPath 是一门在 XML 文档中查找信息的语言。XPath 用于在 XML 文档中通过元素和属性进行导航。
XPath 使用路径表达式来选取 XML 文档中的节点或节点集。节点是通过沿着路径 (path) 或者步 (steps) 来选取的。
--[w3school]
XPath 常用的路径表达式
nodename 选取次节点的所有节点
/ 从根节点选取
// 从匹配选择的当前节点选择文档中的节点,而不考虑他们的位置
. 选取当前节点
.. 选取当前节点的父节点
@ 选取属性
比如我们想修改属性num为 2的osd节点里的x值,路径可以写 "/osd_block/osd[@num= '2']"
添加以下代码
xmlChar *xpath = ("/osd_block/osd[@num= '2']");
xmlXPathObjectPtr app_result = getNodeset(doc,xpath);
if(app_result ==NULL)
{
printf("app_result is NULL\n");
return ;
}
int i =0;
xmlChar *values;
if(app_result)
{
xmlNodeSetPtr nodeset = app_result->nodesetval;
xmlNodePtr cur2;
for(i=0;i<nodeset->nodeNr;i++)
{
cur = nodeset->nodeTab[i];
cur = cur->xmlChildrenNode;
while(cur != NULL)
{
if(!xmlStrcmp(cur->name, (const xmlChar *)"x"))
{
printf("%s\n",((char*)XML_GET_CONTENT(cur->xmlChildrenNode)));
xmlNodeSetContent(cur,(const xmlChar *)"56");
}
cur = cur->next;
}
}
xmlXPathFreeObject(app_result);
}
是把属性num为2的osd节点的x值改成56。其中getNodeset是
static xmlXPathObjectPtr getNodeset(xmlDocPtr doc,const xmlChar *xpath)
{
xmlXPathContextPtr context;
xmlXPathObjectPtr result;
context = xmlXPathNewContext(doc);
if(context == NULL)
{
printf("context is NULL\n");
return NULL;
}
result = xmlXPathEvalExpression(xpath,context);
xmlXPathFreeContext(context);
if(result == NULL)
{
printf("xmlXPathEvalExpression return NULL\n");
}
if(xmlXPathNodeSetIsEmpty(result->nodesetval))
{
xmlXPathFreeObject(result);
printf("nodeset is empty\n");
return NULL;
}
return result;
}
基本的操作可以使用了,另外,添加节点元素使用 xmlNewTextChild ,添加节点属性,用xmlNewProp。
下面的工作就是写一个小web页面(html)和xml文件交互了。