JSON Parser  1.0
JSON Parser Package

Introduction

The JSON Parser package provides three conversion functions, a JSON Parser, a JSON Generator and a JSON Validator. Additionally there are also a rich set of JSON Support Functions to assist C developers is using JSON Data easily with the minimum of coding overhead. Those are described in another document.

This document presents basic ideas on how to use the JSON Conversion Functions provided in this JSON package, and a brief introduction on using the available D-List functions, to access that JSON data after conversion.

Background

This JSON Parser package was written to provide not only a fully functional, fast and accurate JSON Parser for C programmers, but also to provide the JSON data in a form that is more generally useful to C developers. Most JSON parsers write the parsed JSON data into arrays etc, and there are numerous issues as C as a language does not natively support unbounded or variable sized data. However the D-List package for C does natively provide such support with a very easy to use, and rich, set of available functions to access, modify, add to, manipulate, manage and copy this variable data. The D-List package is also extremely fast & efficient and a great way to manipulate varied data from different sources.

All functions described in this document that start with json_xxx are part of the JSON Parser package, and details about there usage and arguments, can be found in the JSON Parser documentation. All functions that start with list_xxx are part of the D-List package.

JSON is a data format focused on inter-system and inter-language interoperability. If you are unfamiliar with JSON grammar or syntax, please refer to ECMA-404 and RFC 8259 or

http://json.org 

before trying to use the JSON Support Functions.

Throughout this JSON Parser package, all JSON Objects and JSON Arrays are accessed and manipulated using D-List list_objects, and all JSON value items are elements within those list_objects. The list_objects are arranged hierarchically allowing these data structures to represent the JSON syntax exactly.

If you are unfamiliar with D-List, then please consult the reference documentation. To find that documentation, or to learn more about list_objects, elements, and the rich set of D-List functions; please refer to the D-List documentation, which is available in the D-List distribution Header Files, or a set of Doxy files that are included in that distribution, or online at

http://info.fwsentry.org/dlist/index.html



Considerable documentation, code snippets and tutorials are available in the documents present in the GUIDES directory of this package. Additionally the json-parser-test.c file contains many additional examples of using the various JSON Conversation and JSON Support Functions available in this package.



JSON Package Function Group Descriptions

  Function Group                Description
JSON Conversion Functions Functions that validate and convert JSON data, to or from D-List list object format.
JSON Support Functions Functions that allow developers to easily access elements of JSON data from within a D-List representation, or modify or build new D-List JSON structures for later JSON Generation to text.
JSON Textural Functions Functions to translate or display JSON data as text.


The Function Descriptions

JSON Conversion Functions Description
json_parse_data_block() Convert a textural JSON data block into list object hierarchy.
json_valid_data_block() Validate a textural JSON data block, without conversion.
json_generate_data_block() Convert a hierarchical list object representing JSON data, into a textural JSON data block.


JSON Support Functions Description
json_find_members_value_string() Using the provided search criteria, search the JSON data and locate all object members in the list hierarchy that match the criteria, returning them in a new list_object to the caller.
json_find_members_value_integer() Using the provided search criteria, search the JSON data and locate all object members in the list hierarchy that match the criteria, returning them in a new list_object to the caller.
json_find_members_value_double() Using the provided search criteria, search the JSON data and locate all object members in the list hierarchy that match the criteria, returning them in a new list_object to the caller.
json_find_members_value_type() Using the provided search criteria, search the JSON data and locate all object members in the list hierarchy that match the criteria, returning them in a new list_object to the caller
json_find_member_value() Using the provided search criteria, search the JSON data and locate the first JSON object member in the list hierarchy that match the criteria, returning a pointer to it to the caller.
json_path_locate() Using the internal representation of a JSON dotted notation path, trace the provided path and return the list_object or element found.
json_create_new_list_object() Create a new list_object to be used to represent either a JSON Object or a JSON Array
json_report_list_object_type() Given an unknown list_object, provide the JSON value type this list_object represents. The result can either be JSON_object, JSON_array or JSON_value.
json_list_object_member_add_string() Add a new Member with a string value to a JSON Object, defined by the list_object provided.
json_list_object_member_add_number() Add a new Member with a number value to a JSON Object, defined by the list_object provided.
json_list_object_member_add_boolean() Add a new Member with a boolean value to a JSON Object, defined by the list_object provided.
json_list_object_member_add_null() Add a new Member with a null value to a JSON Object, defined by the list_object provided.
json_list_object_member_add_list() Add a new Member with a JSON Array or JSON Object value to an existing JSON Object, defined by the list_object provided.
json_list_object_array_value_add_string() Add a new String Value to a JSON Array, defined by the list_object provided.
json_list_object_array_value_add_number() Add a new Number Value to a JSON Array, defined by the list_object provided.
json_list_object_array_value_add_boolean() Add a new Boolean Value to a JSON Array, defined by the list_object provided.
json_list_object_array_value_add_null() Add a new null value to a JSON Array, defined by the list_object provided.
json_list_object_array_value_add_list() Add a new data value to an existing JSON Array, that has as a value type of either a JSON Array or JSON Object, defined by the list_object provided.


JSON Support Functions Description
json_path_to_list_object() Convert a JSON dotted notation path to a list_object for that path.
json_parse_dotted_path() Create a set of links describing a JSON data path, by parsing a character array with JSON dotted notion in it.
json_generate_path_from_links() Create a character array describing a set of links in a JSON Data block for reporting or printing purposes.
json_print_path_from_links() Display to stdout text describing a set of links in a JSON data block.
display_parsed_json_object() This function will either display all the elements in a single list_object, or will walk an entire hierarchy of list_objects displaying the elements as it goes through them.

JSON data representation -

Simply put, 

a JSON Object is mapped to a subordinate D-List list_object
a JSON Object Member name/value pair is an element within that list_object 
a JSON Value is an element inside the parent list_object, which could be a
    JSON Object or JSON Array
a JSON Array is a subordinate D-List list_object which has an element for
    each JSON Value within the JSON Array, inside the parent element which
    is enclosed in a JSON object or JSON Array

JSON value types -
    JSON_string is a pointer to the original string in the original JSON buffer,
        it is now nul terminated and compacted to remve escape sequences etc. 
        The pointer points to the first char after the original double quote 
        character, and the last character is the character before the ending
        double quote.
    JSON_array is a pointer to a new list_object which will contain the 
        values, one element per array value. If the array in the JSON code
        was a single entry then that single entry is a single element in
        the new subordinate Array list_object.
    JSON_object is a pointer to a new list_object which will contain the 
        Object Member name:value pairs, one list_object element per JSON Object 
        Member. If this JSON Object in the JSON data block only enclosed a single
        entry then that single entry is a single element in the new subordinate 
        list_object. There are no shortcuts, the hierarchy is preserved even for
        single elements.
        If the JSON Object was empty " { } " then the subordinate list will
        have zero elements, but there will still be a list_object describing it.
    All other JSON value types are converted and stored directly in the list
        elements, be they JSON Object Members (name/value), or a value only
        element as would be included in an Array, this includes all value types
        numbers, boolean, null, etc.

The D-List list_objects that now represent the JSON object, array and value 
structures, can be used just like any other D-List list_object. They can be sorted
and manipulated as needed. All data pointers within the lists point to portions
of the original JSON string buffer that was parsed.

DO NOT release the JSON buffer, until after the D-List lists that describe it have
been erased. All D-List lists used for parsing JSON objects are built with the 
COPY_DATA attribute, so all list storage is handled internally by the JSON parsing
code, and the deletions use D-List functions to properly free all the allocated
regions.

This representation of a JSON object eliminates all white space, and compresses
strings to remove escape characters, etc.

jCard representations are handled in the same way, a vCardArray would be an
array object, a new list_object, and each of the elements in that array would
be elements in the new list. elements with nul parameters, would have a NULL
entry in the parameters pointer, or there would be pointer to another list.

Important. For every element in every list_object, root or subordinate, that is
returned by the JSON Parser, you must always check the JSON_value_type in each
element BEFORE attempting to access the element value. Inaccurate handling of 
element values in your code will lead to code or data corruption. Please refer
to the typedef json_element_t in json-parser.h for more information. Fields that
are not used in an element will always be 0x00000000.                               

Example of Parser Usage
-----------------------

Using this simple JSON data block, for example:
        {   "country" : "JP", 
            "language" : "en", 
            "names" : [ "Hayao", "Totoro", "Ponyo", "Nausica", "Kiki" ], 
            "films" : { "Ponyo" : 2008,
                        "Princess Mononoke" : 1997,
                        "Tonari no Totoro" : 1988,
                        "How Do You Live?" : "In Production"},
            "Ghibli" : [    { "film" : "Spirited Away", "release" : 2001, "time" : 125},
                            { "film" : "Howl's Moving Castle", "release" : 2004, "time" : 119},
                            { "film" : "Grave of the Fireflies", "release" : 1988, "time" : 89},
                            { } ],
            "Studio" : "Kabushiki gaisha Sutajio Jiburi"
            "Formal" : "株式会社スタジオジブリ" 
        }

would be parsed into a set of D-List list_objects such as:

                                    value_type  member_name : value
                                    ----------  -----------   -----
    (p)-->  root_list-> element_1-> JSON_string "country"   : "JP"

                        element_2-> JSON_string "language"  : "en"

                        element_3-> JSON_array  "names"     : value.object --> array_list_a  -->(a)

                        element_4-> JSON_object "films"     : value.object --> object_list_2 -->(b)

                        element_5-> JSON_array  "Ghibli"    : value.object --> array_list_b  -->(c)

                        element_6-> JSON_string "Studio"    : "Kabushiki gaisha Sutajio Jiburi"

                        element_6-> JSON_string "Formal"    : "株式会社スタジオジブリ"
                    (end of root_list)

             (a)--> array_list_a->  element_1-> JSON_string NULL : "Hayao"
                                    element_2-> JSON_string NULL : "Totoro"
                                    element_3-> JSON_string NULL : "Ponyo"
                                    element_4-> JSON_string NULL : "Nausica"
                                    element_5-> JSON_string NULL : "Kiki"
                                (end of array_list_a)

             (b)--> object_list_2-> element_1-> JSON_integer    "Ponyo"             : 2008
                                    element_2-> JSON_integer    "Princess Mononoke" : 1997
                                    element_3-> JSON_integer    "Tonari no Totoro"  : 1988
                                    element_4-> JSON_string     "How Do You Live?"  : "In Production"
                                (end of object_list_2)

             (c)--> array_list_b->  element_1-> JSON_object NULL : value.object --> object_list_3 -->(d)
                                    element_2-> JSON_object NULL : value.object --> object_list_4 -->(e)
                                    element_3-> JSON_object NULL : value.object --> object_list_5 -->(f)
                                    element_4-> JSON_object NULL : value.object --> object_list_6 -->(g)
                                (end of array_list_b)

             (d)--> object_list_3-> element_1-> JSON_string     "film"      : "Spirited Away"
                                    element_2-> JSON_integer    "release"   : 2001
                                    element_3-> JSON_integer    "time"      : 125
                                (end of object_list_3)

             (e)--> object_list_4-> element_1-> JSON_string     "film"      : "Howl's Moving Castle"
                                    element_2-> JSON_integer    "release"   : 2004
                                    element_3-> JSON_integer    "time"      : 119
                                (end of object_list_4)

             (f)--> object_list_5-> element_1-> JSON_string     "film"      : "Grave of the Fireflies"
                                    element_2-> JSON_integer    "release"   : 1988
                                    element_3-> JSON_integer    "time"      : 89
                                (end of object_list_5)

             (g)--> object_list_6-> <no elements>
                                (end of object_list_6)

For each element encountered in any list_object, the value_type of that element will tell you what type of element it is, and from there you know that it is either a value, in which case the value is there in the element, or it is a JSON Array or JSON Object and the pointer to the relevant subordinate list_object is there in the element.

So in the above example calling json_parse_data_block() with the above JSON data block would return a single pointer (p) to the root_list, which would have 6 elements in it, 3 of them with subordinate list pointers in them, and a the json_data_type of JSON_object, so you knew the root list represented a JSON Object at the top level. In this case the subordinate lists would have 5, 4 and 4 elements in them respectively.

These lists can then be retrieved, searched, sorted, and managed as you would any other D-List using the various D-List functions. It is easy to find out if a JSON array or JSON Object was empty by calling the function list_size() or list_is_empty() on that subordinate list_object. Or by using list iteration so any processing code is only used when there are elements to retrieve. Please refer to D-List documentation for more information on how to use D-List list_objects and functions. However even though D-List provides a broad range of options to access and manage data elements, it is at heart very easy to use the core set of D-List functions.

So with the above JSON sample data as input, this code snippet can find a particular film year, using the JSON Parser default search functions

list_object *root_list;
json_member_list_t results;
json_element_t *element_ptr, *film_element_ptr;
unsigned int json_data_type;
char *the_json_data;

the_json_data = read_the_json_data(filename);

root_list  = json_parse_data_block(the_json_data, &json_data_type, NULL, NULL, NULL, NULL, FALSE);
if (json_data_type == JSON_object) element_ptr = list_simple_search(root_list, "films");
if ((element_ptr) && (element_ptr->value_type == JSON_object)) {
    film_element_ptr = list_simple_search(element_ptr->value.object, "Ponyo");
    if ((film_element_ptr) && (film_element_ptr->value_type == JSON_string)){
        fprintf(stdout, "The Movie %s was released in %Ld.\n", 
                film_element_ptr->member_name, 
                film_element_ptr->value.integer);
    } else {
        fprintf(stdout, "Movie Ponyo not found in list\n");
    }
} else {
    do something if the JSON object element name "films" is missing.
}

Another option to retrieve the specific film element is to use the JSON Support Functions provided in the Package. So for example:

list_object *root_list;
json_member_list_t results;
unsigned int json_data_type;
int ret;

root_list  = json_parse_data_block(the_json_data, &json_data_type, NULL, NULL, NULL, NULL, FALSE);
if (json_data_type == JSON_object) {
    ret = json_find_member_value(&results, root_list, "films", FALSE, "Ponyo", FALSE);
    if ((ret > 0) && (results.value_type == JSON_integer)){
        fprintf(stdout, "The Movie %s was released in %Ld.\n", 
                results.json_member->member_name, 
                results.json_member->value.integer);
    }
} else {
    fprintf(stdout, "Movie Ponyo not found in list\n");
}

If you know the exact layout of a given JSON Data block it will be more efficient for you to write code to navigate it directly, as in the first example above. However if you are unaware of its layout, it is easier to use the built JSON Support Functions which can take the hassle out of finding arbitrary data in unknown or arbitrary locations within that JSON data.

This example uses the function json_find_member_value() which will return a single instance of the data being searched for. However the function does return a count of the number of searches matches it actually found. As an alternative method you could call the broader function json_find_members_value_string() which will return a list of all the matches found based on the same type of (Member Name : String Value) evaluation of the entire JSON data block.

The utility json-parser-test which is part of this JSON Parser package has a function to output a parsed set of list_objects of a JSON data block to stdout, this is a good way to see how the JSON data block is formatted into D-List list_objects and elements.

STANDARDS

This implementation of this JSON Parser package is completely compliant with 
RFC 8259 and ECMA-404. It does not impose any restrictions on what may or may 
not be deemed fully interoperable between JSON peers sharing data, it does 
conform to the specifications of RFC 8259, but does not impose restrictions
based its guidelines of interoperability. 

Based on RFC 8259's written document structure, the following notes should 
help clarify any details.

2. JSON Grammar 

JSON grammar is strictly enforced by the validator and the parser. They
enforce the same requirements as those imposed by RFC 8259 Section 10, and
do not allow variations beyond those standards as MAY be permitted but not
required or standard, by Section 9.

3. Values

All JSON values are kept intact, and in the order they were present in the
original JSON data block. All literal values are converted to a JSON_value_type
present in each D-List element that represents any data value or Object Member 
name:value pair.

All numbers are converted, integers to a 64 bit minimum size, and fractional
numbers to a long double. Exponent numbers may be converted to long double or
optionally may be left intact as text (string) representations for the caller
to convert themselves later. This is an option available when calling the JSON
Parser function to allow callers with high precision number requirements to 
handle the conversions themselves to what ever standard of precision they wish.

4. Objects

Objects are represented as a D-List list_object, each member of the JSON Object
being an element in that list. All JSON object members are converted in the 
order they were presented in the original JSON data block. RFC 8259 states that
member names should be unique. This implementation parses all object members,
therefore it allows non-unique names in an object. If you use the D-List function
list_simple_search() on such a list it will always return the last instance of
a name:value member in that list. All the other list_search_xx() type functions
in D-List can be used to find any or all of such duplicate members if needed.

The JSON Support Functions provide a much richer set of tools to access JSON 
Objects and data, these were built on top of the D-List functions to simplify
JSON data usage for users. These functions can find all JSON Object Members with
equal names, etc.

However for proper interoperability the author strongly suggests that users of
JSON stick to the recommendations of the RFC, and avoid duplicate member names,
and use array values on a single instance of the member name instead. One area
where non-unique member names is appropriate is where a JSON user wants to 
incorporate something in the JSON grammer, that is not important to the
consumer of that object, for example comments. Multiple object members with the
name "comment" would be a sensible way of documenting a JSON data block without
either violating the standards of JSON grammar, or confusing a parser or consumer 
of that data.

When using any element in an object list, or iterating through that list, do not
make any assumptions about what type of value the element value is. ALWAYS check
the element JSON_value_type first before accessing the value item.

JSON Object Member names can be any string (by definition of the standards), so
that also means they can be a zero length string  ( "" ) which may not be very
useful to data blocks, but is part of the standard. A JSON Object Member D-List
element will always have a pointer to the Member name, which will always be a
valid string, even zero length.

5. Arrays

Arrays are represented by a subordinate D-List list_object. The elements of that
list_object can be of any legitimate JSON value type. As per the RFC there is no
requirement for the elements to be of the same type.

Just as with JSON Objects, when using an array list, or iterating through that 
list, do not make any assumptions about what type of value the element value is.
ALWAYS check the element JSON_value_type first before accessing the value item.

JSON Array values have no names, (by definition of the standards), so that also
means a JSON array value D-List element will always have a NULL object name 
pointer and therefore no Member name. The JSON Object Member name, if there
was one, will be in the superior list element that originally pointed to this
array list_object.

If you code has an element from a JSON data block, but for some reason does not
know where it came from (an Object or an Array), checking to see if the pointer
to member_name is NULL or not will answer the question.

6. Numbers, were covered above in 3. Values.

7. Strings

All strings in the JSON Validator, Parser and Generator eco-system are stored
and transfered as nul terminated characters arrays. They are not kept with a
specific size attached. This follows C language style semantics and formal
UTF-8 encoding practices. It also means that it is not permitted in this JSON
eco-system to embed nul (0x00) bytes in text values, as they will cause the 
premature termination of such string values. The parser and validator will
ignore nul bytes in the input values. The generator will terminate the 
processing of a textural value when it encounters a nul byte.

The JSON Parser preserves all string values as is. All string values are in 
fact left in-situ in the original JSON data block that was presented for 
parsing. This data block is extensively modified by the parser. It leaves 
strings in place, but terminates them with a nul at the end of the string. 
Additionally any escape sequences present in the string are converted to the 
actual character in-situ, and the escape character is removed. So for 
example "The test string\n\tNew line." would be converted to a character array
of 27 characters, the \n would be changed to a 0x12 and the \t to 0x11, all 
the following characters would be shifted to the left to fill the empty space
left by the escape characters, and the string would be terminated at the end
by 0x00. Therefore the char * pointer to that character array would act 
correctly, and be treated as a string by all C library functions, and still 
be in-situ in the original JSON data block. The design idea of this JSON 
Parser was to reduce the heap overhead required to represent everything in 
that JSON data block, and greatly simplify the removal of all the objects
(text strings etc) after the user has finished with the JSON data.

JSON escapes \u are similarly encoded in-situ removing the original hex escape 
sequence. However it should be noted that if any byte in a \uxxxx sequence is
nul (0x00), that byte will be ignored and not written into the output character
string. So for example if the serializer of the JSON had escaped a tab control
character 0x11 as \u0011 instead of \t (which is permitted by the standards) 
the byte 0x00 will be ignored, and the byte 0x11 will be added to the string.
If the original JSON serializer encoded \u0000 both bytes will be ignored by 
this parser. Although it is valid in JSON grammar to pass such a value, in 
any C type language, those bytes would terminate a character array and render
the rest of the string useless. No syntax errors are thrown for nul bytes
encoded this way, they are just silently ignored to preserve C language 
semantics.

If the caller of the JSON Parser wishes to preserve (not convert) all exponent
numbers, they will be left in-situ as strings as well. However instead of being
described as JSON_string types, they will be JSON_float_str so the caller knows
the difference when they find the elements.

8.1 Character Encoding

This JSON Parser makes no attempt to enforce or even recognize the encoding used.
It parses the characters in-situ and allows the caller to decide what data is or
is not there. It neither removes byte order marks (0xFEFF) nor tags them as errors.
It passes all text as is. Similarly unpaired UNICODE characters are passed as is
by this parser. The consumer of these data items is responsible for safe handling 
and usage of them. There is an underlying assumption that the data is in UTF-8
form, but there is no such enforcement, and nothing changes or is damaged, if the
data is not in UTF-8 format.

8.3 String Comparison

This JSON Parser provides a set of default search functions and compare 
functions which act on strings as-is. If the caller wishes to transform 
textural values into UNICODE first before such comparisons or compares, they 
can provide their own search and compare functions to add to the lists by 
default. These search or compare functions are very simple and easy to write.

9. Parsers

This JSON Parser accepts all forms of text that conform to the JSON grammar. This
parser sets no limits on the size of texts it accepts. If it was able to fit in the
original JSON data block, it is accepted and used in-situ.

This JSON Validator does set a limit of 2048 nested levels of JSON Objects and
JSON Arrays. 

This JSON Parser sets no limits at all, except those that may be enforced by
the host operating system, such as available heap space. The parser is 
totally recursive and re-entrant, it will parse any valid JSON data block,
mo matter how deeply it is nested.

This JSON Validator and Parser set no limits on string length or contents.

This JSON Generator imposes no limits, and acts as a mirror of the JSON
Parser using the same recursive model.

10. Generators

This package includes a JSON Generator. It translates a valid D-List 
list_object tree representation of JSON data into a textural JSON data 
block, according to the standards of RFC 8259. The caller of the JSON 
Generator function gets a nul terminated character array back, which they
can write to a file, or send over a socket, or whatever they want to do 
with it. The output of this JSON Generator is strictly compliant with the
standards set forth by RFC 8259 and ECMA-404.

Generator Notes: 
    Strings

    The JSON Generator assumes all input strings, either member names or
    values, are ASCII or UTF-8. There is no checking beyond nul bytes which
    have no place inside a UTF-8 string. No enforcement of FF, FE, C0 or C1.
    Both the parser and generator assume the callers will properly deal with
    byte encodings. It is the responsibility of the caller to enforce whatever
    encoding standards they wish for the text strings. All control characters
    (0x01 - 0x1F) are either directly escaped or rendered in \uxxxx form. nul
    0x00 is ignored as it will signify the end of the character array being 
    generated and any characters after the nul byte will be implicitly ignored.

    Style

    The human readable textural data output by the JSON Generator is in a 
    particular style, which is in-line with general conventions of both C
    and JSON. The author investigated a considerable number of JSON files 
    from many different sources, and settled on a format style that seems
    to fit within the general ways people write or generate JSON text data.

    Content

    If you use the JSON Parser to create D-List data from a JSON data file,
    and then use the JSON Generator to create another textural 
    representation of that original data, it will look very similar, with
    some style differences. The only substantial difference will be that
    any hex escape sequences will not be reproduced, but output instead as
    UTF-8 bytes, with the exception of any characters that are required by 
    the standards to be escaped.

    Exponent Numbers.

    The production of exponent numbers will depend on how the caller of the
    JSON Generator prepared those numbers prior to calling. If those exponent
    numbers were put in the D-List elements as textural strings, they are
    reproduced exactly. If they were put in as converted double float numbers
    the results are only going to be as good as the basic C libraries that 
    are available on your system, and the various precision and rounding 
    issues they may have. In general it is best to create these as textural
    floating point representations prior to calling the JSON Generator if
    accuracy and precision for exponent numbers is important to your needs.

12. Security Considerations

This parser uses the JSON data block provided by the caller. All other data
fields and structures are provided by the D-List subsystem. If data leakage
is considered an issue for the caller, they are responsible for managing the
original JSON data block, and may take extra steps to scrub it after usage,
or extra steps to allocate it in a random location if desired. The internal
D-List data, the list_object, the elements, and the internal space management 
data are released when the root list_object is erased by list_erase(). If
extra security precautions are required by the caller, they may take 2 extra
steps. First if they compile the D-List code that is to be used in this parser
with the compile option DLIST_FLUSH_MEMORY, D-List will enforce a strict
memory scrubbing system to purge all data before release. It makes D-List a
little slower, but cleans everything. Another option for the caller is to
provide their own remove_function to the root list_object and all the others.
But be sure that your replacement also provides the same functions as the 
in-built one does, otherwise there will be memory leaks galore.

Extra Note
----------

It seems that the issue of whether or not comments                  // blah blah 
should be permitted in JSON data blocks is rather contentious among the user
community. It does seem clear that there is no contention at all in the 
standards community, IETF and ECMA both clearly state the JSON grammar does
not include comments.

This implementation of both the JSON Validator and JSON Parser sticks to the 
standards, and will reject any JSON data block that contains comments, or 
other non JSON grammar encoding. Although RFC 8259 section 9 states that 

    "A JSON parser MAY accept non-JSON forms or extensions."

This implementation does not accept any non standard grammar forms or syntax.
Not just for the obvious operability argument, but also because the next 
section (10. Generators) states very clearly, that 

    "A JSON generator produces JSON text. The resulting text MUST 
    strictly conform to the JSON grammar."

There would be no point in having a JSON Parser that works on a different 
standard of grammar than that imposed on the JSON Generator of that grammar, 
when both are in the same JSON Package.

If at some point in the future the IETF and/or ECMA change the standard to 
accept comments or update the available JSON grammar and syntax, this package
will be updated accordingly.

If you as a JSON user feel strongly about incorporating comments into the
JSON grammar specification, i urge you to communicate with the IETF and ECMA
about the issue. I also implore you to NOT just go and start changing software
to accept what is not allowed by specification, just because you personally 
don't like it. 

Until then, just put your comments in a value field! It is not hard and 
works within the existing standards. It also allows everyone to properly
interoperate and exchange data, which was the whole point.

    {   "myobject"  : "valuable data",
        "another"   : 3.14159,                  "//" : "was that pi ?",
        "comment"   : "simple enough isn't it",
        "more data" : true,                     "" : "Another style of comment",
        "label_b"   : "something important"     }