解析 修改 xml文件

解析

解析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文件交互了。

源码:https://pan.baidu.com/s/1qXHsP56

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,594评论 18 139
  • 1. XML总结 1.1. XML简介 XML : 可扩展的标记语言。(和HTML非常类似的) 可扩展的。 自定义...
    Ethan_Walker阅读 2,979评论 0 12
  • 使用Ono读取XML文件 在做App的过程中,读取XML文件是最常见的功能之一。并且在iOS的发展过程中涌现出了多...
    CZ_iOS阅读 3,312评论 1 14
  • 元旦假来了,情侣们的旅行计划也开始复苏了。 我们总说旅行是检验爱情的唯一标准,关于这一概论日语里有个词叫“成田分手...
    刺猬小姐_juju阅读 306评论 0 0
  • 1965前后,是古龙风格转型的重要时期。而「铁血大旗」(1965)在古龙的作品当中是相当特殊的一部。 特殊就在于,...
    iClaud阅读 1,762评论 1 1