zoukankan      html  css  js  c++  java
  • mxml 详解

    摘自:https://blog.csdn.net/shift_wwx/article/details/99677421

    前言:

      最近项目中需要将用户的配置信息进行保存,考虑到后期出厂这个配置文件可以清晰地动态更改,所以选择使用xml的方式。

      xml相关的库有很多,例如libxml2、Tinyxml、RapidXml、MinXml等。

      本文使用的是minxml,至于其他库的使用后续会进行补充和比较,敬请期待!

    下载:

    https://github.com/michaelrsweet/mxml/releases

    解压后请仔细阅读ReadMe,例如:

        Mini-XML comes with an autoconf-based configure script; just type the
        following command to get things going:
     
            ./configure
     
        The default install prefix is /usr/local, which can be overridden using the
        --prefix option:
     
            ./configure --prefix=/foo
     
        Other configure options can be found using the --help option:
     
            ./configure --help
     
        Once you have configured the software, type "make" to do the build and run
        the test program to verify that things are working, as follows:
     
            make
     
        If you are using Mini-XML under Microsoft Windows with Visual C++, use the
        included project files in the "vcnet" subdirectory to build the library
        instead.  Note: The static library on Windows is NOT thread-safe.

     使用:

    阅读完ReadMe基本上就有了mxml的概念了,例如:

    • 通过configure脚本产生Makefile,进而可以使用make命令;
    • 如果使用mxml,需要包含mxml.h;
    • 整个mxml的核心是一个链表,数据类型为mxml_node_t;

    mxml提供的接口很多,下面挑一些经常使用的接口。

    1、创建xml

    最开始xml肯定是不存在的,创建的接口为 mxmlNewXML,函数原型:

    mxml_node_t *mxmlNewXML (const char *version);

    参数是一个代表xml 版本信息的字符串,返回的是链表的头指针。

    来看下mxml_node_t的数据结构:

    struct mxml_node_s /**** An XML node. @private@ ****/
    {
        mxml_type_t type; /* Node type */
        struct mxml_node_s *next; /* Next node under same parent */
        struct mxml_node_s *prev; /* Previous node under same parent */
        struct mxml_node_s *parent; /* Parent node */
        struct mxml_node_s *child; /* First child node */
        struct mxml_node_s *last_child; /* Last child node */
        mxml_value_t value; /* Node value */
        int ref_count; /* Use count */
        void *user_data; /* User data */
    };
    
    typedef struct mxml_node_s mxml_node_t; /**** An XML node. ****/

    其中:

    • type为节点的类型,包括MXML_ELEMENT、MXML_INTEGER、MXML_OPAQUE、MXML_REAL、MXML_TEXT、MXML_CUSTOM、MXML_IGNORE等;
    • value为节点的属性值;
    • user_data是用户设定的特殊数据;

    这里的type会经常使用,根据不同的type需要进行get、set等操作,type的数据类型为:

    typedef enum mxml_type_e /**** The XML node type. ****/
    {
        MXML_IGNORE = -1, /* Ignore/throw away node @since Mini-XML 2.3@ */
        MXML_ELEMENT, /* XML element with attributes */
        MXML_INTEGER, /* Integer value */
        MXML_OPAQUE, /* Opaque string */
        MXML_REAL, /* Real value */
        MXML_TEXT, /* Text fragment */
        MXML_CUSTOM /* Custom data @since Mini-XML 2.1@ */
    } mxml_type_t;

    2、添加xml节点

    创建后会得到链表的头指针,后续根据这个头指针进行操作就可以了。

    当然,如果是一个已经存在的xml文件,肯定是不能通过创建的xml的形式获取链表头,而是通过load的方式,下面会解析load的接口使用。

    mxml 提供了很多添加节点的方法,例如:

    其中New*的接口最终调用的是mxmlAdd,区别是New是知道parent,而mxmlAdd之前并不一定需要知道parent,知道调用mxmlAdd的时候才需指定parent。

    建议使用New*的方式进行xml节点的添加。

    另外,选择不同的New*接口最终的节点type是不同的,详细看第 1 节。

    考虑到字符串中间可能有空格的形式,而mxmlNewText只是针对第一个单词,所以建议使用mxmlNewOpaque接口进行统一管理。

    建议也只是建议,个人可以根据实际情况进行接口选择。

    3、删除xml节点

    mxmlDelete不仅会删除当前节点,并且会进行free。当然会将该节点下的子节点一并delete掉;

    mxmlRelease只有在节点的ref_count为1时,才会进行释放,释放时调用mxmlDelete;

    mxmlRemove并不会做free,只是将节点从parent中remove掉,使用时注意内存情况及时回收;

    4、修改xml节点

    对于element的节点有:

    其他type 的节点有:

    5、查询xml 节点

    对于element的节点有:

    其他type 的节点有:

    还有几个特别的接口:

    其中mxmlFindElement 会经常在查询的时候使用,一般是先通过该接口找到element,才能进一步的确认value、attribute等信息。来看下函数原型:

    mxml_node_t *mxmlFindElement (
        mxml_node_t *node,
        mxml_node_t *top,
        const char *name,
        const char *attr,
        const char *value,
        int descend
    );
    • 第一个参数应该为父节点;
    • 第二个参数可以是父节点,也可以是根节点;
    • 第三个参数为要查找的element的名称;
    • 第四个参数为element的attr 名称;
    • 第五个参数为attr对应的属性值;

    另外,可以通过mxmlGetFirstChild 接口切换到子节点的,然后通过 

    查找同一级的其他节点。

    6、保存xml

    上面的一系列操作知识针对链表进行,最终需要将链表的信息保存到xml文件中或保存到string中。使用接口为:

    这里主要解析mxmlSaveFile,来看下函数原型:

    int mxmlSaveFile (
        mxml_node_t *node,
        FILE *fp,
        mxml_save_cb_t cb
    );
    • 第一个参数一般为根节点;
    • 第二个参数为xml文件的句柄;
    • 第三个参数为保存过程中的回调,一共是4次,分别为MXML_WS_BEFORE_OPEN、MXML_WS_AFTER_OPEN、MXML_WS_BEFORE_CLOSE、MXML_WS_AFTER_CLOSE;

    其他两个参数很好理解,主要是第三个参数,通过实际例子来理解,例如有个这样的节点:

    <string name="string1">hehe1</string>

    那么,

    <string 之前会触发MXML_WS_BEFORE_OPEN的callback;
    在"string1"> 之后会触发MXML_WS_AFTER_OPEN 的callback;
    在</string> 之前会触发MXML_WS_BEFORE_CLOSE的callback;
    在</string> 之后会触发MXML_WS_AFTER_CLOSE 的callback;

    注意:

    mxml中如果没有设定callback,那么xml文件中的是没有格式的,所以,用户需要通过callback处理这个格式。而在coding过程中需要考虑刚打开的xml文件中添加新节点(需要关注load的方式)、连续添加节点、修改已有节点等情况,因为在save的时候每个节点都会触发callback。

    来看下我之前写的一个例子中的callback,其中节点有可能是integer、string、string-array、item等。

     1 const char *whitespace_cb(mxml_node_t *node, int where)
     2 {
     3 //    printf("whitespace_cb, node name: %s, node type: %d, where: %d
    ", mxmlGetElement(node), node->type, where);
     4     const char *name;
     5  
     6     name = mxmlGetElement(node);
     7  
     8     if (name == NULL) { // it isn't an element
     9         return NULL;
    10     }
    11  
    12     if (!need_update_whitespace(node)) {
    13         return NULL;
    14     }
    15  
    16     if (!strcmp(XML_NODE_ROOT_NAME, name)) { // config
    17         if (where == MXML_WS_BEFORE_OPEN) {
    18             return XML_SEPERATE_ROOT;
    19         }
    20     } else if (!strcmp(XML_NODE_INTEGER_NAME, name) //integer
    21             || !strcmp(XML_NODE_STRING_NAME, name)) { //string
    22         if (where == MXML_WS_AFTER_CLOSE) {
    23             return XML_SEPERATE_ROOT;
    24         } else if (where == MXML_WS_BEFORE_OPEN) {
    25             return XML_SEPERATE_COMMON;
    26         }
    27     } else if (!strcmp(XML_NODE_STRING_ARRAY_NAME, name)) { //string-array
    28         if (where == MXML_WS_AFTER_CLOSE) {
    29             return XML_SEPERATE_ROOT;
    30         } else if (where == MXML_WS_BEFORE_OPEN || where == MXML_WS_BEFORE_CLOSE) {
    31             return XML_SEPERATE_COMMON;
    32         }
    33     } else if (!strcmp(XML_NODE_ARRAY_ITEM_NAME, name)) { // item
    34         if (where == MXML_WS_BEFORE_OPEN) {
    35             return XML_SEPERATE_ITEM;
    36         }
    37     }
    38  
    39     return NULL;
    40 }

    其他的都ok的,返回的是空格、回车、制表符等,但在这之前有个函数need_update_whitespace 来确定该节点格式是否需要更新。

    7、加载xml

    从创建xml一直到保存xml 的流程都梳理完了,只差 load 已有的xml了。

    同样的,这里只分析mxmlLoadFile,来看下函数原型:

    mxml_node_t *mxmlLoadFile (
        mxml_node_t *top,
        FILE *fp,
        mxml_load_cb_t cb
    );

    同save,load的时候也有callback,这个load_cb主要是在load xml 到链表时需要确定element 下子节点的type。

    还是save 中同样的节点:

    <string name="string1">hehe1</string>

    load xml的时候可以通过element 的name 为string,确定子节点hehe1 的类型为MXML_TEXT或是MXML_OPAQUE。这里的type 最好是跟add 的时候统一,再次建议使用OPAQUE 的类型进行操作。

    所以,如果使用OPAQUE 的类型进行load xml,那么mxml 中提供了这样的callback:MXML_OPAQUE_CALLBACK

    mxml 也提供了其他类型的callback,MXML_INTEGER_CALLBACK、MXML_REAL_CALLBACK、MXML_TEXT_CALLBACK、MXML_IGNORE_CALLBACK、MXML_NO_CALLBACK

    注意:

    load 的时候如果用OPAQUE格式,会将之前save 时候的空格、tab(哪怕这些是在element之前)认为也 是OPAQUE格式的节点。

    8、特殊接口

    mxml 中会给字符串指定wrap 的最大值,如果到了mxml会自动换行。

    设定为0 则关闭wrap 属性。

  • 相关阅读:
    HTML笔记
    Android自定义View 自定义组合控件
    CSS 笔记
    HTML 4.01 快速参考
    MSP430单片机之中断服务
    MSP430单片机之RTC实时时钟
    Centos7.4内核符号地址查找函数的BUG
    珍惜世上的五个人
    实习
    毕业后的五年拉开大家差距的原因在哪里
  • 原文地址:https://www.cnblogs.com/LiuYanYGZ/p/14216114.html
Copyright © 2011-2022 走看看