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