JSON serialization library for C using printf-style formatting
#include "printj.h"
int64_t printj(const char *format, ...);
int64_t fprintj(FILE *stream, const char *format, ...);
int64_t snprintj(char *str, size_t size, const char *format, ...);
int64_t asprintj(char **ret, const char *format, ...);
int64_t vprintj(const char *format, va_list args);
int64_t vfprintj(FILE *stream, const char *format, va_list args);
int64_t vsnprintj(char *str, size_t size, const char *format, va_list args);
int64_t vasprintj(char **ret, const char *format, va_list args);printj is a C99 header-only library for JSON serialization using a simplified printf-like format string.
This library does not support JSON parsing. It is intended for direct, formatted emission of valid JSON. Serialization is done without heap memory allocation, except when using asprintj or vasprintj.
The format string describes a JSON structure and contains directives introduced by a percent sign (%). Quotes around object keys are added automatically.
String values are quoted and escaped automatically. Quotes (") and backslashes (\) inside strings are escaped as required by the JSON specification.
Embedded null characters (\0) are not supported. All strings must be null-terminated. Wide character types are not supported. UTF-8 strings are supported and compatible.
Each function returns the number of characters that would have been written, excluding the terminating null byte, or a negative value on error.
printj() writes to stdout.
fprintj() writes to the given FILE * stream.
snprintj() writes at most size bytes (including the terminating null byte) to
the provided buffer.
asprintj() allocates a new null-terminated string using malloc and stores its
address in *ret.
The corresponding v* versions accept a va_list instead of variadic arguments.
The format of a directive is:
%[precision][n][length]specifier
The following specifiers are available:
| Specifier | Description |
|---|---|
| b | Boolean. |
| d | Signed integer. |
| u | Unsigned integer. |
| f | Floating-point value. |
| s | Null-terminated string. |
| % | Literal percent character. Prints % |
The specifier defines the broad type of directive. Though some specifiers can map to different C types. For example, an integer can be represented in C by a short, a long... To clearly define the C type of a directive we use the Length Modifier
If n is appended to a directive, the argument is treated as a pointer to the
corresponding type. If the pointer is NULL, the JSON literal null is emitted
instead of a value.
int *age_ptr = NULL;
printj("{\"age\":%nd}", age_ptr); // prints {"age":null}
char *name = NULL;
printj("{\"name\":%ns}", name); // prints {"name":null}The length modifier defines the C type that is passed as argument to the directive.
| Length | b | d | u | f |
|---|---|---|---|---|
| (none) | int |
int |
unsigned int |
double |
| hh | char |
char |
unsigned char |
|
| h | short |
short |
unsigned short |
|
| l | long |
long |
unsigned long |
float |
| ll | long long |
long long |
unsigned long long |
double |
| z | size_t |
|||
| j | intmax_t |
uintmax_t |
||
| t | ptrdiff_t |
Non-standard modifiers such as Q and Z (capital Z) are not supported. The L
length modifier corresponding to long double is also not supported.
The precision modifier specify the maximum length of the decimal part when serializing floats (or doubles). The default value is 15. For more details about float serialization see Float serialization below.
This modifier is only compatible with the f specifier and will produce an
error when applied to any other specifier.
The floating-point serialization algorithm is based upon this excellent
article. It takes
doubles as input and eventual float inputs will automatically be converted to
doubles. It uses scientific notation on numbers less than 1e-3 or greater or
equal to 1e9. The maximum number of digits in the decimal part is set via the
precision modifier (see Precision). The default and maximum value of this
parameter for doubles is 15. For floats the default precision is 8 (to
recognized as a float, your directive must have the appropriate Length
Modifier). NaN and infinity are not part of the JSON standard and will be
serialized as null.
Rounding is applied to the decimal part so that you can expect loss in precision less than 1/2 * 10^p (where p is the precision modifier). Though due to inherent floating point roundoff errors, when working with precision of 15 or 14 it's possible to get precision loss exceeding 1/2 * 10^p. No precision loss exceeding 10^p was observed during the testing of over 1000000 random doubles.
This library is distributed as a single header file.
To use the library, define the implementation macro in one C file before including the header:
#define PRINTJ_IMPLEMENTATION
#include "printj.h"Then include the header wherever needed:
#include "printj.h"By default, stdio.h is required. To disable all stdio.h usage, define the macro
PRINTJ_DONT_USE_STDIO before #including the library. The only required
standard headers are stddef.h and stdarg.h.
Similarly you can define PRINTJ_ASSERT, PRINTJ_MALLOC, PRINTJ_REALLOC and
PRINTJ_FREE macros by custom assert and malloc implementations.
The current implementation only works on 64-bit systems. Though it could easily be ported to other architectures.
- No validation of JSON output.
- Strings must not contain embedded null (\0) characters.
- No bounds-checking of format strings.
- No validation of UTF-8 input. UTF-8 is assumed to be well-formed.
- Wide character types (
wchar_t,wint_t) are not supported. long doubletype not supported.- Quotes are added automatically around string values and keys.
- No support for JSON parsing.
This library has not been audited for security. No security review or testing has been performed. Use in security-critical contexts is discouraged without further analysis.