美文网首页
从零开始的JSON教程学习

从零开始的JSON教程学习

作者: 王昕业 | 来源:发表于2016-11-13 17:35 被阅读0次

    此文为学习知乎上Milo的编程”专栏所载的从零开始的JSON教程所作的学习笔记,同时也是学习简书的Markdown语法的时间,因为一直想找一个能做学习笔记的网站,并且由于笔记的很多内容涉及代码块,支持Markdown语法的简书似乎是个不错的选择。同时希望自己能够坚持,持续学习。(由于是学习笔记,大量的内容来自于专栏,专栏作者为Milo Yip,特此说明)

    一、启程

    1. JSON是什么?

    JSON是一种用于数据交换的文本格式,具有类似功能的语言有XML、YAML等等,但JSON的语法最简单。一个简单的例子的JSON文本的例子:

     {
        "title": "Design Patterns",
        "subtitle": "Elements of Reusable Object-Oriented Software",
        "author": [
            "Erich Gamma",
            "Richard Helm",
            "Ralph Johnson",
            "John Vlissides"
        ],
        "year": 2009,
        "weight": 1.8,
        "hardcover": true,
        "publisher": {
            "Company": "Pearson Education",
            "Country": "India"
        },
        "website": null
    }
    

    由此可以看见,一个JSON对象的表示是用{...},JSON的数据类型有六种数据类型:

    Type Format
    null null
    booleab true or false
    number 浮点数
    string "Design Patterns"
    array [...]
    objecte {}

    可以发现一个object的数据类型可以是另一个object,这说明JSON是一种树状的结构。一个JSON库应当提供的功能包括:

    • parse:将JSON文本(即JSON字符串)解析为对象,肯定是一个树状的对象
    • stringify:将一个对象文本化,即把一个对象表示为一串JSON文本
    • access:提供方法使得程序中可以访问已经parse过的对象的数据结构

    (事实上,对于access不是特别能理解,希望在感悟下)

    2. 编译环境配置

    pass
    此处的内容之后再补充,对于编译环境的搭建CMake、Git的使用还是要抽时间本地使用下

    3.头文件与API设计
    • 头文件#include 防范:为了防止重复引用头文件而采取的一种方法,一般为每个头文件中预定义一个唯一的宏,然后判断是否这个宏已经定义过了。
    #ifndef LEPTJSON_H__
    #define LEPTJSON_H__
    /* 此处写入头文件内容,如各种声明 */
    #endif /*LEPTJSON_H__*/
    

    一般以_H__作为后缀,并且要保证唯一。

    • 六种数据类型:将truefalse算作两种类型,那么共有其中类型,定义一个enum类型。为了方便使用,用关键字typedef在处理下:
    typedef enum { 
            LEPT_NULL, 
            LEPT_FALSE,  
            LEPT_TRUE,
            LEPT_NUMBER, 
            LEPT_STRING,
            LEPT_ARRAY, 
            LEPT_OBJECT
    }  lept_type;
    

    一些作者提到的注意点:C语言没有明明空间的概念,因此需要保证标识符的唯一性,因此一般用项目名称的简写做前缀,枚举值用大写而函数和类型定义用小写

    • JSON是一个树状的数据结构,每个结点用一个结构表示,当前第一节只考虑三种类型nullfalsetrue,因此就当前来讲,这个结构只需要存储一个数据类型即可,以下是定义:
    typedef struct {
    lept_type type;
    } lept_value;
    
    • API定义:
    typedef enum {
             LEPT_PARSE_OK , 
             LEPT_PARSE_EXPECT_VALUE,
             LEPT_PARSE_INVALID_VALUE,
             LEPT_PARSE_ROOT_NOT_SINGULAR
    } lept_parse_result;      
    /*API-1,解析一个json,即parse,结果填入一个lept_value。注意const的用法是保证json不会被改动*/
    lept_parse_result lept_parse(lept_value &v, const char* json);       
    /*API-2,获取结果,即access,注意const 以及指针的使用(防止对象拷贝)*/
    lept_type lept_get_tyepe(const lept_value* v);
    
    • 此单元json只包含null,false,true三种类型,语法描述为:
    json_text = ws value ws
    ws = *(%0x20, %x0x09, %0x0A, %x0D)   ------*  表示零或多个
    value = 'null' / 'false' / 'true'    -----/ 表示其中一个
    

    前面已经提到了lept_parse的返回类型,下面表示本单元语法下分别代表的含义:

    typedef enum {
             LEPT_PARSE_OK ,         //解析成功
             LEPT_PARSE_EXPECT_VALUE,  //JSON串只有空白
             LEPT_PARSE_INVALID_VALUE,
             LEPT_PARSE_ROOT_NOT_SINGULAR //一个值、空白后还有其他值
    } lept_parse_result;    
    
    • TTD(测试驱动开发)
      是一种开发方法,主要是:
      1. 加入一个测试
      2. 运行所有测试,新的测试会失败
      3. 编写实现代码
      4. 运行所有测试,如有失败返回3
      • 重构代码,并测试,如有失败返回3
      • 返回1

    TTD是先写测试在写开发,优点是实现刚好满足测试的代码,且容易把控质量。本项目提供了一个测试框架。以下做代码学习。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include "leptjson.h"
    
    static int main_ret = 0;
    static int test_count = 0;
    static int test_pass = 0;
    //注意宏分多行写的技巧 
    //注意fprintf和printf区别:注意format为什么可以这么写:类似于char * = “%d”“%d”这样写没毛病,注意stderr
    //printf是向stdout,fprintf是向FILE*输出,stderr和stdout等都是该类型,
    //stderr时另一个输
    //出流
    #define EXPECT_EQ_BASE(equality, expect, actual, format) \
        do {\
            test_count++;\
            if (equality)\
                test_pass++;\
            else {\
                fprintf(stderr, "%s:%d: expect: " format " actual: " format "\n", __FILE__, __LINE__, expect, actual);\
                main_ret = 1;\
            }\
        } while(0)
    
    #define EXPECT_EQ_INT(expect, actual) EXPECT_EQ_BASE((expect) == (actual), expect, actual, "%d")
    
    static void test_parse_null() {
        lept_value v;
        v.type = LEPT_TRUE;
        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "null"));
        EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));
    }
    
    /* ... */
    
    static void test_parse() {
        test_parse_null();
        /* ... */
    }
    
    int main() {
        test_parse();
        printf("%d/%d (%3.2f%%) passed\n", test_pass, test_count, test_pass * 100.0 / test_count);
        return main_ret;
    }
    
    4. 课后习题

    其他的就不关注 了,有一点有意思的要注意下,当有一个函数申明为static后意味这个函数只在当前编译单元使用

    一、启程

    1. JSON是什么?

    相关文章

      网友评论

          本文标题:从零开始的JSON教程学习

          本文链接:https://www.haomeiwen.com/subject/gfucpttx.html