Document Json

This commit is contained in:
Jordan Bancino 2022-11-30 17:54:10 +00:00
parent 27da9ed88f
commit 5610cafe46
4 changed files with 254 additions and 190 deletions

View file

@ -35,7 +35,7 @@ Due: January 1, 2023
[x] HashMap [x] HashMap
[ ] Http [ ] Http
[ ] HttpServer [ ] HttpServer
[ ] Json [x] Json
[x] Log [x] Log
[ ] Matrix [ ] Matrix
[x] Queue [x] Queue

228
man/man3/Json.3 Normal file
View file

@ -0,0 +1,228 @@
.Dd $Mdocdate: November 30 2022 $
.Dt JSON 3
.Os Telodendria Project
.Sh NAME
.Nm Json
.Nd A fully-featured JSON API.
.Sh SYNOPSIS
.In Json.h
.Ft JsonType
.Fn JsonValueType "JsonValue *"
.Ft JsonValue *
.Fn JsonValueObject "HashMap *"
.Ft HashMap *
.Fn JsonValueAsObject "JsonValue *"
.Ft JsonValue *
.Fn JsonValueArray "Array *"
.Ft Array *
.Fn JsonValueAsArray "
.Ft JsonValue *
.Fn JsonValueString "char *"
.Ft char *
.Fn JsonValueAsString "JsonValue *"
.Ft JsonValue *
.Fn JsonValueInteger "long"
.Ft long
.Fn JsonValueAsInteger "JsonValue *"
.Ft JsonValue *
.Fn JsonValueFloat "double"
.Ft double
.Fn JsonValueAsFloat "JsonValue *"
.Ft JsonValue *
.Fn JsonValueBoolean "int"
.Ft int
.Fn JsonValueAsBoolean "JsonValue *"
.Ft JsonValue *
.Fn JsonValueNull "void"
.Ft void
.Fn JsonValueFree "JsonValue *"
.Ft void
.Fn JsonFree "HashMap *"
.Ft void
.Fn JsonEncodeString "const char *" "FILE *"
.Ft void
.Fn JsonEncodeValue "JsonValue *" "FILE *"
.Ft int
.Fn JsonEncode "HashMap *" "FILE *"
.Ft HashMap *
.Fn JsonDecode "FILE *"
.Sh DESCRIPTION
.Nm
is a fully-featured JSON API for C using
.Xr Array 3
and
.Xr HashMap 3
that can parse JSON, and serialize an in-memory structure
to JSON.
It builds on the foundation set up by those APIs because
that's all JSON really is, just maps and arrays.
.Nm
also provides a structure for encapsulating an arbitrary
value and identifying its type, making it easy for a program
to work with JSON data.
.Nm
is very strict and tries to adhere as closely as possible to
the proper defintion of JSON. It will fail on syntax errors
of any kind, which is fine for a Matrix homeserver because we can
just return M_BAD_JSON if anything here fails, but it may not
be suitable for other purposes.
.Pp
This JSON implementation focuses primarily on serialization and
deserialization to and from streams. It does not provide facilities
for handling JSON strings; it only writes JSON to output streams, and
reads them from input streams. If course, you can use the POSIX
.Xr fmemopen 3
and
.Xr open_memstream 3
if you want to deal with JSON strings, but JSON is intended to be an
exchange format. Data should be converted to JSON right before it is
leaving the program, and converted from JSON as soon as it is coming
in.
.Pp
The
.Nm
header defines the following enumeration for identifying types of
values:
.Bd -literal -offset indent
typedef enum JsonType
{
JSON_NULL, /* Maps to C NULL */
JSON_OBJECT, /* Maps to a HashMap of JsonValues */
JSON_ARRAY, /* Maps to an Array of JsonValues */
JSON_STRING, /* Maps to a NULL-terminated C string */
JSON_INTEGER, /* Maps to a C long */
JSON_FLOAT, /* Maps to a C double */
JSON_BOOLEAN /* Maps to a C boolean, 1 or 0 */
} JsonType;
.Ed
.Pp
A JsonValue encapsulates all the possible types that can be stored in
JSON. It is an opaque structure that can be managed entirely by the
functions defined in this API. It is important to note that in the case
of objects, arrays, and strings, this structure only stores pointers to
the allocated data, it doesn't store the data itself, but the data IS
freed when using
.Fn JsonFree .
.Pp
Objects are represented as hash maps consisting entirely of JsonValue
structures, and arrays are represented as arrays consisting entirely
of JsonValue structures. When generating a JSON object, any
attempt to stuff a value into a hash map or array without encoding it
as a JsonValue first will result in undefined behavior.
.Pp
.Fn JsonValueType
determines the type of a given JsonValue.
.Pp
The
.Fn JsonValue*
functions wrap their input in a JsonValue that represents the given
value. The
.Fn JsonValueAs*
functions do the opposite; they unwrap a JsonValue and return the
actual usable value itself. They all closely resemble each other and
they all behave the same way, so to save on time and effort, they're
not explicitly documented individually. If something is unclear about
how these functions work, consult the source code, and feel free
to write the documentation yourself for clarification. Otherwise,
reach out to the official Matrix rooms, and someone should be able
to help you.
.Pp
.Fn JsonValueNull
is a special case that represents a JSON null. Because
.Xr Array 3
and
.Xr HashMap 3
do not accept NULL values, this function should be used to represent
NULL in JSON. Even though a small amount of memory is allocated just
to point to NULL, this keeps the APIs clean.
.Pp
.Fn JsonValueFree
frees the memory being used by a JSON value. Note that this will
recursively free all Arrays, HashMaps, and other JsonValues that
are reachable from the given value. It also invokes
.Fn Free
(documented in
.Xr Memory )
on all strings, so make sure passed string pointers point to strings
on the heap, not the stack. This will be the case for all strings
returned by
.Fn JsonDecode ,
but if you are manually creating JSON objects and stitching them
together, you should be aware that calling this function on a value
that contains a pointer to a stack string will result in undefined
behavior.
.Pp
.Fn JsonFree
recursively frees a JSON object, iterating over all the values and
freeing them using
.Fn JsonValueFree .
.Pp
.Fn JsonEncodeString
encodes the given string in such a way that it can be embedded in a
JSON stream. This entails:
.Bl -bullet -offset indent
.It
Escaping quotes, backslashes, and other special characters using
their backslash escape.
.It
Encoding bytes that are not UTF-8 using escapes.
.It
Wrapping the entire string in double quotes.
.El
.Pp
This function is provided via the public
.Nm
API so that it is accessible to custom JSON encoders, such as
.Xr CanonicalJson 3 .
This will typically be used for encoding JSON keys; for encoding
values, just use
.Fn JsonEncodeValue .
.Pp
.Fn JsonEncodeValue
serializes a JsonValue as it would appear in JSON output. This is
a recursive function that also encodes all child values reachable
from the given value. This function is exposed via the public
.Nm
API so that it is accessible to custom JSON encoders. Normal users
that are not writing custom encoders should in most cases just use
.Fn JsonEncode
to encode an entire object.
.Pp
.Fn JsonEncode
encodes a JSON object as minimized JSON and writes it to the given
output stream. This function is recursive; it will serialize
everything accessible from the passed object.
.Fn JsonDecode
does the opposite; it reads from a JSON stream and decodes it
into a hash map of JsonValues.
.Sh RETURN VALUES
.Pp
.Fn JsonValueType
returns a JsonType, documented above, that tells what the given
value actually is, or JSON_NULL if the passed value is NULL.
Note that even a fully valid JsonValue may actually be of type
JSON_NULL, so this function should not be used to determine
whether or not a given value is valid.
.Pp
The
.Fn JsonValue*
functions return a JsonValue that holds a pointer to the passed
value, or NULL if there was an error allocating memory. The
.Fn JsonValueAs*
functions return the actual value represented by the given
JsonValue so that it can be manipulated by the program, or
NULL if no value was provided, or the value is not of the
correct type expected by the function.
.Pp
.Fn JsonEncode
returns whether or not the encoding operation was successful.
This function will fail if any passed parameters are NULL,
otherwise it will assume all pointers are valid and return a
success value.
.Pp
.Fn JsonDecode
returns a hash map of JsonValues that can be manipulated by
this API, or NULL if there was an error parsing the JSON.
.Sh SEE ALSO
.Xr HashMap 3 ,
.Xr Array 3

View file

@ -229,6 +229,12 @@ cache.
Smart memory management API. Smart memory management API.
</td> </td>
</tr> </tr>
<tr>
<td><a href="man/man3/Json.3.html">Json(3)</a></td>
<td>
A feature-complete API for reading and writing JSON.
</td>
</tr>
</table> </table>
<h2 id="resources">Resources</h2> <h2 id="resources">Resources</h2>
<ul> <ul>

View file

@ -22,29 +22,6 @@
* SOFTWARE. * SOFTWARE.
*/ */
/*
* Json.h: A fully-featured JSON API for C using Arrays and HashMaps.
* This API builds on the foundations of Arrays and HashMaps, because
* that's all a JSON object really is. It provides a JsonValue, which
* is used to encapsulate arbitrary values while being able to identify
* them in the future, so that JSON can be effectively handled.
*
* This implementation is just to get the job done in parsing and
* generating JSON. It is extremely strict; it will fail on syntax
* errors. This is fine for Matrix, because we can just return
* M_BAD_JSON anything in here fails.
*
* One thing to note about this implementation is that it focuses
* primarily on serialization and deserialization to and from streams.
* What this means is that it does not provide facilities for handling
* JSON strings; it only writes JSON to output streams, and reading
* them from input streams. Of course, you could use the POSIX
* fmemopen() and open_memstream() functions if you really want to deal
* with JSON strings, but JSON is intended to be an exchange format.
* Data should be converted to JSON when it is leaving, and converted
* from JSON when it is coming in. Ideally, most of the program would
* have no idea what JSON actually is.
*/
#ifndef TELODENDRIA_JSON_H #ifndef TELODENDRIA_JSON_H
#define TELODENDRIA_JSON_H #define TELODENDRIA_JSON_H
@ -54,223 +31,76 @@
#include <stdio.h> #include <stdio.h>
#include <stddef.h> #include <stddef.h>
/*
* All the possible JSON types. This enumeration is used to identify
* the type of the value stored in a JsonValue.
*/
typedef enum JsonType typedef enum JsonType
{ {
JSON_NULL, /* Maps to nothing. */ JSON_NULL,
JSON_OBJECT, /* Maps to a HashMap of JsonValues */ JSON_OBJECT,
JSON_ARRAY, /* Maps to an Array of JsonValues */ JSON_ARRAY,
JSON_STRING, /* Maps to a C string */ JSON_STRING,
JSON_INTEGER, /* Maps to a C long */ JSON_INTEGER,
JSON_FLOAT, /* Maps to a C double */ JSON_FLOAT,
JSON_BOOLEAN /* Maps to a C 1 or 0 */ JSON_BOOLEAN
} JsonType; } JsonType;
/*
* A JsonValue encapsulates all the possible values that can be stored
* in a JSON object as a single type, so as to provide a consistent
* API for accessing and setting them. It is an opaque structure that
* can be managed entirely by the functions defined in this API.
*
* Note that in the case of objects, arrays, and strings, this structure
* only stores pointers to allocated data, it doesn't store the data
* itself. JsonValues only store integers, floats, booleans, and NULL
* in their memory. Anything else must be freed separately.
*/
typedef struct JsonValue JsonValue; typedef struct JsonValue JsonValue;
/*
* Get the type of a JsonValue.
*
* Params:
*
* (JsonValue *) The value to get the type of.
*
* Return: A JsonType that tells what the provided value is, or
* JSON_NULL if the passed value is NULL. Note that even a fully
* valid JsonValue may still be of type JSON_NULL, so this function
* should not be used to check whether or not the JSON value is valid.
*/
extern JsonType extern JsonType
JsonValueType(JsonValue *); JsonValueType(JsonValue *);
/*
* Wrap a HashMap into a JsonValue that represents a JSON object. Note
* that the HashMap should contain only JsonValues. Any other contents
* are not supported and will lead to undefined behavior.
*
* Params:
*
* (HashMap *) The hash map of JsonValues to wrap in a JsonValue.
*
* Return: A JsonValue that holds a pointer to the given object, or
* NULL if there was an error allocating memory.
*/
extern JsonValue * extern JsonValue *
JsonValueObject(HashMap *); JsonValueObject(HashMap *);
/*
* Get a HashMap from a JsonValue that represents a JSON object.
*
* Params:
*
* (JsonValue *) The value to extract the object from.
*
* Return: A HashMap of JsonValues, or NULL if no value was provided,
* or the value is not of type JSON_OBJECT.
*/
extern HashMap * extern HashMap *
JsonValueAsObject(JsonValue *); JsonValueAsObject(JsonValue *);
/*
* The following methods very closely resemble the ones above, and
* behave pretty much the exact same. To save on time and effort,
* I'm choosing not to explicitly document all of these. If something
* is unclear about how these functions work, consult the source code,
* and then feel free to write the documentation yourself.
*
* Otherwise, reach out to the official Matrix room, and someone will
* be able to help you.
*/
extern JsonValue * extern JsonValue *
JsonValueArray(Array * array); JsonValueArray(Array *);
extern Array * extern Array *
JsonValueAsArray(JsonValue * value); JsonValueAsArray(JsonValue *);
extern JsonValue * extern JsonValue *
JsonValueString(char *string); JsonValueString(char *);
extern char * extern char *
JsonValueAsString(JsonValue *); JsonValueAsString(JsonValue *);
extern JsonValue * extern JsonValue *
JsonValueInteger(long integer); JsonValueInteger(long);
extern long extern long
JsonValueAsInteger(JsonValue *); JsonValueAsInteger(JsonValue *);
extern JsonValue * extern JsonValue *
JsonValueFloat(double floating); JsonValueFloat(double);
extern double
JsonValueAsFloat(JsonValue *);
extern JsonValue * extern JsonValue *
JsonValueBoolean(int boolean); JsonValueBoolean(int);
extern int
JsonValueAsBoolean(JsonValue *);
/*
* Create a JsonValue that represents a JSON null. Because Arrays and
* HashMaps should not contain NULL values, I thought it appropriate
* to provide support for JSON nulls. Yes, a small amount of memory is
* allocated just to point to a NULL, but this keeps all the APIs
* clean.
*
* Return: A JsonValue that represents a JSON null, or NULL if memory
* could not be allocated.
*/
extern JsonValue * extern JsonValue *
JsonValueNull(void); JsonValueNull(void);
/*
* Free the memory being used by a JSON value. Note that this will
* recursively free all Arrays, HashMaps, and other JsonValues that
* are reachable from this one. It will invoke free() on strings as
* well, so make sure passed string pointers point to strings on the
* heap, not the stack. This will be the case for all strings returned
* by JsonDecode(), which is why this assumption is made. However, if
* you are manually creating JsonObjects and stitching them together,
* you'll have to manually free them as well. Calling this on a
* JsonValue that contains a pointer to a stack string is undefined.
*
* Params:
*
* (JsonValue *) The JsonValue to recursively free.
*/
extern void extern void
JsonValueFree(JsonValue *); JsonValueFree(JsonValue *);
/*
* Recursively free a HashMap of JsonValues. This iterates over all
* the JsonValues in a HashMap and frees them using JsonValueFree(),
* which will in turn call JsonFree() on values of type JSON_OBJECT.
*
* Params:
*
* (HashMap *) The hash map of JsonValues to recursively free.
*/
extern void extern void
JsonFree(HashMap *); JsonFree(HashMap *);
/*
* Encode the given string in such a way that it can be embedded in a
* JSON stream. This entails:
*
* - Escaping quotes, backslashes, and other special characters using
* their backslash escape
* - Encoding bytes that are not UTF-8 using \u escapes.
* - Wrapping the entire string in double quotes.
*
* This function is provided via the public API so it is accessible to
* custom JSON encoders, such as the CanonicalJson API. This will
* typically be used for encoding JSON keys; for values, just use
* JsonEncodeValue().
*
* Params:
*
* (const char *) The C string to serialize as a JSON string.
* (FILE *) The output stream to write the encoded string to.
*/
extern void extern void
JsonEncodeString(const char *, FILE *); JsonEncodeString(const char *, FILE *);
/*
* Serialize a JsonValue as it would appear in JSON output. This is
* a recursive function that will also encode all child values
* reachable from the given JsonValue.
*
* This is exposed via the public API so that custom JSON encoders
* such as CanonicalJson can take advantage of it. Normal users that
* are writing custom encoders should just use JsonEncode() to encode
* an entire object.
*
* Params:
*
* (JsonValue *) The value to encode.
* (FILE *) The output stream to write the given value to.
*/
extern void extern void
JsonEncodeValue(JsonValue * value, FILE * out); JsonEncodeValue(JsonValue * value, FILE * out);
/*
* Encode a HashMap of JsonValues into a fully-valid, minimized JSON
* object. This function is recursive; it will serialize everything
* accessible from the passed object into JSON.
*
* Params:
*
* (HashMap *) The HashMap of JsonValues to encode and write to the
* output stream.
* (FILE *) The output stream to write the given HashMap to.
*
* Return: Whether or not the operation was successful. This function
* will fail if either the passed HashMap or file stream are NULL. In
* all other cases, this function succeeds.
*/
extern int extern int
JsonEncode(HashMap *, FILE *); JsonEncode(HashMap *, FILE *);
/*
* Decode the given input stream into a HashMap of JsonValues.
*
* Params:
*
* (FILE *) The input stream to parse JSON from.
*
* Return: A HashMap of JsonValues, or NULL if there was an error
* parsing the JSON.
*/
extern HashMap * extern HashMap *
JsonDecode(FILE *); JsonDecode(FILE *);