{"id":15048893,"url":"https://github.com/Sigmyne/xchange","last_synced_at":"2026-03-27T06:01:42.217Z","repository":{"id":255366091,"uuid":"796202092","full_name":"Smithsonian/xchange","owner":"Smithsonian","description":"Structured data representation and JSON support for C/C++","archived":false,"fork":false,"pushed_at":"2025-03-12T10:06:00.000Z","size":1834,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-12T11:22:29.521Z","etag":null,"topics":["c-language","c-programming-language","data-exchange","json","json-api","library","open-source","structured-data"],"latest_commit_sha":null,"homepage":"https://smithsonian.github.io/xchange/","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Smithsonian.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"attipaci","patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"lfx_crowdfunding":null,"polar":null,"buy_me_a_coffee":null,"thanks_dev":null,"custom":null}},"created_at":"2024-05-05T08:45:49.000Z","updated_at":"2025-03-12T10:06:04.000Z","dependencies_parsed_at":"2024-10-28T11:18:49.569Z","dependency_job_id":"0fba8274-ae67-4ff4-9a0c-82a4b522c2e9","html_url":"https://github.com/Smithsonian/xchange","commit_stats":{"total_commits":74,"total_committers":1,"mean_commits":74.0,"dds":0.0,"last_synced_commit":"c99ef719dcb10a2ec8ffc59429f41d4e1b0f4530"},"previous_names":["smithsonian/xchange"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Smithsonian%2Fxchange","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Smithsonian%2Fxchange/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Smithsonian%2Fxchange/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Smithsonian%2Fxchange/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Smithsonian","download_url":"https://codeload.github.com/Smithsonian/xchange/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243500771,"owners_count":20300770,"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":["c-language","c-programming-language","data-exchange","json","json-api","library","open-source","structured-data"],"created_at":"2024-09-24T21:17:02.894Z","updated_at":"2026-03-27T06:01:42.203Z","avatar_url":"https://github.com/Smithsonian.png","language":"C","funding_links":["https://github.com/sponsors/attipaci"],"categories":[],"sub_categories":[],"readme":"![Build Status](https://github.com/Sigmyne/xchange/actions/workflows/build.yml/badge.svg)\n![Tests](https://github.com/Sigmyne/xchange/actions/workflows/test.yml/badge.svg)\n![Static Analysis](https://github.com/Sigmyne/xchange/actions/workflows/analyze.yml/badge.svg)\n\u003ca href=\"https://sigmyne.github.io/xchange/apidoc/html/files.html\"\u003e\n ![API documentation](https://github.com/Sigmyne/xchange/actions/workflows/dox.yml/badge.svg)\n\u003c/a\u003e\n\u003ca href=\"https://sigmyne.github.io/xchange/index.html\"\u003e\n ![Project page](https://github.com/Sigmyne/xchange/actions/workflows/pages/pages-build-deployment/badge.svg)\n\u003c/a\u003e\n\n# xchange \n\n[![DOI](resources/796202092.svg)](https://doi.org/10.5281/zenodo.14634824)\n\nStructured data representation and JSON support for C/C++.\n\n - [API documentation](https://sigmyne.github.io/xchange/apidoc/html/files.html)\n - [Project page](https://sigmyne.github.io/xchange) on github.io\n\nAuthor: Attila Kovacs\n\nUpdated for 1.0 and later releases.\n\n## Table of Contents\n\n - [Introduction](#xchange-introduction)\n - [Building](#building-xchange)\n - [Linking your application against `xchange`](#xchange-linking)\n - [Structured data](#structured-data)\n - [JSON parser and emitter](#json-interchange)\n - [Error handling](#xchange-error-handling)\n - [Debugging support](#xchange-debugging-support)\n - [Future plans](#xchange-future-plans)\n - [Release schedule](#xchange-release-schedule)\n\n\n-----------------------------------------------------------------------------\n\n\u003ca name=\"xchange-introduction\"\u003e\u003c/a\u003e\n## Introduction\n\nThe __xchange__ library provides structured data representation and exchange in C/C++, and includes support for \nJSON parsing and generation. It is free to use, in any way you like, without licensing restrictions.\n\nFor JSON parsing end emitting, __xchange__ provides a higher-level data model than __cjson__, with high-level \nfunctions for accessing and manipulating data both with less code and with cleaner code.\n\nThe __xchange__ library was created, and is maintained, by Attila Kovács (Sigmyne, LLC), and it is available through \nthe [Sigmyne/xchange](https://github.com/Sigmyne/xchange) repository on GitHub. \n\n-----------------------------------------------------------------------------\n\n\u003ca name=\"building-xchange\"\u003e\u003c/a\u003e\n## Building\n\nThe __xchange__ library can be built either as a shared (`libxchange.so[.1]`) and as a static (`libxchange.a`) library, \ndepending on what suits your needs best.\n\nYou can configure the build, either by editing `config.mk` or else by defining the relevant environment variables \nprior to invoking `make`. The following build variables can be configured:\n\n - `CC`: The C compiler to use (default: `gcc`).\n\n - `CPPFLAGS`: C preprocessor flags, such as externally defined compiler constants.\n \n - `CFLAGS`: Flags to pass onto the C compiler (default: `-g -Os -Wall`). Note, `-Iinclude` will be added \n   automatically.\n \n - `CSTANDARD`: Optionally, specify the C standard to compile for, e.g. `c99` to compile for the C99 standard. If\n   defined then `-std=$(CSTANDARD)` is added to `CFLAGS` automatically.\n   \n - `WEXTRA`: If set to 1, `-Wextra` is added to `CFLAGS` automatically.\n\n - `FORTIFY`: If set it will set the `_FORTIFY_SOURCE` macro to the specified value (`gcc` supports values 1 \n   through 3). It affords varying levels of extra compile time / runtime checks.\n   \n - `LDFLAGS`: Extra linker flags (default: _not set_). Note, `-lm -lpthread` will be added automatically.\n\n - `CHECKEXTRA`: Extra options to pass to `cppcheck` for the `make check` target\n\n - `DOXYGEN`: Specify the `doxygen` executable to use for generating documentation. If not set (default), `make` will\n   use `doxygen` in your `PATH` (if any). You can also set it to `none` to disable document generation and the\n   checking for a usable `doxygen` version entirely.\n \n \nAfter configuring, you can simply run `make`, which will build the `shared` (`lib/libxchange.so[.1]`) and `static` \n(`lib/libxchange.a`) libraries, local HTML documentation (provided `doxygen` is available), and performs static\nanalysis via the `check` target. Or, you may build just the components you are interested in, by specifying the\ndesired `make` target(s). (You can use `make help` to get a summary of the available `make` targets). \n\nAfter building the library you can install the above components to the desired locations on your system. For a \nsystem-wide install you may simply run:\n\n```bash\n  $ sudo make install\n```\n\nOr, to install in some other locations, you may set a prefix and/or `DESTDIR`. For example, to install under `/opt` \ninstead, you can:\n\n```bash\n  $ sudo make prefix=\"/opt\" install\n```\n\nOr, to stage the installation (to `/usr`) under a 'build root':\n\n```bash\n  $ make DESTDIR=\"/tmp/stage\" install\n```\n\n\n\n-----------------------------------------------------------------------------\n\n\u003ca name=\"xchange-linking\"\u003e\u003c/a\u003e\n## Linking your application against `xchange`\n\nProvided you have installed the shared (`libxchange.so`) or static (`libxchange.a`) library in a location that is\nin your `LD_LIBRARY_PATH` (e.g. in `/usr/lib` or `/usr/local/lib`) you can simply link your program using the \n`-lxchange` flag. Your `Makefile` may look like: \n\n```make\nmyprog: ...\n\t$(CC) -o $@ $^ $(LDFLAGS) -lxchange \n```\n\n(Or, you might simply add `-lxchange` to `LDFLAGS` and use a more standard recipe.) And, in if you installed the \n__xchange__ library elsewhere, you can simply add the location to `LD_LIBRARY_PATH` prior to linking.\n\n\n-----------------------------------------------------------------------------\n\n\u003ca name=\"structured-data\"\u003e\u003c/a\u003e\n## Structured data\n\n - [Basic data types](#xchange-data-types)\n - [Scalars](#xchange-scalars)\n - [Arrays](#xchange-arrays)\n - [Creating structure](#xchange-creating-structure)\n - [Aggregate IDs](#xchange-aggregate-ids)\n - [Accessing substructures and elements](#accessing-data)\n - [Sorting fields](#sorting-fields)\n\n\nThe __xchange__ library defines the `XStructure` type to represent structured data. It is defined in `xchange.h`, but \nas a user you really do not need to know much about its layout, as you probably want to avoid low-level direct access \nto its elements. Rather, you should be using the functions of the __xchange__ API to create, modify, or access data \nwithin.\n\nUnder the hood, the `XStructure` contains a linked list of fields, each an `XField` data type to represent a single \nelement, or an array of elements, of the above mentioned types, including embedded `Xstructure`s. In this way, an \n`Xstructure` can easily represent a multi-level hierarchy of a composite data object. Each `XField` has a name/ID, an \nassociated data type, a dimensionality, a shape (for multidimensional arrays).\n\n\u003ca name=\"xchange-data-types\"\u003e\u003c/a\u003e\n### Basic data types\n\nThe __xchange__ library supports most basic (primitive) data types used across programming languages. The table below \nshows the unique __xchange__ types recognized by the library and the corresponding pointer/array type values:\n\n | `XType`       | element type             | Comment / example                                               |\n |:--------------|:------------------------:|:----------------------------------------------------------------|\n | `X_BOOLEAN`   | `boolean`                | `TRUE` (1 or non-zero) or `FALSE` (0)                           |\n | `X_BYTE`      | `char`                   | '`-128`' to  '`127`'                                            |\n | `X_INT16`     | `int16_t`                | '`-32768`' to '`32767`'                                         |\n | `X_INT32`     | `int32_t`                | '`-2,147,483,648`' to '`2,147,483,647`'                         |\n | `X_INT64`     | `int64_t`                | '`-9,223,372,036,854,775,808`' to '`9,223,372,036,854,775,807`' |\n | `X_FLOAT`     | `float`                  | `1`, `1.0`, `-1.234567e-28`                                     |\n | `X_DOUBLE`    | `double`                 | `1`, `1.0`, `-1.2345678901234567e-111`                          |\n | `X_STRING`    | `char *`    \t            | `Hello world!`, `line1\\nline2\\n` (0-terminated)                 |\n | `X_CHARS(n) ` | `char[n]`                | Fixed-length character arrays (also w/o termination)            |\n | `X_FIELD`     | `XField`                 | For irregular and/or heterogeneous arrays                       |\n | `X_STRUCT`    | `XStructure`             | substructure                                                    |\n\nThe `boolean` type is defined in `xchange.h`. The `XField.value` is a pointer / array of the given element type. So,\nan `XField` of type `X_DOUBLE` will have a `value` field that should be cast a `(double *)`, while for type `X_STRING`\nthe value field shall be cast as `(char **)`.\n\nAdditionally, the __xchange__ also defines derivative `XType` values for native integer storage types, whose widths \ndepend on the particular CPU architecture. Hence, these are aliased to matching unique types (above) by the C \npreprocessor during compilation:\n\n | `XType`       | element type             | width            | alias of                                   |\n |:--------------|:------------------------:|:----------------:|:-------------------------------------------|\n | `X_SHORT`     | `short`                  |   \u0026gt;= 16-bits  | typically `X_INT16`                        |\n | `X_INT`       | `int`                    |   \u0026gt;= 16-bits  | often `X_INT32`                            |\n | `X_LONG`      | `long`                   |   \u0026gt;= 32-bits  | typically `X_INT32` or `X_INT64`           |\n | `X_LLONG`     | `long long`              |   \u0026gt;= 64-bits  | typically `X_INT64`                        |\n\n\n\u003ca name=\"xchange-strings\"\u003e\u003c/a\u003e\n#### Strings\n\nStrings can be either fixed-width or else a 0-terminated sequence of ASCII characters. At its basic level the library \ndoes not impose any restriction of what ASCII characters may be used. However, we recommend that users stick to the \nJSON convention, and represent special characters in escaped form. E.g. carriage return (`0xd`) as `\\` followed by \n`n`, tabs as `\\` followed by `t`, etc. As a result a single backslash should also be escaped as two consecutive `\\` \ncharacters. You might use `xjsonEscapeString()` or `xjsonUnescapeString()` to perform the conversion to/from standard\nJSON representation.\n\nFixed-width strings of up to _n_ characters are represented internally as the `XCHAR(n)` type. They may be \n0-terminated as appropriate, or else represent exactly _n_ ASCII characters without explicit termination. \nAlternatively, the `X_STRING` type represents ASCII strings of arbitrary length, up to the 0-termination character.\n\n\u003ca name=\"xchange-scalars\"\u003e\u003c/a\u003e\n### Scalars\n\nYou can create scalar fields easily, e.g.:\n\n```c\n  // Create \"is_ok\" as a boolean field with TRUE\n  XField *fb = xCreateBooleanField(\"is_ok\", TRUE);\n\n  // Create \"serial-number\" field with an integer value\n  XField *fi = xCreateIntField(\"serial-number\", 1001);\n\n  // Create \"my measurement\" as a double-precision value 1.04\n  XField *fd = xCreateDoubleField(\"my-measurement\", 1.04);\n  \n  // Create \"description\" as a string\n  XField *fs = xCreateStringField(\"description\", \"blah-blah-blah\");\n```\n\nUnder the hood, scalar values are a special case of arrays containing a single element. Scalars have dimension zero \ni.e., a shape defined by an empty integer array, e.g. `int shape[0]` in a corresponding `XField` element. \n\nIn this way scalars are distinguished from true arrays containing just a single elements, which have dimensionality \n\u0026lt;=1 and shapes e.g., `int shape[1] = {1}` or `int shape[2] = {1, 1}`. The difference, while subtle, becomes more \nobvious when serializing the array, e.g. to JSON. A scalar floating point value of 1.04, for example, will appear as \n`1.04` in JSON, whereas the 1D and 2D single-element arrays will be serialized as `{ 1.04 }` or `{{ 1.04 }}`, \nrespectively.\n\n\n\u003ca name=\"xchange-arrays\"\u003e\u003c/a\u003e\n### Arrays\n\nThe __xchange__ library supports array data types in one or more dimensions (up to 20 dimensions). For example, to\ncreate a field for 2\u0026times;3\u0026times;4 array of `double`s, you may have something along:\n\n```c\n  double data[2][3][4] = ...;           // The native array in C\n  int sizes[] = { 2, 3, 4 };            // An array containing the dimensions for xchange\n  \n  // Create a field for the 3-dimensional array with the specified shape.\n  XField *f = xCreateField(\"my-array\", X_DOUBLE, 3, sizes, data);\n```\n\nNote, that there is no requirement that the native array has the same dimensionality as it's nominal format in the\nfield. We could have declared `data` as a 1D array `double data[2 * 3 * 4] = ...`, or really any array (pointer)\ncontaining doubles with storage for at least 24 elements. It is the `sizes` array, along with the dimensionality,\nwhich together define the number of elements used from it, and the shape of the array for __xchange__.\n\nArrays of irregular shape or mixed element types can be represented by fields containing an array of `XField`\nentries:\n\n```c\n  XField *row1, *row2, ...                 // Heterogeneous entries, each wrapped in an `XField`\n  XField data[N] = { *row1, *row2, ... };  // The irregular / mixed-type array. \n\n  XField *f = xCreateMixed1DField(\"my_array\", N);\n```\n\nOr, use `xCreateMixedArrayField()` to create a multi-dimensional array of heterogeneous elements the same way.\n\n\n\u003ca name=\"xchange-creating-structure\"\u003e\u003c/a\u003e\n### Creating structure\n\nStructures should always be created by calling `xCreateStruct()` (or else by an appropriate de-serialization \nfunction such as `xjsonParseAt()`, or as a copy via `xCopyStruct()`). Once the structure is no longer used it should be \nexplicitly destroyed (freed) by calling `xDestroyStruct()`. Named substructures can be added to any structure with \n`xSetSubstruct()`, and named fields via `xSetField()`. That is the gist of it. So for example, the skeleton structure \nfrom the example above can be created programatically as:\n\n\n```c\n  XStructure *s, *sys, *sub;\n  \n  // Create the top-level structure\n  s = xCreateStruct();\n  \n  // Create and add the \"system\" sub-structure\n  sys = xCreateStruct();\n  xSetSubstruct(s, \"system\", sys);\n  \n  // Create and add the \"subsystem\" sub-structure\n  sub = xCreateStruct();\n  xSetSubstruct(sys, \"subsystem\", sub);\n  \n  // Set the \"property\" field in \"subsystem\".\n  xSetField(sub, \"property\", xCreateStringField(\"some value here\"));\n```\n\nand then eventually destroyed after use as:\n\n```c\n  // Free up all resources used by the structure 's'\n  xDestroyStruct(s);\n```\n\n\u003ca name=\"xchange-aggregate-ids\"\u003e\u003c/a\u003e\n### Aggregate IDs\n\nSince the `XStructure` data type can represent hierarchies of arbitrary depth, and named at every level of the \nhierarchy, we can uniquely identify any particular field, at any level, with an aggregate ID, which concatenates the \nfield names each every level, top-down, with a separator. The convention of __xchange__ is to use colon (':') as the\nseparator. Consider an example structure (in JSON notation):\n\n```json\n  {\n    \"system\": {\n      \"subsystem\": {\n        \"property\": \"some value here\"\n      }\n    }\n  }\n```\n\nThen, the leaf \"property\" entry can be 'addressed' with the aggregate ID of `system:subsystem:property` from the top\nlevel. The `xGetAggregateID()` function is provided to construct such aggregate IDs by gluing together a leading and \ntrailing component.\n\n\n\u003ca name=\"accessing-data\"\u003e\u003c/a\u003e\n### Accessing substructures and elements\n\nOnce a structure is populated -- either by having constructed it programatically, or e.g. by parsing a JSON definition\nof it from a string or file -- you can access its content and/or modify it.\n\nE.g., to retrieve the \"property\" field from the above example structure:\n\n```c\n  XField *f = xGetField(s, \"system:subsystem:property\"); \n```\n\nor to retrieve the \"subsystem\" structure from within:\n\n```c\n  XStructure *sub = xGetSubstruct(s, \"system:subsystem\");\n```\n\nConversely you can set / update fields in a structure using `xSetField()` / `xSetSubstruct()`, e.g.:\n\n```c\n  XStructrure *newsub = ...     // The new substructure\n  XField *newfield = ...        // A new field to set\n  XField *oldfield, *oldsub;    // prior entries by the same field name/location (if any)\n  \n  oldfield = xSetField(s, newfield);        // Sets the a field in 's'\n  oldsub = xSetSubstruct(s, \"field\", sub);  // Set a substructure named \"bar\" in 's'\n```\n\nThe above calls return the old values (if any) for the \"foo\" and \"bar\" field in the structure, e.g. so we may dispose \nof them if appropriate:\n\n```c\n  // Destroy the replaced fields if they are no longer needed.\n  xDestroyField(oldfield);\n  xDestroyField(oldsub);\n```\n\nYou can also remove existing fields from structures using `xRemoveField()`, e.g.\n\n```c\n  // Remove and then destroy the field named \"blah\" in structure 's'.\n  xDestroyField(xRemoveField(s, \"blah\"));\n```\n\n#### Large structures\n\nThe normal `xGetField()` and `xGetSubstruct()` functions have computational costs that scale linearly with the number \nof direct fields in the structure. It is not much of an issue for structures that contain dozens of, or even a couple \nhundred, fields (per layer). For much larger structures, which have a fixed layout, there is an option for a \npotentially much more efficient hash-based lookup also. E.g. instead of `xGetField()` you may use `xLookupField()`:\n\n```c\n  XStructure *s = ...\n  \n  // Create a lookup table for all fields of 's' and all its substructures.\n  XLookupTable *l = xCreateLookupTable(s, TRUE);\n  \n  // Now use a hash-based lookup to locate the field by name\n  XField *f = xLookupField(l, \"subsystem:property\");\n \n  ...\n  \n  // Once done with the lookup, destroy it.\n  xDestroyLookup(l);\n```\n\nNote however, that preparing the lookup table has significant _O(N)_ computational cost also. Therefore, a lookup \ntable is practical only if you are going to use it repeatedly, many times over. As a rule of thumb, lookups may have \nthe advantage if accessing fields in a structure by name hundreds of times, or more.\n\nThe same performance limitation also applies to building large structures, since the `xSetField()` and \n`xSetSubstruct()` functions iterate over the existing fields to check if a prior field by the same name was already \npresent, and which should be removed before the new field is set (hence the time to build up a structure with _N_\nfields will scale as _O(N\u003csup\u003e2\u003c/sup\u003e)_ in general). The user may consider using `xInsertField()` instead, which is \nmuch more scalable for building large structures, since it does not check for duplicates (hence scales as _O(N)_ \noverall). However, `xInsertField()` also makes the ordering of fields less intuitive, and it is left up to the caller \nto ensure that field names added this way are never duplicated. (Tip: if you used `InsertField()` consistently, you \nmay call `xReverseFieldOrder()` at the end, so the fields will appear in the same order in which they were inserted.)\n\n\n#### Iterating over elements\n\nYou can easily iterate over the elements also. This is one application where you may want to know the internal layout\nof `XStructure`, namely that it contains a simple linked-list of `XField` fields. One way to iterate over a structures\nelements is with a `for` loop, e.g.:\n\n```c\n  XStructure *s = ...\n  XField *f;\n  \n  for (f = s-\u003efirstField; f != NULL; f = f-\u003enext) {\n    // Process each field 'f' here...\n    ...\n  }\n```\n\n\u003ca name=\"sorting-fields\"\u003e\u003c/a\u003e\n### Sorting fields\n\nYou can easily sort fields by name using `xSortFieldsByName()`, or with using a custom comparator function with \n`xSortFields()`. You can also reverse the order with `xReverseFieldOrder()`. For example to sort fields in a \nstructure (and its substructures) in descending alphabetical order:\n\n```c\n  XStructure *s = ...\n  \n  // Sort in by names in ascending order, recursively\n  xSortFieldsByName(s, TRUE);\n\n  // Reverse the order, recursively\n  xReverseFieldOrder(s, TRUE);\n```\n\n-----------------------------------------------------------------------------\n\n\u003ca name=\"json-interchange\"\u003e\u003c/a\u003e\n## JSON parser and emitter\n\nOnce you have an `XStructure` data object, you can easily convert it to JSON string representation, as:\n\n```c\n  #include \u003cxjson.h\u003e\n\n  XStructure *s = ...\n\n  // Obtain a JSON string representation of the structure 's'.\n  char *json = xjsonToString(s);\n```\n\nThe above produces a proper JSON document. Or, you can do the reverse and create an `XStructure` from its JSON \nrepresentation, either from a string (a 0-terminated `char` array):\n\n```c\n  #include \u003cxjson.h\u003e\n\n  char *tail;    // for return parse position\n  XStructure *s = xjsonParseString(json, \u0026tail);\n  if (s == NULL) {\n     // Oops, there was some problem...\n  }\n```\n\nor parse it from a file, which contains a JSON definition of the structured data:\n\n```c\n  #include \u003cxjson.h\u003e\n\n  XStructure *s = xjsonParsePath(\"my-data.json\");\n  if (s == NULL) {\n     // Oops, there was some problem...\n  }\n```\n\n### JSON fragments\n\nAlternatively, you can also create partial JSON fragments for individual fields, e.g.:\n  \n```c\n  XField *f = ...\n  \n  // Obtain a JSON fragment for the field 'f'.\n  char *json = xjsonFieldToString(f);\n```\n\nFor example, for a numerical array field with 4 elements the above might generate something like:\n\n```json\n  \"my-numbers\": [ 1, 2, 3, 4 ]\n```\n\n\n### Escaped string representations\n\nYou might just want to use JSON-style escaping for strings, and `xjsonEscape()` / `xjsonUnescape()` can help with that \ntoo. Suppose you have a C string that you want to escape...\n\n```c\n  char *string = \"\\\"This has some\\n\\t special characters\\\"\";\n  \n  // Escape the special character, e.g. replace `\\n` with `\\` + `n` etc...\n  char *escaped = xjsonEscape(string);\n```\n\nIf you print `string` to a file or the standard output, it will show up as 2 lines:\n\n```txt\n  \"This has some\n          special characters\"\n```\n\nBut if you now print `escaped` instead, that will show up as:\n\n```txt\n  \\\"This has some\\n\\t special characters\\\"\n```\n\nAnd the reverse, suppose you read back the above line from an input, containing the escaped form, and want to \nreconstruct from it the original C string with the special characters in it:\n\n```c\n   // And reverse, from escaped form to ASCII (e.g. `\\` + `n` --\u003e `\\n`)\n   char *string = xjsonUnescape(escaped);\n```\n\n\n-----------------------------------------------------------------------------\n\n\u003ca name=\"xchange-error-handling\"\u003e\u003c/a\u003e\n## Error handling\n\nThe functions that can encounter an error will return either one of the error codes defined in `xchange.h`, or \n`NULL` pointers. String descriptions for the error codes can be produced by `xErrorDescription(int)`. For \nexample,\n\n```c\n  char *text = ...\n  int status = xParseDouble(text, NULL);\n  if (status != X_SUCCESS) {\n    // Oops, something went wrong...\n    fprintf(stderr, \"WARNING! %s\", xErrorDescription(status));\n    ...\n  }\n```\n\nThe JSON parser can also sink its error messages to a designated file or stream, which can be set by \n`xjsonSetErrorStream(FILE *)`.\n \n-----------------------------------------------------------------------------\n \n\u003ca name=\"xchange-debugging-support\"\u003e\u003c/a\u003e\n## Debugging support\n\nYou can enable verbose output of the __xchange__ library with `xSetVerbose(boolean)`. When enabled, it will produce \nstatus messages to `stderr`so you can follow what's going on. In addition (or alternatively), you can enable debug \nmessages with `xSetDebug(boolean)`. When enabled, all errors encountered by the library (such as invalid arguments \npassed) will be printed to `stderr`, including call traces, so you can walk back to see where the error may have \noriginated from. (You can also enable debug messages by default by defining the `DEBUG` constant for the compiler, \ne.g. by adding `-DDEBUG` to `CFLAGS` prior to calling `make`). \n\nFor helping to debug your application, the __xchange__ library provides two macros: `xvprintf()` and `xdprintf()`, \nfor printing verbose and debug messages to `stderr`. Both work just like `printf()`, but they are conditional on \nverbosity being enabled via `xSetVerbose(boolean)` and `xSetDebug(boolean)`, respectively. Applications using \n__xchange__ may use these macros to produce their own verbose and/or debugging outputs conditional on the same global \nsettings. \n\n-----------------------------------------------------------------------------\n\n\u003ca name=\"xchange-future-plans\"\u003e\u003c/a\u003e\n## Future plans\n\nThere are a number of ways this little library can evolve and grow in the not too distant future. Some of the obvious\npaths forward are:\n\n - Add regression testing and code coverage tracking (high priority)\n - Add support for [BSON](https://bsonspec.org/spec.html) -- MongoDB's binary exchange format.\n - Add support for 128-bit floating point types (`X_FLOAT128`).\n \nIf you have an idea for a must have feature, please let me (Attila) know. Pull requests, for new features or fixes to\nexisting ones are especially welcome! \n \n-----------------------------------------------------------------------------\n\n\u003ca name=\"xchange-release-schedule\"\u003e\u003c/a\u003e\n## Release schedule\n\nA predictable release schedule and process can help manage expectations and reduce stress on adopters and developers \nalike.\n\nThe __xchange__ library will try to follow a quarterly release schedule. You may expect upcoming releases to be \npublished around __February 1__, __May 1__, __August 1__, and/or __November 1__ each year, on an as-needed basis. That \nmeans that if there are outstanding bugs, or new pull requests (PRs), you may expect a release that addresses these in \nthe upcoming quarter. The dates are placeholders only, with no guarantee that a new release will actually be available \nevery quarter. If nothing of note comes up, a potential release date may pass without a release being published.\n\nNew features are generally reserved for the feature releases (e.g. __1.x.0__ version bumps), although they may also be \nrolled out in bug-fix releases as long as they do not affect the existing API -- in line with the desire to keep \nbug-fix releases fully backwards compatible with their parent versions.\n\nIn the weeks and month(s) preceding releases one or more _release candidates_ (e.g. `1.0.1-rc3`) will be published \ntemporarily on GitHub, under [Releases](https://github.com/Sigmyne/xchange/releases), so that changes can be \ntested by adopters before the releases are finalized. Please use due diligence to test such release candidates with \nyour code when they become available to avoid unexpected surprises when the finalized release is published. Release \ncandidates are typically available for one week only before they are superseded either by another, or by the finalized \nrelease.\n\n\n-----------------------------------------------------------------------------\nCopyright (C) 2025 Attila Kovács\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FSigmyne%2Fxchange","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FSigmyne%2Fxchange","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FSigmyne%2Fxchange/lists"}