lept_json的学习之stringify

本文详细介绍了JSON解析库lept_json中stringify功能的实现,包括各种类型如Null、Bool、Number、String、Array和Object的字符串化过程。通过代码分析展示了如何将JSON值转换为整洁的字符串,并提供了单元测试用例以验证转换的正确性。此外,还讨论了代码的美化和优化策略。
摘要由CSDN通过智能技术生成

lept_json的学习之stringify

这一节我们讲解所有类型的生成Stringify。
其实stringify的意思本不是生成,而是字符串化,但为了方便理解,我直接称之为生成(generate)也是可以的。


Stringify

生成器它生成得好,可以生成出很整洁的字符串,方便我们阅读,不过在这里我们只进行字符生成,不进行排版操作,所以最后生成出来的就是一长串的挤在一行里的字符串。
在这里我们也能想到,生成器的操作就是解析器反过来,所以它大致的架构也是一个循环里套一个switch语句。
这里为了代码的美观我们也是像parse和parse_value那样将stringify和stringify_value区分开来。


#ifndef LEPT_PARSE_STRINGIFY_INIT_SIZE
#define LEPT_PARSE_STRINGIFY_INIT_SIZE 256
#endif

char* lept_stringify(const lept_value& v, size_t length) {
	lept_context c;
	assert(&v != NULL);
	c.stack = (char*)malloc(c.size = LEPT_PARSE_STRINGIFY_INIT_SIZE);
	c.top = 0;
	lept_stringify_value(c, v);
	if (length)
		length = c.top;
	PUTC(c, '\0');
	return c.stack;
}

这里的stringify函数的主要操作是创建一个context并为其分配一定初识内存(初始化)以及最后的在字符串尾放一个空字符

Null/Bool Stringify

case LEPT_NULL:   PUTS(c, "null", 4); break;
case LEPT_FALSE:  PUTS(c, "false", 5); break;
case LEPT_TRUE:   PUTS(c, "true", 4);break;

简单类型,不多赘述,

Number Stringify

case LEPT_NUMBER: c.top -= 32 - (sprintf((char*)lept_context_push(c, 32), "%.17g", v.u.m_num)); 
break;

这里的操作其实不是很美观,在对性能的极致追求上,将一些理解性的临时变量给省略了。如果换成以下的代码也许会更好理解:

  case LEPT_NUMBER:
            {
            //向context中压入32的内存(32为数字最大占用字符串大小)
                char* buffer = lept_context_push(c, 32);
                //在其中写入数据(number->string),并记录占用的字符数
                int length = sprintf(buffer, "%.17g", v->u.n);
                //栈顶移动
                c->top -= 32 - length;
            }
            
            break;

String Stringify

String的生成因为涉及到转义序列,而转义序列会用到switch,为了代码的美观,我们将string_stringify封装以下。
在stringify_value中代码如下

case LEPT_STRING: lept_stringify_string(c, v.u.m_str.s, v.u.m_str.len);
break;

string_stringify函数:

static void lept_stringify_string(lept_context& c, const char* s, size_t len) {
   //十六进制编码转换的常量
	static const char hex_digits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
	size_t i, size;
	char* head, * p;
	assert(s != NULL);
	p = head = (char*)lept_context_push(c, size = len * 6 + 2); /* "\u00xx..." */
	*p++ = '"';
	for (i = 0; i < len; i++) {
		unsigned char ch = (unsigned char)s[i];
		switch (ch) {
		case '\"': *p++ = '\\'; *p++ = '\"'; break;
		case '\\': *p++ = '\\'; *p++ = '\\'; break;
		case '\b': *p++ = '\\'; *p++ = 'b';  break;
		case '\f': *p++ = '\\'; *p++ = 'f';  break;
		case '\n': *p++ = '\\'; *p++ = 'n';  break;
		case '\r': *p++ = '\\'; *p++ = 'r';  break;
		case '\t': *p++ = '\\'; *p++ = 't';  break;
		default:
		//十六进制编码转换
			if (ch < 0x20) {
				*p++ = '\\'; *p++ = 'u'; *p++ = '0'; *p++ = '0';
				*p++ = hex_digits[ch >> 4];
				*p++ = hex_digits[ch & 15];
			}
			else
				*p++ = s[i];
		}
	}
	*p++ = '"';
	c.top -= size - (p - head);
}

Array Stringify&Object Stringify

因为二者十分相似,都递归了stringify_value,我就放一起了,object只是多一个key值的stringify

case LEPT_ARRAY:
		PUTC(c, '[');
		for (i = 0; i < v.u.m_arr.size; i++) {
			if (i > 0)
				PUTC(c, ',');
			lept_stringify_value(c, v.u.m_arr.e[i]);
		}
		PUTC(c, ']');
		break;
		
	case LEPT_OBJECT:
		PUTC(c, '{');
		for (i = 0; i < v.u.m_obj.size; i++) {
			if (i > 0)
				PUTC(c, ',');
			lept_stringify_string(c, v.u.m_obj.m[i].k, v.u.m_obj.m[i].klen);
			PUTC(c, ':');
			lept_stringify_value(c, v.u.m_obj.m[i].v);
		}
		PUTC(c, '}');
		break;

如果要进行美化的话,可以使用局部静态变量。
以下是我的美化操作:

static int num = 0;//在stringify的switch外部引入一个局部静态变量
/* ..... */
case LEPT_OBJECT:
		PUTC(c, '{');
		for (i = 0; i < v.u.m_obj.size; i++) {
			PUTC(c, '\n');            /* 美化操作 */
			for(int x = 0;x<++num;x++)/* 美化操作 */
				PUTC(c, '\t');
			lept_stringify_string(c, v.u.m_obj.m[i].k, v.u.m_obj.m[i].klen);
			PUTC(c, ':');
			lept_stringify_value(c, v.u.m_obj.m[i].v);
			if (i > 0)
				PUTC(c, ',');
		}
		PUTC(c, '\n');                 /* 美化操作 */
		for (int x = 0; x < --num; x++)/* 美化操作 */
			PUTC(c, '\t');
		PUTC(c, '}');
		break;
/* ..... */

这里主要对object进行了美化,也就是说其它的变量比如array依然是会输出在同一行。
最后可以自行进行一些测试,因为现在能够生成字符串了,自行感受以下整洁的数据结构还是很享受的一件事。

单元测试

不过该有的单元测试还是要有的

为贯彻 TDD,先写测试:

专门定义一个生成测试宏

#define TEST_ROUNDTRIP(json)\
    do {\
        lept_value v;\
        char* json2;\
        size_t length;\
        lept_init(&v);\
        EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, json));\
        EXPECT_EQ_INT(LEPT_STRINGIFY_OK, lept_stringify(&v, &json2, &length));\
        EXPECT_EQ_STRING(json, json2, length);\
        lept_free(&v);\
        free(json2);\
    } while(0)

    static void test_stringify_number() {
        TEST_ROUNDTRIP("0");
        TEST_ROUNDTRIP("-0");
        TEST_ROUNDTRIP("1");
        TEST_ROUNDTRIP("-1");
        TEST_ROUNDTRIP("1.5");
        TEST_ROUNDTRIP("-1.5");
        TEST_ROUNDTRIP("3.25");
        TEST_ROUNDTRIP("1e+20");
        TEST_ROUNDTRIP("1.234e+20");
        TEST_ROUNDTRIP("1.234e-20");

        TEST_ROUNDTRIP("1.0000000000000002"); /* the smallest number > 1 */
        TEST_ROUNDTRIP("4.9406564584124654e-324"); /* minimum denormal */
        TEST_ROUNDTRIP("-4.9406564584124654e-324");
        TEST_ROUNDTRIP("2.2250738585072009e-308");  /* Max subnormal double */
        TEST_ROUNDTRIP("-2.2250738585072009e-308");
        TEST_ROUNDTRIP("2.2250738585072014e-308");  /* Min normal positive double */
        TEST_ROUNDTRIP("-2.2250738585072014e-308");
        TEST_ROUNDTRIP("1.7976931348623157e+308");  /* Max double */
        TEST_ROUNDTRIP("-1.7976931348623157e+308");
    }

    static void test_stringify_string() {
        TEST_ROUNDTRIP("\"\"");
        TEST_ROUNDTRIP("\"Hello\"");
        TEST_ROUNDTRIP("\"Hello\\nWorld\"");
        TEST_ROUNDTRIP("\"\\\" \\\\ / \\b \\f \\n \\r \\t\"");
        TEST_ROUNDTRIP("\"Hello\\u0000World\"");
    }

    static void test_stringify_array() {
        TEST_ROUNDTRIP("[]");
        TEST_ROUNDTRIP("[null,false,true,123,\"abc\",[1,2,3]]");
    }

    static void test_stringify_object()
     {
        TEST_ROUNDTRIP("{}");
        TEST_ROUNDTRIP("{\"n\":null,\"f\":false,\"t\":true,\"i\":123,\"s\":\"abc\",\"a\":[1,2,3],\"o\":{\"1\":1,\"2\":2,\"3\":3}}");
    }

    static void test_stringify() {
        TEST_ROUNDTRIP("null");
        TEST_ROUNDTRIP("false");
        TEST_ROUNDTRIP("true");
        test_stringify_number();
        test_stringify_string();
        test_stringify_array();
        test_stringify_object();
    }

以上就是生成器的编写。之后讲以下所有的api,那么整个json的复习(对于我自己)就结束了。之后就要进入下一个项目的学习了

我走在一条未知的道路上,我所能做的就是走下去,直到路口或者尽头


lept_json Github:https://github.com/miloyip/json-tutorial

本人流星画魂第七次在csdn上做笔记,有什么错误或者是需要改进的地方请即时提出
我只是一个对编程感兴趣的人,但懒得要死,学得又不认真,希望读者能骂就骂两句,真的太懒了

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值