JSON Parser : Documentation migration

This commit migrates the JSON overview documentation that
was recently added from Markdown to Doxygen.
This commit is contained in:
Andrew Lindesay 2017-05-26 21:04:56 +12:00
parent ec6735b596
commit f943fa465c
5 changed files with 223 additions and 110 deletions

View File

@ -1,109 +0,0 @@
# JSON
[JSON](http://www.json.org/) is a simple textual description of a data structure. An example of some JSON would be;
```
[ "apple", "orange", { "drink": "tonic water", "count" : 123 } ]
```
## Parsing
### Generic In-Memory Model
For some applications, parsing to an in-memory data structure is ideal. In such cases, the ```BJson``` class provides static methods for parsing a block of JSON data into a ```BMessage``` object.
#### BMessage Structure
The ```BMessage``` class has the ability to carry a collection of key-value pairs. In the case of a JSON object type, the key-value pairs correlate to the JSON object. In the case of a JSON array type, the key-value pairs are the index of the elements in the JSON array represented as strings.
For example, the following JSON array...
```
[ "a", "b", "c" ]
```
...would be represented by the following ```BMessage```;
|Key|Value|
|---|---|
|"0"|"a"|
|"1"|"b"|
|"2"|"c"|
A JSON object that, in its entirety, consists of a non-collection type such as a simple string or a boolean is not able to be represented by a ```BMessage```; at the top level there must be an array or an object.
### Streaming
Streaming is useful in many situations;
* where handling the parsed data is easier to undertake as a stream of events
* where the quantity of input or output data could be non-trivial and holding that quantity of material in memory is undesirable
* where being able to start processing a stream of data before the entire payload has arrived is desirable
This architecture is sometimes known as an event-based parser or a "SAX" parser.
The ```BJson``` class provides a static method that accepts a stream of JSON data in the form of a ```BDataIO``` and a ```BJsonEventListener``` sub-class. As each token is processed from the stream, it will provided to the listener. The listener must implement three callback methods to handle the JSON parsing;
|Method|Description|
|---|---|
|Handle(..)|Provides JSON events to the listener|
|HandleError(..)|Signals parse or processing errors to the listener|
|Complete(..)|Informs the listener that parsing has completed|
Events are embodied in instances of the ```BJsonEvent``` class and each of these has a type. Sample example types are;
* B_JSON_STRING
* B_JSON_OBJECT_START
* B_JSON_TRUE
In this way, the listener is able to interpret the incoming stream of data as JSON and handle it in some way.
The following JSON...
```
{"color": "red", "alpha": 0.6}
```
Would yield the following stream of events;
|Event Type|Event Data|
|---|---|
|B_JSON_OBJECT_START|-|
|B_JSON_OBJECT_NAME|"color"|
|B_JSON_STRING|"red"|
|B_JSON_OBJECT_NAME|"alpha"|
|B_JSON_NUMBER|0.6|
|B_JSON_OBJECT_END|-|
#### Number Handling
The JSON number literal format does not specify a numeric type such as ```int32``` or ```double```. To cope with the widest range of possibilities, the ```B_JSON_NUMBER``` event type captures the content as a string and then the ```BJsonEvent``` object is able to provide the original string for specific handling as well as convenient accessors for parsing to ```double``` or ```int64``` types. This provides a high level of flexibility for the client.
#### Stacked Listeners
One implementation approach for the listener used to read a data-transfer-object (DTO) is to create "sub-listeners" that mirror the structure of the JSON.
In the following example, a nested data structure is being parsed.
![Stacked Listeners](stacked-listeners.svg)
A primary-listener is employed called ```ColorGradientsListener```. The primary-listener accepts JSON parse events and will relay them to a sub-listener. The sub-listener is implemented to specifically deal with one tier of the inbound data. The sub-listeners are structured in a stack where the sub-listener at the head of the stack has a pointer to it's parent. The primary-listener maintains a pointer to the current head of the stack and will direct events to that sub-listener.
In response to events, the sub-listener can take-up the data, pop itself from the stack or push additional sub-listeners from the stack.
The same approach has been used in the following classes in a more generic manner;
* BJsonTextWriter
* BJsonMessageWriter
The intention with this approach is that the structure of the event handling code in the sub-listeners mirrors that of the data-structure being parsed. Hopefully this makes creating the filling of a specific data-model easier even when very specific behaviours are required.
From a schema of the data structure it is _probably_ also possible to create these sub-listeners and in this way automatically generate the C++ parse code as event listeners.
## Writing
In order to render a data-structure as JSON data, the opposite occurs; events are emitted by the client software into a class ```BJsonTextWriter```. This class supports public methods such as ```WriteFalse()```, ```WriteObjectStart()``` and ```WriteString(...)``` that control the outbound JSON stream.
### End to End
Because ```BJsonTextWriter``` is accepting JSON parse events, it is also a ```JsonEventListener``` and so can be used as a listener with the stream parsing; producing JSON output from JSON input. The output will however not include inbound whitespace because whitespace is not grammatically significant in JSON.

View File

@ -784,6 +784,7 @@ INPUT = . \
midi2 \
net \
posix \
shared \
storage \
support \
translation \
@ -905,7 +906,8 @@ IMAGE_PATH = . \
keyboard \
midi2/images \
net/images \
storage/images
storage/images \
shared/images
# The INPUT_FILTER tag can be used to specify a program that doxygen should
# invoke to filter for each input file. Doxygen will invoke the filter program

View File

@ -61,6 +61,7 @@
- \ref drivers
- \ref keyboard
- \ref json
*/
///// Define main kits /////
@ -610,4 +611,7 @@ snooze_until(time - Latency(), B_SYSTEM_TIMEBASE);
/*!
\defgroup drivers Device Drivers
\defgroup json Json Handling
\brief Provides for parsing and writing of data in Json encoding.
*/

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

216
docs/user/shared/json.dox Normal file
View File

@ -0,0 +1,216 @@
/*!
\page json Json Handling
<a href="http://www.json.org/">JSON</a> is a simple textual description of a
data structure. An example of some JSON would be;
\code
[ "apple", "orange", { "drink": "tonic water", "count" : 123 } ]
\endcode
This example is a list that contains two strings followed by an "object". The
term object refers to a construct akin to a "dictionary" or a "map". It is also
possible for top-level objects to be non-collection types such as strings. The
following is also valid JSON;
\code
"fijoa"
\endcode
This page details how Haiku provides facilities for both parsing as well as
writing data encoded as Json.
\section parsing-in-memory-model Parsing with Generic In-Memory Model
For some applications, parsing to an in-memory data structure is ideal. In
such cases, the \c BJson class provides static methods for parsing a block
of JSON data into a \c BMessage object. The application logic is then able to
introspect the \c BMessage to obtain values.
\subsection bmessage-structure BMessage Structure
The \c BMessage class has the ability to carry a collection of key-value pairs.
In the case of a Json object type, the key-value pairs correlate to a JSON
object or array. In the case of a JSON array type, the key-value pairs are
the index of the elements in the JSON array represented as strings.
For example, the following JSON array...
\code
[ "a", "b", "c" ]
\endcode
...would be represented by the following \c BMessage ;
<table>
<tr>
<th>Key</th>
<th>Value</th>
</tr>
<tr>
<td>\c "0"</td>
<td>\c "a"</td>
</tr>
<tr>
<td>\c "1"</td>
<td>\c "b"</td>
</tr>
<tr>
<td>\c "2"</td>
<td>\c "c"</td>
</tr>
</table>
A Json object that, in its entirety, consists of a non-collection type such as
a simple string or a boolean is not able to be represented by a \c BMessage ;
at the top level there must be an array or an object for the parse to be
successful.
\section parsing-streaming Stream-based Parsing
Streaming is useful in many situations;
- where handling the parsed data is easier to undertake as a stream of
events
- where the quantity of input or output data could be non-trivial and
holding that quantity of material in memory is undesirable
- where being able to start processing a stream of data before the entire
payload has arrived is desirable
This architecture is sometimes known as an event-based parser or a "SAX" parser.
The \c BJson class provides a static method that accepts a stream of Json
data in the form of a \c BDataIO. A \c BJsonEventListener sub-class is also
supplied and as each Json token is read-in from the stream, it will be
provided to the listener. The listener must implement three callback methods
to handle the Json tokens;
<table>
<tr>
<th>Method</th>
<th>Description</th>
</tr>
<tr>
<td>\c Handle(..)</td>
<td>Provides JSON events to the listener</td>
</tr>
<tr>
<td>\c HandleError(..)</td>
<td>Signals parse or processing errors to the listener</td>
</tr>
<tr>
<td>\c Complete(..)</td>
<td>Informs the listener that parsing has completed</td>
</tr>
</table>
Events are embodied in instances of the \c BJsonEvent class and each of
these has a type. Example types are;
- \c B_JSON_STRING
- \c B_JSON_OBJECT_START
- \c B_JSON_TRUE
In this way, the listener is able to interpret the incoming stream of data as
Json and handle it in some way.
The following Json...
\code
{"color": "red", "alpha": 0.6}
\endcode
Would yield the following stream of events;
<table>
<tr>
<th>Event Type</th>
<th>Event Data</th>
</tr>
<tr>
<td>\c B_JSON_OBJECT_START</td>
<td>-</td>
</tr>
<tr>
<td>\c B_JSON_OBJECT_NAME</td>
<td>"color"</td>
</tr>
<tr>
<td>\c B_JSON_STRING</td>
<td>"red"</td>
</tr>
<tr>
<td>\c B_JSON_OBJECT_NAME</td>
<td>"alpha"</td>
</tr>
<tr>
<td>\c B_JSON_NUMBER</td>
<td>0.6</td>
</tr>
<tr>
<td>\c B_JSON_OBJECT_END</td>
<td>-</td>
</tr>
</table>
\subsection parsing-streaming-numbers Number Handling
The Json number literal format does not specify a numeric type such as \c int32
or \c double. To cope with the widest range of possibilities, the
\c B_JSON_NUMBER event type captures the content as a string and then the
\c BJsonEvent object is able to provide the original string for specific
handling as well as convenient accessors for parsing to \c double or \c int64
types. This provides a high level of flexibility for the client.
\subsection parsing-streaming-stacked-listeners Stacked Listeners
One implementation approach for a listener implement that might be used to
read a data-transfer-object (DTO) is to create "sub-listeners" that mirror
the structure of the Json data.
In the following example, a nested data structure is being parsed.
\image html stacked-listeners.svg
A primary-listener is employed called \c ColorGradientsListener. The
primary-listener accepts Json parse events and will relay them to a
sub-listener. The sub-listener is implemented to specifically deal with one
tier of the inbound data. The sub-listeners are structured in a stack where
the sub-listener at the head of the stack has a pointer to it's parent. The
primary-listener maintains a pointer to the current head of the stack and will
direct events to that sub-listener.
In response to events, the sub-listener can take-up the data, pop itself from
the stack or push additional sub-listeners from the stack.
The same approach has been used in the following classes in a more generic
manner;
- \c BJsonTextWriter
- \c BJsonMessageWriter
The intention with this approach is that the structure of the event handling
code in the sub-listeners mirrors that of the data-structure being parsed.
Hopefully this makes creating the filling of a specific data-model easier even
when very specific behaviours are required.
From a schema of the data structure it is probably also possible to create
these sub-listeners and in this way automatically generate the C++ parse
code as event listeners.
\section writing Writing
In order to render a data-structure as textual Json data, the opposite flow
occurs; events are emitted by the client software into a class
\c BJsonTextWriter. This class supports public methods such as
\c WriteFalse() , \c WriteObjectStart() and \c WriteString(...) that control
the outbound Json stream.
\section end-to-end End to End
Because \c BJsonTextWriter is accepting JSON parse events, it is also a
\c JsonEventListener and so can be used as a listener with the stream parsing;
producing Json output from Json input. The output will however not include
inbound whitespace because whitespace is not grammatically significant in Json.
*/