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