• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * lws-api-test-lws_struct-json
3  *
4  * Written in 2010-2020 by Andy Green <andy@warmcat.com>
5  *
6  * This file is made available under the Creative Commons CC0 1.0
7  * Universal Public Domain Dedication.
8  *
9  * lws_struct apis are used to serialize and deserialize your C structs and
10  * linked-lists in a standardized way that's very modest on memory but
11  * convenient and easy to maintain.
12  *
13  * This second test file shows a worked example for how to express a schema
14  * and both consume JSON -> struct and struct -> JSON for it.
15  */
16 
17 #include <libwebsockets.h>
18 
19 static const char * const test2_json =
20 "{"
21 	"\"config\":["
22 		"{"
23 			"\"id1\":"		"null,"
24 			"\"creds\":{"
25 				"\"key1\":"	"\"\\\"xxxxxxxxx\\\"\","
26 				"\"key2\":"	"null"
27 			"},"
28 			"\"frequency\":"	"0,"
29 			"\"arg1\":"		"\"val1\","
30 			"\"arg2\":"		"0,"
31 			"\"priority\":"		"1,"
32 			"\"ssid\":"		"\"\\\"nw2\\\"\""
33 		"}, {"
34 			"\"id2\":"		"null,"
35 			"\"creds\": {"
36 				"\"key1\":"	"\"\\\"xxxxxxxxxxxxx\\\"\","
37 				"\"key2\":"	"null"
38 			"},"
39 			"\"frequency\":"	"11,"
40 			"\"arg1\":"		"\"val2\","
41 			"\"arg2\":"		"1420887242594,"
42 			"\"priority\":"		"3,"
43 			"\"ssid\":"		"\"\\\"nw1\\\"\""
44 		"}"
45 	"]"
46 "}";
47 
48 static const char * const test2_json_expected =
49 	"{\"config\":[{\"creds\":{\"key1\":\"\\u0022xxxxxxxxx\\u0022\"},"
50 	 "\"arg1\":\"val1\",\"ssid\":\"\\u0022nw2\\u0022\","
51 	 "\"frequency\":0,\"arg2\":0,\"priority\":1},"
52 	 "{\"creds\":{\"key1\":\"\\u0022xxxxxxxxxxxxx\\u0022\"},"
53 	 "\"arg1\":\"val2\",\"ssid\":\"\\u0022nw1\\u0022\","
54 	 "\"frequency\":11,\"arg2\":1420887242594,\"priority\":3}]}"
55 ;
56 
57 /*
58  * level 3: Credentials object
59  */
60 
61 typedef struct t2_cred {
62 	const char				*key1;
63 	const char				*key2;
64 } t2_cred_t;
65 
66 static const lws_struct_map_t lsm_t2_cred[] = {
67 	LSM_STRING_PTR	(t2_cred_t, key1, "key1"),
68 	LSM_STRING_PTR	(t2_cred_t, key2, "key2"),
69 };
70 
71 /*
72  * level 2: Configuration object, containing a child credentials object
73  */
74 
75 typedef struct t2_config {
76 	lws_dll2_t				list;
77 	t2_cred_t 				*creds;
78 	const char				*id1;
79 	const char				*arg1;
80 	const char				*ssid;
81 	unsigned int				frequency;
82 	unsigned long long			arg2;
83 	unsigned int				priority;
84 } t2_config_t;
85 
86 static const lws_struct_map_t lsm_t2_config[] = {
87 	LSM_CHILD_PTR	(t2_config_t,
88 			 creds,			/* the child pointer member */
89 			 t2_cred_t,		/* the child type */
90 			 NULL, lsm_t2_cred,	/* map object for item type */
91 			 "creds"),		/* outer json object name */
92 	LSM_STRING_PTR	(t2_config_t, id1, 	 "id1"),
93 	LSM_STRING_PTR	(t2_config_t, arg1,	 "arg1"),
94 	LSM_STRING_PTR	(t2_config_t, ssid,	 "ssid"),
95 
96 	LSM_UNSIGNED	(t2_config_t, frequency, "frequency"),
97 	LSM_UNSIGNED	(t2_config_t, arg2,	 "arg2"),
98 	LSM_UNSIGNED	(t2_config_t, priority,	 "priority"),
99 };
100 
101 /*
102  * level 1: list-of-configurations object
103  */
104 
105 typedef struct t2_configs {
106 	lws_dll2_owner_t 			configs;
107 } t2_configs_t;
108 
109 static const lws_struct_map_t lsm_t2_configs[] = {
110 	LSM_LIST	(t2_configs_t, configs, /* the list owner type/member */
111 			 t2_config_t,  list,	/* the list item type/member */
112 			 NULL, lsm_t2_config,	/* map object for item type */
113 			 "config"),		/* outer json object name */
114 };
115 
116 /*
117  * For parsing, this lists the kind of object we expect to parse so the struct
118  * can be allocated polymorphically.
119  *
120  * Lws uses an explicit "schema" member so the type is known unambiguously.  If
121  * in the incoming JSON the first member is not "schema", it will scan the
122  * maps listed here and instantiate the first object that has a member of that
123  * name.
124  */
125 
126 static const lws_struct_map_t lsm_schema[] = {
127 	LSM_SCHEMA	(t2_configs_t, NULL, lsm_t2_configs, "t2"),
128 	/* other schemata that might need parsing... */
129 };
130 
131 
132 
133 static int
t2_config_dump(struct lws_dll2 * d,void * user)134 t2_config_dump(struct lws_dll2 *d, void *user)
135 {
136 #if !defined(LWS_WITH_NO_LOGS)
137 	t2_config_t *c = lws_container_of(d, t2_config_t, list);
138 
139 	lwsl_notice("%s:   id1 '%s'\n", __func__, c->id1);
140 	lwsl_notice("%s:   arg1 '%s'\n", __func__, c->arg1);
141 	lwsl_notice("%s:   ssid '%s'\n", __func__, c->ssid);
142 
143 	lwsl_notice("%s:   freq %d\n", __func__, c->frequency);
144 	lwsl_notice("%s:   arg2 %llu\n", __func__, c->arg2);
145 	lwsl_notice("%s:   priority %d\n", __func__, c->priority);
146 
147 	lwsl_notice("%s:      key1: %s, key2: %s\n", __func__,
148 			     c->creds->key1, c->creds->key2);
149 #endif
150 
151 	return 0;
152 }
153 
154 static int
t2_configs_dump(t2_configs_t * t2cs)155 t2_configs_dump(t2_configs_t *t2cs)
156 {
157 	lwsl_notice("%s: number of configs: %d\n", __func__,
158 		    t2cs->configs.count);
159 
160 	lws_dll2_foreach_safe(&t2cs->configs, NULL, t2_config_dump);
161 
162 	return 0;
163 }
164 
165 
166 int
test2(void)167 test2(void)
168 {
169 	lws_struct_serialize_t *ser;
170 	struct lejp_ctx ctx;
171 	lws_struct_args_t a;
172 	t2_configs_t *top;
173 	uint8_t buf[4096];
174 	size_t written;
175 	int n, bad = 1;
176 
177 	lwsl_notice("%s: start \n", __func__);
178 
179 	memset(&a, 0, sizeof(a));
180 	a.map_st[0] = lsm_schema;
181 	a.map_entries_st[0] = LWS_ARRAY_SIZE(lsm_schema);
182 	a.ac_block_size = 512;
183 	lws_struct_json_init_parse(&ctx, NULL, &a);
184 
185 	n = lejp_parse(&ctx, (uint8_t *)test2_json, (int)strlen(test2_json));
186 	lwsl_notice("%s: lejp_parse %d\n", __func__, n);
187 	if (n < 0) {
188 		lwsl_err("%s: test2 JSON decode failed '%s'\n",
189 				__func__, lejp_error_to_string(n));
190 		goto bail;
191 	}
192 	lwsac_info(a.ac);
193 
194 	top = (t2_configs_t *)a.dest; /* the top level object */
195 
196 	if (!top) {
197 		lwsl_err("%s: no top level object\n", __func__);
198 		goto bail;
199 	}
200 	t2_configs_dump(top);
201 
202 	/* 2. Let's reserialize the top level object and see what comes out */
203 
204 	ser = lws_struct_json_serialize_create(&lsm_schema[0], 1,
205 					       LSSERJ_FLAG_OMIT_SCHEMA, top);
206 	if (!ser) {
207 		lwsl_err("%s: unable to init serialization\n", __func__);
208 		goto bail;
209 	}
210 
211 	do {
212 		n = (int)lws_struct_json_serialize(ser, buf, sizeof(buf), &written);
213 		switch (n) {
214 		case LSJS_RESULT_FINISH:
215 			puts((const char *)buf);
216 			break;
217 		case LSJS_RESULT_CONTINUE:
218 		case LSJS_RESULT_ERROR:
219 			goto bail;
220 		}
221 	} while (n == LSJS_RESULT_CONTINUE);
222 
223 	if (strcmp(test2_json_expected, (char *)buf)) {
224 		lwsl_err("%s: expected %s\n", __func__, test2_json_expected);
225 		goto bail;
226 	}
227 
228 	lws_struct_json_serialize_destroy(&ser);
229 
230 	bad = 0;
231 
232 bail:
233 	lwsac_free(&a.ac);
234 
235 	return bad;
236 }
237