{"id":13414463,"url":"https://github.com/cesanta/frozen","last_synced_at":"2025-03-14T21:32:35.060Z","repository":{"id":12704156,"uuid":"15376630","full_name":"cesanta/frozen","owner":"cesanta","description":"JSON parser and generator for C/C++ with scanf/printf like interface. Targeting embedded systems.","archived":false,"fork":false,"pushed_at":"2023-12-28T23:56:23.000Z","size":309,"stargazers_count":706,"open_issues_count":27,"forks_count":163,"subscribers_count":49,"default_branch":"master","last_synced_at":"2024-07-31T21:53:00.748Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cesanta.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2013-12-22T14:27:00.000Z","updated_at":"2024-07-22T09:14:03.000Z","dependencies_parsed_at":"2024-01-08T21:18:51.066Z","dependency_job_id":null,"html_url":"https://github.com/cesanta/frozen","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cesanta%2Ffrozen","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cesanta%2Ffrozen/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cesanta%2Ffrozen/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cesanta%2Ffrozen/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cesanta","download_url":"https://codeload.github.com/cesanta/frozen/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221509015,"owners_count":16834813,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T21:00:21.757Z","updated_at":"2025-03-14T21:32:35.053Z","avatar_url":"https://github.com/cesanta.png","language":"C","readme":"JSON parser and emitter for C/C++\n=================================\n\n# Features\n\n- ISO C and ISO C++ compliant portable code\n- Very small footprint\n- No dependencies\n- `json_scanf()` scans a string directly into C/C++ variables\n- `json_printf()` prints C/C++ variables directly into an output stream\n- `json_setf()` modifies an existing JSON string\n- `json_fread()` reads JSON from a file\n- `json_fprintf()` writes JSON to a file\n- Built-in base64 encoder and decoder for binary data\n- Parser provides low-level callback API and high-level scanf-like API\n- 100% test coverage\n- Used in [Mongoose OS](https://mongoose-os.com), an operating system\n  for connected commercial products on low-power microcontrollers\n\n# API reference\n\n## `json_scanf()`, `json_vscanf`\n\n```c\nint json_scanf(const char *str, int str_len, const char *fmt, ...);\nint json_vscanf(const char *str, int str_len, const char *fmt, va_list ap);\n\n/* json_scanf's %M handler  */\ntypedef void (*json_scanner_t)(const char *str, int len, void *user_data);\n```\n\nScans the JSON string `str`, performing scanf-like conversions according to `fmt`.\n`fmt` uses `scanf()`-like format, with the following differences:\n\n1. Object keys in the format string don't have to be quoted, e.g. \"{key: %d}\"\n2. Order of keys in the format string does not matter, and the format string may\n   omit keys to fetch only those that are of interest, for example,\n   assume `str` is a JSON string `{ \"a\": 123, \"b\": \"hi\", c: true }`.\n   We can fetch only the value of the `c` key:\n      ```C\n      int value = 0;\n      json_scanf(str, strlen(str), \"{c: %B}\", \u0026value);\n      ```\n3. Several extra format specifiers are supported:\n   - %B: consumes `int *` (or `char *`, if `sizeof(bool) == sizeof(char)`),\n      expects boolean `true` or `false`.\n   - %Q: consumes `char **`, expects quoted, JSON-encoded string. Scanned\n      string is malloc-ed, caller must free() the string.\n   - %V: consumes `char **`, `int *`. Expects base64-encoded string.\n      Result string is base64-decoded, malloced and NUL-terminated.\n      The length of result string is stored in `int *` placeholder.\n      Caller must free() the result.\n   - %H: consumes `int *`, `char **`.\n      Expects a hex-encoded string, e.g. \"fa014f\".\n      Result string is hex-decoded, malloced and NUL-terminated.\n      The length of the result string is stored in `int *` placeholder.\n      Caller must free() the result.\n   - %M: consumes custom scanning function pointer and\n      `void *user_data` parameter - see json_scanner_t definition.\n   - %T: consumes `struct json_token *`, fills it out with matched token.\n\nReturns the number of elements successfully scanned \u0026 converted.\nNegative number means scan error.\n\nExample - scan arbitrary JSON string:\n\n```c\n  // str has the following JSON string (notice keys are out of order):\n  // { \"a\": 123, \"d\": true, \"b\": [1, 2], \"c\": \"hi\" }\n\n  int a = 0, d = 0;\n  char *c = NULL;\n  void *my_data = NULL;\n  json_scanf(str, strlen(str), \"{ a:%d, b:%M, c:%Q, d:%B }\",\n             \u0026a, scan_array, my_data, \u0026c, \u0026d);\n\n  // This function is called by json_scanf() call above.\n  // str is \"[1, 2]\", user_data is my_data.\n  static void scan_array(const char *str, int len, void *user_data) {\n    struct json_token t;\n    int i;\n    printf(\"Parsing array: %.*s\\n\", len, str);\n    for (i = 0; json_scanf_array_elem(str, len, \"\", i, \u0026t) \u003e 0; i++) {\n      printf(\"Index %d, token [%.*s]\\n\", i, t.len, t.ptr);\n    }\n  }\n```\n\nExample - parse array of objects:\n\n```c\n  // str has the following JSON string - array of objects:\n  // { \"a\": [ {\"b\": 123}, {\"b\": 345} ] }\n  // This example shows how to iterate over array, and parse each object.\n\n  int i, value, len = strlen(str);\n  struct json_token t;\n\n  for (i = 0; json_scanf_array_elem(str, len, \".a\", i, \u0026t) \u003e 0; i++) {\n    // t.type == JSON_TYPE_OBJECT\n    json_scanf(t.ptr, t.len, \"{b: %d}\", \u0026value);  // value is 123, then 345\n  }\n```\n\n\n## `json_scanf_array_elem()`\n```c\nint json_scanf_array_elem(const char *s, int len,\n                          const char *path,\n                          int index,\n                          struct json_token *token);\n```\n\nA helper function to scan an array item with given path and index.\nFills `token` with the matched JSON token.\nReturns -1 if no array element found, otherwise non-negative token length.\n\n## `json_printf()`\n\nFrozen printing API is pluggable. Out of the box, Frozen provides a way\nto print to a string buffer or to an opened file stream. It is easy to\ntell Frozen to print to another destination, for example, to a socket, etc.\nFrozen does this by defining an \"output context\" descriptor which has\na pointer to a low-level printing function. If you want to print to another\ndestination, just define your specific printing function and initialise\noutput context with it.\n\nThis is the definition of the output context descriptor:\n\n```c\nstruct json_out {\n  int (*printer)(struct json_out *, const char *str, size_t len);\n  union {\n    struct {\n      char *buf;\n      size_t size;\n      size_t len;\n    } buf;\n    void *data;\n    FILE *fp;\n  } u;\n};\n```\n\nFrozen provides two helper macros to initialise two built-in output\ndescriptors:\n\n```c\nstruct json_out out1 = JSON_OUT_BUF(buf, len);\nstruct json_out out2 = JSON_OUT_FILE(fp);\n```\n\n```c\ntypedef int (*json_printf_callback_t)(struct json_out *, va_list *ap);\nint json_printf(struct json_out *, const char *fmt, ...);\nint json_vprintf(struct json_out *, const char *fmt, va_list ap);\n```\n\nGenerate formatted output into a given string buffer, auto-escaping keys.\nThis is a superset of printf() function, with extra format specifiers:\n - `%B` print json boolean, `true` or `false`. Accepts an `int`.\n - `%Q` print quoted escaped string or `null`. Accepts a `const char *`.\n - `%.*Q` same as `%Q`, but with length. Accepts `int`, `const char *`\n - `%V` print quoted base64-encoded string. Accepts a `const char *`, `int`.\n - `%H` print quoted hex-encoded string. Accepts a `int`, `const char *`.\n - `%M` invokes a json_printf_callback_t function. That callback function\n can consume more parameters.\n\nReturn number of bytes printed. If the return value is bigger than the\nsupplied buffer, that is an indicator of overflow. In the overflow case,\noverflown bytes are not printed.\n\nExample:\n\n```c\n  json_printf(\u0026out, \"{%Q: %d, x: [%B, %B], y: %Q}\", \"foo\", 123, 0, -1, \"hi\");\n  // Result:\n  // {\"foo\": 123, \"x\": [false, true], \"y\": \"hi\"}\n```\n\nTo print a complex object (for example, serialise a structure into an object),\nuse `%M` format specifier:\n\n```c\n  struct my_struct { int a, b; } mys = {1,2};\n  json_printf(\u0026out, \"{foo: %M, bar: %d}\", print_my_struct, \u0026mys, 3);\n  // Result:\n  // {\"foo\": {\"a\": 1, \"b\": 2}, \"bar\": 3}\n```\n\n```c\nint print_my_struct(struct json_out *out, va_list *ap) {\n  struct my_struct *p = va_arg(*ap, struct my_struct *);\n  return json_printf(out, \"{a: %d, b: %d}\", p-\u003ea, p-\u003eb);\n}\n```\n\n## `json_printf_array()`\n\n```c\nint json_printf_array(struct json_out *, va_list *ap);\n```\n\nA helper `%M` callback that prints contiguous C arrays.\nConsumes `void *array_ptr, size_t array_size, size_t elem_size, char *fmt`\nReturns number of bytes printed.\n\n## `json_walk()` - low level parsing API\n\n\n```c\n/* JSON token type */\nenum json_token_type {\n  JSON_TYPE_INVALID = 0, /* memsetting to 0 should create INVALID value */\n  JSON_TYPE_STRING,\n  JSON_TYPE_NUMBER,\n  JSON_TYPE_TRUE,\n  JSON_TYPE_FALSE,\n  JSON_TYPE_NULL,\n  JSON_TYPE_OBJECT_START,\n  JSON_TYPE_OBJECT_END,\n  JSON_TYPE_ARRAY_START,\n  JSON_TYPE_ARRAY_END,\n\n  JSON_TYPES_CNT,\n};\n\n/*\n * Structure containing token type and value. Used in `json_walk()` and\n * `json_scanf()` with the format specifier `%T`.\n */\nstruct json_token {\n  const char *ptr;           /* Points to the beginning of the value */\n  int len;                   /* Value length */\n  enum json_token_type type; /* Type of the token, possible values are above */\n};\n\n/* Callback-based API */\ntypedef void (*json_walk_callback_t)(void *callback_data,\n                                     const char *name, size_t name_len,\n                                     const char *path,\n                                     const struct json_token *token);\n\n/*\n * Parse `json_string`, invoking `callback` in a way similar to SAX parsers;\n * see `json_walk_callback_t`.\n */\nint json_walk(const char *json_string, int json_string_length,\n              json_walk_callback_t callback, void *callback_data);\n```\n\n`json_walk()` is a low-level, callback based parsing API.\n`json_walk()` calls a given callback function for each scanned value.\n\nCallback receives a name, a path to the value, a JSON token that points to the\nvalue and an arbitrary user data pointer.\n\nThe path is constructed using this rule:\n- Root element has \"\" (empty string) path\n- When an object starts, `.` (dot) is appended to the path\n- When an object key is parsed, a key name is appended to the path\n- When an array is parsed, an `[ELEMENT_INDEX]` is appended for each element\n\nFor example, consider the following json string:\n`{ \"foo\": 123, \"bar\": [ 1, 2, { \"baz\": true } ] }`.\nThe sequence of callback invocations will be as follows:\n- type: `JSON_TYPE_OBJECT_START`, name: `NULL`, path: `\"\"`, value: `NULL`\n- type: `JSON_TYPE_NUMBER`, name: `\"foo\"`, path: `\".foo\"`, value: `\"123\"`\n- type: `JSON_TYPE_ARRAY_START`, name: `\"bar\"`, path: `\".bar\"`, value: `NULL`\n- type: `JSON_TYPE_NUMBER`, name: `\"0\"`, path: `\".bar[0]\"`, value: `\"1\"`\n- type: `JSON_TYPE_NUMBER`, name: `\"1\"`, path: `\".bar[1]\"`, value: `\"2\"`\n- type: `JSON_TYPE_OBJECT_START`, name: `\"2\"`, path: `\".bar[2]\"`, value: `NULL`\n- type: `JSON_TYPE_TRUE`, name: `\"baz\"`, path: `\".bar[2].baz\"`, value: `\"true\"`\n- type: `JSON_TYPE_OBJECT_END`, name: `NULL`, path: `\".bar[2]\"`, value: `\"{ \\\"baz\\\": true }\"`\n- type: `JSON_TYPE_ARRAY_END`, name: `NULL`, path: `\".bar\"`, value: `\"[ 1, 2, { \\\"baz\\\": true } ]\"`\n- type: `JSON_TYPE_OBJECT_END,` name: `NULL`, path: `\"\"`, value: `\"{ \\\"foo\\\": 123, \\\"bar\\\": [ 1, 2, { \\\"baz\\\": true } ] }\"`\n\nIf top-level element is an array: `[1, {\"foo\": 2}]`\n- type: `JSON_TYPE_ARRAY_START`, name: `NULL`, path: `\"\"`, value: `NULL`\n- type: `JSON_TYPE_NUMBER`, name: `\"0\"`, path: `\"[0]\"`, value: `\"1\"`\n- type: `JSON_TYPE_OBJECT_START`, name: `\"1\"`, path: `\"[1]\"`, value: `NULL`\n- type: `JSON_TYPE_NUMBER`, name: `\"foo\"`, path: `\"[1].foo\"`, value: `\"2\"`\n- type: `JSON_TYPE_OBJECT_END`, name: `NULL`, path: `\"[1]\"`, value: `\"{\\\"foo\\\": 2}\"`\n- type: `JSON_TYPE_ARRAY_END`, name: `NULL`, path: `\"\"`, value: `\"[1, {\"foo\": 2}]\"`\n\nIf top-level element is a scalar: `true`\n- type: `JSON_TYPE_TRUE`, name: `NULL`, path: `\"\"`, value: `\"true\"`\n\n## `json_walk_args()` - low level parsing API extensible interface\n\nThis function is identical to json_walk() except that it takes a\nstruct pointer argument for the `callback` and `callback_data`\narguments and additional configuration elements:\n\n```\nstruct frozen_args {\n  json_walk_callback_t callback;\n  void *callback_data;\n  int limit;\n};\n```\n\nThis struct must be initialized using `INIT_FROZEN_ARGS()` to retain\nforward compatibility before any members are set as illustrated here:\n\n```\nstruct frozen_args args[1];\n\nINIT_FROZEN_ARGS(args);\n\nargs-\u003ecallback = mycb;\nargs-\u003ecallback_data = data;\nargs-\u003elimit = 20;\n\nret = json_walk_args(string, len, args);\n```\n\nthe `limit` member of `struct frozen_args` can be set to limit the\nmaximum recursion depth to prevent possible stack overflows and limit\nparsing complexity.\n\n## `json_fprintf()`, `json_vfprintf()`\n\n```c\n/*\n * Same as json_printf, but prints to a file.\n * File is created if does not exist. File is truncated if already exists.\n */\nint json_fprintf(const char *file_name, const char *fmt, ...);\nint json_vfprintf(const char *file_name, const char *fmt, va_list ap);\n```\n\n## `json_asprintf()`, `json_vasprintf()`\n\n```c\n/*\n * Print JSON into an allocated 0-terminated string.\n * Return allocated string, or NULL on error.\n * Example:\n *\n * ```c\n *   char *str = json_asprintf(\"{a:%H}\", 3, \"abc\");\n *   printf(\"%s\\n\", str);  // Prints \"616263\"\n *   free(str);\n * ```\n */\nchar *json_asprintf(const char *fmt, ...);\nchar *json_vasprintf(const char *fmt, va_list ap);\n```\n\n## `json_fread()`\n\n```c\n/*\n * Read the whole file in memory.\n * Return malloc-ed file content, or NULL on error. The caller must free().\n */\nchar *json_fread(const char *file_name);\n```\n\n## `json_setf()`, `json_vsetf()`\n\n```c\n/*\n * Update given JSON string `s,len` by changing the value at given `json_path`.\n * The result is saved to `out`. If `json_fmt` == NULL, that deletes the key.\n * If path is not present, missing keys are added. Array path without an\n * index pushes a value to the end of an array.\n * Return 1 if the string was changed, 0 otherwise.\n *\n * Example:  s is a JSON string { \"a\": 1, \"b\": [ 2 ] }\n *   json_setf(s, len, out, \".a\", \"7\");     // { \"a\": 7, \"b\": [ 2 ] }\n *   json_setf(s, len, out, \".b\", \"7\");     // { \"a\": 1, \"b\": 7 }\n *   json_setf(s, len, out, \".b[]\", \"7\");   // { \"a\": 1, \"b\": [ 2,7 ] }\n *   json_setf(s, len, out, \".b\", NULL);    // { \"a\": 1 }\n */\nint json_setf(const char *s, int len, struct json_out *out,\n              const char *json_path, const char *json_fmt, ...);\n\nint json_vsetf(const char *s, int len, struct json_out *out,\n               const char *json_path, const char *json_fmt, va_list ap);\n```\n\n## `json_prettify()`\n\n```c\n/*\n * Pretty-print JSON string `s,len` into `out`.\n * Return number of processed bytes in `s`.\n */\nint json_prettify(const char *s, int len, struct json_out *out);\n```\n\n## `json_prettify_file()`\n\n```c\n/*\n * Prettify JSON file `file_name`.\n * Return number of processed bytes, or negative number of error.\n * On error, file content is not modified.\n */\nint json_prettify_file(const char *file_name);\n```\n\n## `json_next_key()`, `json_next_elem()`\n\n```c\n/*\n * Iterate over an object at given JSON `path`.\n * On each iteration, fill the `key` and `val` tokens. It is OK to pass NULL\n * for `key`, or `val`, in which case they won't be populated.\n * Return an opaque value suitable for the next iteration, or NULL when done.\n *\n * Example:\n *\n * ```c\n * void *h = NULL;\n * struct json_token key, val;\n * while ((h = json_next_key(s, len, h, \".foo\", \u0026key, \u0026val)) != NULL) {\n *   printf(\"[%.*s] -\u003e [%.*s]\\n\", key.len, key.ptr, val.len, val.ptr);\n * }\n * ```\n */\nvoid *json_next_key(const char *s, int len, void *handle, const char *path,\n                    struct json_token *key, struct json_token *val);\n\n\n/*\n * Iterate over an array at given JSON `path`.\n * Similar to `json_next_key`, but fills array index `idx` instead of `key`.\n */\nvoid *json_next_elem(const char *s, int len, void *handle, const char *path,\n                     int *idx, struct json_token *val);\n\n```\n\n# Minimal mode\n\nBy building with `-DJSON_MINIMAL=1` footprint can be significantly reduced.\nThe following limitations apply in this configuration:\n * Only integer numbers are supported. This affects parsing and `%f/%lf` conversions in printf and scanf.\n * Hex ('%H') and base64 (`%V`) conversions are disabled.\n\n# Examples\n\n## Print JSON configuration to a file\n\n```c\njson_fprintf(\"settings.json\", \"{ a: %d, b: %Q }\", 123, \"string_value\");\njson_prettify_file(\"settings.json\"); // Optional\n```\n\n## Read JSON configuration from a file\n\n```c\nstruct my_config { int a; char *b; } c = { .a = 0, .b = NULL };\nchar *content = json_fread(\"settings.json\");\njson_scanf(content, strlen(content), \"{a: %d, b: %Q}\", \u0026c.a, \u0026c.b);\n```\n\n## Modify configuration setting in a JSON file\n\n```c\nconst char *settings_file_name = \"settings.json\", *tmp_file_name = \"tmp.json\";\nchar *content = json_fread(settings_file_name);\nFILE *fp = fopen(tmp_file_name, \"w\");\nstruct json_out out = JSON_OUT_FILE(fp);\njson_setf(content, strlen(content), \u0026out, \".b\", \"%Q\", \"new_string_value\");\nfclose(fp);\njson_prettify_file(tmp_file_name);  // Optional\nrename(tmp_file_name, settings_file_name);\n```\n\n# Contributions\n\nTo submit contributions, sign\n[Cesanta CLA](https://docs.cesanta.com/contributors_la.shtml)\nand send GitHub pull request.\n\n# Licensing\n\nFrozen is released under the\n[Apache 2.0 license](https://www.apache.org/licenses/LICENSE-2.0).\n\nFor commercial support and professional services,\n[contact us](https://www.mongoose-os.com/contact.html).\n\n# See also\n- [Mongoose Web Server Library](https://mongoose.ws/) - a robust, open-source solution licensed under GPLv2, designed to seamlessly integrate web server functionality into your embedded devices. \n- With complementary [Mongoose Wizard](https://mongoose.ws/wizard/) - a no-code visual tool that enables rapid WebUI creation without the need for frontend expertise.\n","funding_links":[],"categories":["JSON","C","Awesome Mongoose OS [![Awesome](https://awesome.re/badge.svg)](https://awesome.re)","进程间通信"],"sub_categories":["Community Tutorials","Json"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcesanta%2Ffrozen","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcesanta%2Ffrozen","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcesanta%2Ffrozen/lists"}