| /* |
| * lws-api-test-lws_struct-json |
| * |
| * Written in 2010-2020 by Andy Green <[email protected]> |
| * |
| * This file is made available under the Creative Commons CC0 1.0 |
| * Universal Public Domain Dedication. |
| * |
| * lws_struct apis are used to serialize and deserialize your C structs and |
| * linked-lists in a standardized way that's very modest on memory but |
| * convenient and easy to maintain. |
| * |
| * This second test file shows a worked example for how to express a schema |
| * and both consume JSON -> struct and struct -> JSON for it. |
| */ |
| |
| #include <libwebsockets.h> |
| |
| static const char * const test2_json = |
| "{" |
| "\"config\":[" |
| "{" |
| "\"id1\":" "null," |
| "\"creds\":{" |
| "\"key1\":" "\"\\\"xxxxxxxxx\\\"\"," |
| "\"key2\":" "null" |
| "}," |
| "\"frequency\":" "0," |
| "\"arg1\":" "\"val1\"," |
| "\"arg2\":" "0," |
| "\"priority\":" "1," |
| "\"ssid\":" "\"\\\"nw2\\\"\"" |
| "}, {" |
| "\"id2\":" "null," |
| "\"creds\": {" |
| "\"key1\":" "\"\\\"xxxxxxxxxxxxx\\\"\"," |
| "\"key2\":" "null" |
| "}," |
| "\"frequency\":" "11," |
| "\"arg1\":" "\"val2\"," |
| "\"arg2\":" "1420887242594," |
| "\"priority\":" "3," |
| "\"ssid\":" "\"\\\"nw1\\\"\"" |
| "}" |
| "]" |
| "}"; |
| |
| static const char * const test2_json_expected = |
| "{\"config\":[{\"creds\":{\"key1\":\"\\u0022xxxxxxxxx\\u0022\"}," |
| "\"arg1\":\"val1\",\"ssid\":\"\\u0022nw2\\u0022\"," |
| "\"frequency\":0,\"arg2\":0,\"priority\":1}," |
| "{\"creds\":{\"key1\":\"\\u0022xxxxxxxxxxxxx\\u0022\"}," |
| "\"arg1\":\"val2\",\"ssid\":\"\\u0022nw1\\u0022\"," |
| "\"frequency\":11,\"arg2\":1420887242594,\"priority\":3}]}" |
| ; |
| |
| /* |
| * level 3: Credentials object |
| */ |
| |
| typedef struct t2_cred { |
| const char *key1; |
| const char *key2; |
| } t2_cred_t; |
| |
| static const lws_struct_map_t lsm_t2_cred[] = { |
| LSM_STRING_PTR (t2_cred_t, key1, "key1"), |
| LSM_STRING_PTR (t2_cred_t, key2, "key2"), |
| }; |
| |
| /* |
| * level 2: Configuration object, containing a child credentials object |
| */ |
| |
| typedef struct t2_config { |
| lws_dll2_t list; |
| t2_cred_t *creds; |
| const char *id1; |
| const char *arg1; |
| const char *ssid; |
| unsigned int frequency; |
| unsigned long long arg2; |
| unsigned int priority; |
| } t2_config_t; |
| |
| static const lws_struct_map_t lsm_t2_config[] = { |
| LSM_CHILD_PTR (t2_config_t, |
| creds, /* the child pointer member */ |
| t2_cred_t, /* the child type */ |
| NULL, lsm_t2_cred, /* map object for item type */ |
| "creds"), /* outer json object name */ |
| LSM_STRING_PTR (t2_config_t, id1, "id1"), |
| LSM_STRING_PTR (t2_config_t, arg1, "arg1"), |
| LSM_STRING_PTR (t2_config_t, ssid, "ssid"), |
| |
| LSM_UNSIGNED (t2_config_t, frequency, "frequency"), |
| LSM_UNSIGNED (t2_config_t, arg2, "arg2"), |
| LSM_UNSIGNED (t2_config_t, priority, "priority"), |
| }; |
| |
| /* |
| * level 1: list-of-configurations object |
| */ |
| |
| typedef struct t2_configs { |
| lws_dll2_owner_t configs; |
| } t2_configs_t; |
| |
| static const lws_struct_map_t lsm_t2_configs[] = { |
| LSM_LIST (t2_configs_t, configs, /* the list owner type/member */ |
| t2_config_t, list, /* the list item type/member */ |
| NULL, lsm_t2_config, /* map object for item type */ |
| "config"), /* outer json object name */ |
| }; |
| |
| /* |
| * For parsing, this lists the kind of object we expect to parse so the struct |
| * can be allocated polymorphically. |
| * |
| * Lws uses an explicit "schema" member so the type is known unambiguously. If |
| * in the incoming JSON the first member is not "schema", it will scan the |
| * maps listed here and instantiate the first object that has a member of that |
| * name. |
| */ |
| |
| static const lws_struct_map_t lsm_schema[] = { |
| LSM_SCHEMA (t2_configs_t, NULL, lsm_t2_configs, "t2"), |
| /* other schemata that might need parsing... */ |
| }; |
| |
| |
| |
| static int |
| t2_config_dump(struct lws_dll2 *d, void *user) |
| { |
| #if !defined(LWS_WITH_NO_LOGS) |
| t2_config_t *c = lws_container_of(d, t2_config_t, list); |
| |
| lwsl_notice("%s: id1 '%s'\n", __func__, c->id1); |
| lwsl_notice("%s: arg1 '%s'\n", __func__, c->arg1); |
| lwsl_notice("%s: ssid '%s'\n", __func__, c->ssid); |
| |
| lwsl_notice("%s: freq %d\n", __func__, c->frequency); |
| lwsl_notice("%s: arg2 %llu\n", __func__, c->arg2); |
| lwsl_notice("%s: priority %d\n", __func__, c->priority); |
| |
| lwsl_notice("%s: key1: %s, key2: %s\n", __func__, |
| c->creds->key1, c->creds->key2); |
| #endif |
| |
| return 0; |
| } |
| |
| static int |
| t2_configs_dump(t2_configs_t *t2cs) |
| { |
| lwsl_notice("%s: number of configs: %d\n", __func__, |
| t2cs->configs.count); |
| |
| lws_dll2_foreach_safe(&t2cs->configs, NULL, t2_config_dump); |
| |
| return 0; |
| } |
| |
| |
| int |
| test2(void) |
| { |
| lws_struct_serialize_t *ser; |
| struct lejp_ctx ctx; |
| lws_struct_args_t a; |
| t2_configs_t *top; |
| uint8_t buf[4096]; |
| size_t written; |
| int n, bad = 1; |
| |
| lwsl_notice("%s: start \n", __func__); |
| |
| memset(&a, 0, sizeof(a)); |
| a.map_st[0] = lsm_schema; |
| a.map_entries_st[0] = LWS_ARRAY_SIZE(lsm_schema); |
| a.ac_block_size = 512; |
| lws_struct_json_init_parse(&ctx, NULL, &a); |
| |
| n = lejp_parse(&ctx, (uint8_t *)test2_json, (int)strlen(test2_json)); |
| lwsl_notice("%s: lejp_parse %d\n", __func__, n); |
| if (n < 0) { |
| lwsl_err("%s: test2 JSON decode failed '%s'\n", |
| __func__, lejp_error_to_string(n)); |
| goto bail; |
| } |
| lwsac_info(a.ac); |
| |
| top = (t2_configs_t *)a.dest; /* the top level object */ |
| |
| if (!top) { |
| lwsl_err("%s: no top level object\n", __func__); |
| goto bail; |
| } |
| t2_configs_dump(top); |
| |
| /* 2. Let's reserialize the top level object and see what comes out */ |
| |
| ser = lws_struct_json_serialize_create(&lsm_schema[0], 1, |
| LSSERJ_FLAG_OMIT_SCHEMA, top); |
| if (!ser) { |
| lwsl_err("%s: unable to init serialization\n", __func__); |
| goto bail; |
| } |
| |
| do { |
| n = (int)lws_struct_json_serialize(ser, buf, sizeof(buf), &written); |
| switch (n) { |
| case LSJS_RESULT_FINISH: |
| puts((const char *)buf); |
| break; |
| case LSJS_RESULT_CONTINUE: |
| case LSJS_RESULT_ERROR: |
| goto bail; |
| } |
| } while (n == LSJS_RESULT_CONTINUE); |
| |
| if (strcmp(test2_json_expected, (char *)buf)) { |
| lwsl_err("%s: expected %s\n", __func__, test2_json_expected); |
| goto bail; |
| } |
| |
| lws_struct_json_serialize_destroy(&ser); |
| |
| bad = 0; |
| |
| bail: |
| lwsac_free(&a.ac); |
| |
| return bad; |
| } |