test2.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /*
  2. * lws-api-test-lws_struct-json
  3. *
  4. * Written in 2010-2020 by Andy Green <[email protected]>
  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. #include <libwebsockets.h>
  17. static const char * const test2_json =
  18. "{"
  19. "\"config\":["
  20. "{"
  21. "\"id1\":" "null,"
  22. "\"creds\":{"
  23. "\"key1\":" "\"\\\"xxxxxxxxx\\\"\","
  24. "\"key2\":" "null"
  25. "},"
  26. "\"frequency\":" "0,"
  27. "\"arg1\":" "\"val1\","
  28. "\"arg2\":" "0,"
  29. "\"priority\":" "1,"
  30. "\"ssid\":" "\"\\\"nw2\\\"\""
  31. "}, {"
  32. "\"id2\":" "null,"
  33. "\"creds\": {"
  34. "\"key1\":" "\"\\\"xxxxxxxxxxxxx\\\"\","
  35. "\"key2\":" "null"
  36. "},"
  37. "\"frequency\":" "11,"
  38. "\"arg1\":" "\"val2\","
  39. "\"arg2\":" "1420887242594,"
  40. "\"priority\":" "3,"
  41. "\"ssid\":" "\"\\\"nw1\\\"\""
  42. "}"
  43. "]"
  44. "}";
  45. static const char * const test2_json_expected =
  46. "{\"config\":[{\"creds\":{\"key1\":\"\\u0022xxxxxxxxx\\u0022\"},"
  47. "\"arg1\":\"val1\",\"ssid\":\"\\u0022nw2\\u0022\","
  48. "\"frequency\":0,\"arg2\":0,\"priority\":1},"
  49. "{\"creds\":{\"key1\":\"\\u0022xxxxxxxxxxxxx\\u0022\"},"
  50. "\"arg1\":\"val2\",\"ssid\":\"\\u0022nw1\\u0022\","
  51. "\"frequency\":11,\"arg2\":1420887242594,\"priority\":3}]}"
  52. ;
  53. /*
  54. * level 3: Credentials object
  55. */
  56. typedef struct t2_cred {
  57. const char *key1;
  58. const char *key2;
  59. } t2_cred_t;
  60. static const lws_struct_map_t lsm_t2_cred[] = {
  61. LSM_STRING_PTR (t2_cred_t, key1, "key1"),
  62. LSM_STRING_PTR (t2_cred_t, key2, "key2"),
  63. };
  64. /*
  65. * level 2: Configuration object, containing a child credentials object
  66. */
  67. typedef struct t2_config {
  68. lws_dll2_t list;
  69. t2_cred_t *creds;
  70. const char *id1;
  71. const char *arg1;
  72. const char *ssid;
  73. unsigned int frequency;
  74. unsigned long arg2;
  75. unsigned int priority;
  76. } t2_config_t;
  77. static const lws_struct_map_t lsm_t2_config[] = {
  78. LSM_CHILD_PTR (t2_config_t,
  79. creds, /* the child pointer member */
  80. t2_cred_t, /* the child type */
  81. NULL, lsm_t2_cred, /* map object for item type */
  82. "creds"), /* outer json object name */
  83. LSM_STRING_PTR (t2_config_t, id1, "id1"),
  84. LSM_STRING_PTR (t2_config_t, arg1, "arg1"),
  85. LSM_STRING_PTR (t2_config_t, ssid, "ssid"),
  86. LSM_UNSIGNED (t2_config_t, frequency, "frequency"),
  87. LSM_UNSIGNED (t2_config_t, arg2, "arg2"),
  88. LSM_UNSIGNED (t2_config_t, priority, "priority"),
  89. };
  90. /*
  91. * level 1: list-of-configurations object
  92. */
  93. typedef struct t2_configs {
  94. lws_dll2_owner_t configs;
  95. } t2_configs_t;
  96. static const lws_struct_map_t lsm_t2_configs[] = {
  97. LSM_LIST (t2_configs_t, configs, /* the list owner type/member */
  98. t2_config_t, list, /* the list item type/member */
  99. NULL, lsm_t2_config, /* map object for item type */
  100. "config"), /* outer json object name */
  101. };
  102. /*
  103. * For parsing, this lists the kind of object we expect to parse so the struct
  104. * can be allocated polymorphically.
  105. *
  106. * Lws uses an explicit "schema" member so the type is known unambiguously. If
  107. * in the incoming JSON the first member is not "schema", it will scan the
  108. * maps listed here and instantiate the first object that has a member of that
  109. * name.
  110. */
  111. static const lws_struct_map_t lsm_schema[] = {
  112. LSM_SCHEMA (t2_configs_t, NULL, lsm_t2_configs, "t2"),
  113. /* other schemata that might need parsing... */
  114. };
  115. static int
  116. t2_config_dump(struct lws_dll2 *d, void *user)
  117. {
  118. t2_config_t *c = lws_container_of(d, t2_config_t, list);
  119. lwsl_notice("%s: id1 '%s'\n", __func__, c->id1);
  120. lwsl_notice("%s: arg1 '%s'\n", __func__, c->arg1);
  121. lwsl_notice("%s: ssid '%s'\n", __func__, c->ssid);
  122. lwsl_notice("%s: freq %d\n", __func__, c->frequency);
  123. lwsl_notice("%s: arg2 %lu\n", __func__, c->arg2);
  124. lwsl_notice("%s: priority %d\n", __func__, c->priority);
  125. lwsl_notice("%s: key1: %s, key2: %s\n", __func__,
  126. c->creds->key1, c->creds->key2);
  127. return 0;
  128. }
  129. static int
  130. t2_configs_dump(t2_configs_t *t2cs)
  131. {
  132. lwsl_notice("%s: number of configs: %d\n", __func__,
  133. t2cs->configs.count);
  134. lws_dll2_foreach_safe(&t2cs->configs, NULL, t2_config_dump);
  135. return 0;
  136. }
  137. int
  138. test2(void)
  139. {
  140. lws_struct_serialize_t *ser;
  141. struct lejp_ctx ctx;
  142. lws_struct_args_t a;
  143. t2_configs_t *top;
  144. uint8_t buf[4096];
  145. size_t written;
  146. int n, bad = 1;
  147. lwsl_notice("%s: start \n", __func__);
  148. memset(&a, 0, sizeof(a));
  149. a.map_st[0] = lsm_schema;
  150. a.map_entries_st[0] = LWS_ARRAY_SIZE(lsm_schema);
  151. a.ac_block_size = 512;
  152. lws_struct_json_init_parse(&ctx, NULL, &a);
  153. n = lejp_parse(&ctx, (uint8_t *)test2_json, (int)strlen(test2_json));
  154. lwsl_notice("%s: lejp_parse %d\n", __func__, n);
  155. if (n < 0) {
  156. lwsl_err("%s: test2 JSON decode failed '%s'\n",
  157. __func__, lejp_error_to_string(n));
  158. goto bail;
  159. }
  160. lwsac_info(a.ac);
  161. top = (t2_configs_t *)a.dest; /* the top level object */
  162. if (!top) {
  163. lwsl_err("%s: no top level object\n", __func__);
  164. goto bail;
  165. }
  166. t2_configs_dump(top);
  167. /* 2. Let's reserialize the top level object and see what comes out */
  168. ser = lws_struct_json_serialize_create(&lsm_schema[0], 1,
  169. LSSERJ_FLAG_OMIT_SCHEMA, top);
  170. if (!ser) {
  171. lwsl_err("%s: unable to init serialization\n", __func__);
  172. goto bail;
  173. }
  174. do {
  175. n = lws_struct_json_serialize(ser, buf, sizeof(buf), &written);
  176. switch (n) {
  177. case LSJS_RESULT_FINISH:
  178. puts((const char *)buf);
  179. break;
  180. case LSJS_RESULT_CONTINUE:
  181. case LSJS_RESULT_ERROR:
  182. goto bail;
  183. }
  184. } while (n == LSJS_RESULT_CONTINUE);
  185. if (strcmp(test2_json_expected, (char *)buf)) {
  186. lwsl_err("%s: expected %s\n", __func__, test2_json_expected);
  187. goto bail;
  188. }
  189. lws_struct_json_serialize_destroy(&ser);
  190. bad = 0;
  191. bail:
  192. lwsac_free(&a.ac);
  193. return bad;
  194. }