Skip to content

json、xml、plist的解析和生成

ruki edited this page Aug 7, 2014 · 1 revision

object库,提供运行时对象支持,通过引用计数维护所有对象。只要是继承自tb_object_t的对象类型都是可以经过扩展实现序列化和反序列化。

库内部也已经提供了常用的对象类型:

tb_object_data_t: 数据对象类型
tb_object_date_t: 日期对象类型
tb_object_null_t: 空值对象类型
tb_object_array_t: 数组对象类型
tb_object_string_t: 字符串对象类型
tb_object_number_t: 数值对象类型,包括浮点、整型
tb_object_boolean_t: 布尔对象类型
tb_object_dictionary_t: 字典对象类型

可以看到,基本上这些对象已经可以进行常用数据维护了,和apple的CoreFoundation内部的常用对象很类似,而且可以很方便的扩展其他自定义类型的序列化,这部分等以后再细讲吧。。。

现在先简单看下如何快速解析一个json文件到内存:

// 从文件读取json数据到内存,json_root为整个根对象
tb_object_ref_t json_root = tb_object_read_from_url("/home/file/json.txt");

// 从http读取json数据到内存,json_root为整个根对象
// tb_object_ref_t json_root = tb_object_read_from_url("http://localhost/file/json.txt");

// 从数据读取json数据到内存,json_root为整个根对象
// tb_object_ref_t json_root = tb_object_read_from_data(data, size);

// 从stream读取json数据到内存,json_root为整个根对象
// tb_object_ref_t json_root = tb_object_read(stream);

if (json_root)
{
    // 将json_root的所有数据格式打印到终端,一般调试使用
    tb_object_dump(json_root);

    // 释放json_root对象,object跟CoreFoundation一样是有引用计数的
    // 这里json_root没有被其他引用,所以会被立马释放掉
    tb_object_exit(json_root);
}

怎么样简单吧,如果要解析plist文件, 也是类似,只需换成plist 文件的url就行了 库内部回去自动检测文件格式,进行相应地解析工作,上层都是通用的object对象树 并且可以支持xplist(xml格式)、bplist(二进制格式)两种格式。

tb_object_ref_t plist_root = tb_object_read_from_url("/home/file/file.plist");

对于序列化到文件,也很简单:

// 序列化json object到文件, size 为实际序列化的字节数,如果失败,返回:-1
// 默认格式存储,通过 tab 和 换行 进行了格式化,方便查看
tb_long_t size = tb_object_writ_to_url(object, "/home/file/json.txt", TB_OBJECT_FORMAT_JSON);

// 序列化json object到文件, size 为实际序列化的字节数,如果失败,返回:-1
// 并且压缩存储,去掉冗余的空白字符
tb_long_t size = tb_object_writ_to_url(object, "/home/file/json.txt", TB_OBJECT_FORMAT_JSON | TB_OBJECT_FORMAT_DEFLATE);

// 序列化json object到数据buffer,size 为实际序列化的字节数,如果失败,返回:-1
tb_byte_t data[8192] = {0};
tb_long_t size = tb_object_writ_to_data(object, data, 8192, TB_OBJECT_FORMAT_JSON);

其他格式类似,如下是可以支持的序列化格式:

  • TB_OBJECT_FORMAT_BIN:tbox内部二进制序列化格式,最为节省空间,并且对字符串做了些简单的加密,可以扩展自定义的数据类型
  • TB_OBJECT_FORMAT_BPLIST:apple的二进制plist格式,内部字符串为明文,并且空间利用率不是很高
  • TB_OBJECT_FORMAT_XPLIST:apple的xml文本plist格式
  • TB_OBJECT_FORMAT_XML:tbox内部的xml文本序列化格式,可以扩展自定义的数据类型
  • TB_OBJECT_FORMAT_JSON:json序列化格式

object对象的字段解析有两种模式,一种是一层层迭代遍历,一种是直接定位到指定字段 迭代遍历,只有array和dictionay需要,他们同样是支持tbox容器库的迭代器模式的,例如:

// 遍历array
tb_for_all (tb_object_ref_t, item, tb_object_array_itor(array))
{
    if (item)
    {
        // ...
    }
}

// 遍历dictionary
tb_for_all (tb_object_dictionary_item_t*, item, tb_object_dictionary_itor(dictionary))
{
    // 获取dictionary的每一个键值对
    if (item)
    {
        // 键名字符串
        tb_char_t const*    key = item->key;

        // 值对象, 可以继续迭代下层或直接取值
        tb_object_ref_t     val = item->val;

        // ...
    }
}

如果要直接定位某个字段,可以使用tbox的seek模式,支持自定义路径格式:

/* 例如对于这个xml的数据解析

    <dict>
        <key>string</key>
        <string>hello wolrd!</string>

        <key>com.xxx.xxx</key>
        <string>hello wolrd!</string>

        <key>integer</key>
        <number>31415926</number>

        <key>array</key>
        <array>
            <string>hello wolrd!</string>
            <number>31415926</number>
            <number>3.1415926</number>
            <false/>
            <true/>
            <dict>
                <key>string</key>
                <string>hello wolrd!</string>
            </dict>
        </array>
    </dict>

 * 其对应的字段路径:
 * 1. ".string"             : hello wolrd!
 * 2. ".array[1]"           : 31415926
 * 3. ".array[5].string"    : hello wolrd!
 * 4. ".com\\.xxx\\.xxx"    : hello wolrd!
 */

/* seek到指定路径:.array[5].string ,进行解析字段
 *
 * 这里传TB_OBJECT_TYPE_STRING作为最后一个参数,是为了内部做一次类型检测
 * 如果这个字段确实string类型的,才会返回对象,否则返回null,这样上层解析代码
 * 看上去更加的简洁,不需要每次解析一个字段,都要外面做一下检测类型
 * 
 * 如果传TB_OBJECT_TYPE_NONE进去,那么不管是不是string对象,都会返回成功
 * 这个时候上层如果不做类型检测,只是去字符串,库内部会有断言,但是不影响程序逻辑
 * 仅仅是提示下,你现在的处理类型有误。
 */
tb_object_ref_t object = tb_object_seek(object, ".array[5].string", TB_OBJECT_TYPE_STRING);
if (object)
{
    tb_trace_d("%s", tb_object_string_cstr(object));
}

其他字段的解析:

/* 解析string类型字段, 取值前,先做类型判断是最安全的方式
 * 虽然直接转换也是安全的,类型不对内部直接会返回tb_null
 * 但是为了养成良好的编程习惯,在调试模式下,库内部会有检测断言提示类型不匹配
 */
if (tb_object_type(object) == TB_OBJECT_TYPE_STRING)
{
    tb_char_t const* string = tb_object_string_cstr(object);
}

// 解析number类型字段
if (tb_object_type(object) == TB_OBJECT_TYPE_NUMBER)
{
    // 获取整型值,如果不是会自动强转,有可能丢失精度
    tb_uint32_t value = tb_object_number_uint32(object);

    // 获取浮点值,如果不是会自动强转
//    tb_float_t value = tb_object_number_float(object);

    // 获取双精度浮点值,如果不是会自动强转
//    tb_double_t value = tb_object_number_double(object);
}

// 解析boolean类型字段
if (tb_object_type(object) == TB_OBJECT_TYPE_BOOLEAN)
{
    // 获取bool值
    tb_bool_t value = tb_object_boolean_bool(object);
}

// 解析data类型字段
if (tb_object_type(object) == TB_OBJECT_TYPE_DATA)
{
    // 获取数据指针
    tb_byte_t* data = tb_object_data_getp(object);

    // 获取数据大小
    tb_size_t size = tb_object_data_size(object);
}

// 解析date类型字段
if (tb_object_type(object) == TB_OBJECT_TYPE_DATE)
{
    // 获取时间戳
    tb_time_t time = tb_object_date_time(object);
}

// 解析array类型字段
if (tb_object_type(object) == TB_OBJECT_TYPE_ARRAY)
{
    // 获取成员数量
    tb_size_t count = tb_object_array_size(object);
    tb_size_t i = 0;
    for (i = 0; i < count; i++)
    {
        tb_object_ref_t item = tb_object_array_item(object, i);
    }
}

// 解析dictionary类型字段
if (tb_object_type(object) == TB_OBJECT_TYPE_DICTIONARY)
{
    // 获取对象键值
    tb_object_ref_t value = tb_object_dictionary_val(object, "key_name");
}
Clone this wiki locally