mxml 详解

前言:

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

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

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

下载:

Releases · michaelrsweet/mxml · GitHub

解压后请仔细阅读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等。

const char *whitespace_cb(mxml_node_t *node, int where)
{
//    printf("whitespace_cb, node name: %s, node type: %d, where: %d\n", mxmlGetElement(node), node->type, where);
    const char *name;

    name = mxmlGetElement(node);

    if (name == NULL) { // it isn't an element
        return NULL;
    }

    if (!need_update_whitespace(node)) {
        return NULL;
    }

    if (!strcmp(XML_NODE_ROOT_NAME, name)) { // config
        if (where == MXML_WS_BEFORE_OPEN) {
            return XML_SEPERATE_ROOT;
        }
    } else if (!strcmp(XML_NODE_INTEGER_NAME, name) //integer
            || !strcmp(XML_NODE_STRING_NAME, name)) { //string
        if (where == MXML_WS_AFTER_CLOSE) {
            return XML_SEPERATE_ROOT;
        } else if (where == MXML_WS_BEFORE_OPEN) {
            return XML_SEPERATE_COMMON;
        }
    } else if (!strcmp(XML_NODE_STRING_ARRAY_NAME, name)) { //string-array
        if (where == MXML_WS_AFTER_CLOSE) {
            return XML_SEPERATE_ROOT;
        } else if (where == MXML_WS_BEFORE_OPEN || where == MXML_WS_BEFORE_CLOSE) {
            return XML_SEPERATE_COMMON;
        }
    } else if (!strcmp(XML_NODE_ARRAY_ITEM_NAME, name)) { // item
        if (where == MXML_WS_BEFORE_OPEN) {
            return XML_SEPERATE_ITEM;
        }
    }

    return NULL;
}

其他的都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 属性。

  • 5
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

私房菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值