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 19static 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 48static 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 61typedef struct t2_cred { 62 const char *key1; 63 const char *key2; 64} t2_cred_t; 65 66static 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 75typedef 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 86static 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 105typedef struct t2_configs { 106 lws_dll2_owner_t configs; 107} t2_configs_t; 108 109static 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 126static 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 133static int 134t2_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 154static int 155t2_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 166int 167test2(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 232bail: 233 lwsac_free(&a.ac); 234 235 return bad; 236} 237