|
c95f11b…
|
stephan
|
1 |
# JSON API: Hacker's Guide |
|
c95f11b…
|
stephan
|
2 |
([⬑JSON API Index](index.md)) |
|
c95f11b…
|
stephan
|
3 |
|
|
c95f11b…
|
stephan
|
4 |
Jump to: |
|
c95f11b…
|
stephan
|
5 |
|
|
c95f11b…
|
stephan
|
6 |
* [Before Committing Changes](#before-committing) |
|
c95f11b…
|
stephan
|
7 |
* [JSON C API](#json-c-api) |
|
c95f11b…
|
stephan
|
8 |
* [Reporting Errors](#reporting-errors) |
|
c95f11b…
|
stephan
|
9 |
* [Getting Command Arguments](#command-args) |
|
c95f11b…
|
stephan
|
10 |
* [Creating JSON Data](#creating-json) |
|
c95f11b…
|
stephan
|
11 |
* [Creating JSON Values](#creating-json-values) |
|
c95f11b…
|
stephan
|
12 |
* [Converting SQL Query Results to JSON](#query-to-json) |
|
c95f11b…
|
stephan
|
13 |
|
|
c95f11b…
|
stephan
|
14 |
This section will only be of interest to those wanting to work on the |
|
c95f11b…
|
stephan
|
15 |
Fossil/JSON code. That said... |
|
c95f11b…
|
stephan
|
16 |
|
|
c95f11b…
|
stephan
|
17 |
If you happen to hack on the code and find something worth noting here |
|
c95f11b…
|
stephan
|
18 |
for others, please feel free to expand this section. It will only |
|
c95f11b…
|
stephan
|
19 |
improve via feedback from those working on the code. |
|
c95f11b…
|
stephan
|
20 |
|
|
c95f11b…
|
stephan
|
21 |
--- |
|
c95f11b…
|
stephan
|
22 |
|
|
c95f11b…
|
stephan
|
23 |
<a id="before-committing"></a> |
|
c95f11b…
|
stephan
|
24 |
# Before Committing Changes... |
|
c95f11b…
|
stephan
|
25 |
|
|
c95f11b…
|
stephan
|
26 |
Because this code lives in the trunk, there are certain |
|
c95f11b…
|
stephan
|
27 |
guidelines which must be followed before committing any changes: |
|
c95f11b…
|
stephan
|
28 |
|
|
c95f11b…
|
stephan
|
29 |
1. Read the [checkin preparation list](/doc/trunk/www/checkin.wiki). |
|
c95f11b…
|
stephan
|
30 |
2. Changes to the files `src/json_*.*`, and its related support code |
|
c95f11b…
|
stephan
|
31 |
(e.g. `ajax/*.*`), may be made freely without affecting mainline |
|
c95f11b…
|
stephan
|
32 |
users. Changes to other files, unless they are trivial or made for |
|
c95f11b…
|
stephan
|
33 |
purposes outside the JSON API (e.g. an unrelated bug fix), must be |
|
c95f11b…
|
stephan
|
34 |
reviewed carefully before committing. When in doubt, create a branch |
|
c95f11b…
|
stephan
|
35 |
and post a request for a review. |
|
c95f11b…
|
stephan
|
36 |
3. The Golden Rule is: *do not break the trunk build*. |
|
c95f11b…
|
stephan
|
37 |
|
|
c95f11b…
|
stephan
|
38 |
|
|
c95f11b…
|
stephan
|
39 |
<a id="json-c-api"></a> |
|
c95f11b…
|
stephan
|
40 |
# JSON C API |
|
c95f11b…
|
stephan
|
41 |
|
|
c95f11b…
|
stephan
|
42 |
libcson, the underlying JSON API, is a separate project, included in |
|
73373b9…
|
stephan
|
43 |
fossil in "amalgamation" form: see `extsrc/cson_amalgamation.[ch]`. It has |
|
c95f11b…
|
stephan
|
44 |
thorough API docs and a good deal of information is in its wiki: |
|
c95f11b…
|
stephan
|
45 |
|
|
c95f11b…
|
stephan
|
46 |
[](https://fossil.wanderinghorse.net/wikis/cson/) |
|
c95f11b…
|
stephan
|
47 |
|
|
c95f11b…
|
stephan
|
48 |
In particular: |
|
c95f11b…
|
stephan
|
49 |
|
|
c95f11b…
|
stephan
|
50 |
[](https://fossil.wanderinghorse.net/wikis/cson/?page=CsonArchitecture) |
|
c95f11b…
|
stephan
|
51 |
|
|
c95f11b…
|
stephan
|
52 |
gives an overview of its architecture. Occasionally new versions of it |
|
c95f11b…
|
stephan
|
53 |
are pulled into the Fossil tree, but other developers generally need not |
|
c95f11b…
|
stephan
|
54 |
concern themselves with that. |
|
c95f11b…
|
stephan
|
55 |
|
|
73373b9…
|
stephan
|
56 |
(Trivia: the cson wiki's back-end is fossil using this very JSON API, |
|
73373b9…
|
stephan
|
57 |
living on top of a custom JavaScript+HTML5 application.) |
|
c95f11b…
|
stephan
|
58 |
|
|
c95f11b…
|
stephan
|
59 |
Only a small handful of low-level fossil routines actually input or |
|
c95f11b…
|
stephan
|
60 |
output JSON text (only for reading in POST data and sending the |
|
c95f11b…
|
stephan
|
61 |
response). In the C code we work with the higher-level JSON value |
|
c95f11b…
|
stephan
|
62 |
abstractions provided by cson (conceptually similar to an XML DOM). All |
|
c95f11b…
|
stephan
|
63 |
of the JSON-defined data types are supported, and we can construct JSON |
|
c95f11b…
|
stephan
|
64 |
output of near arbitrary complexity with the caveat that *cyclic data |
|
c95f11b…
|
stephan
|
65 |
structures are strictly forbidden*, and *will* cause memory corruption, |
|
c95f11b…
|
stephan
|
66 |
crashes, double free()'s, or other undefined behaviour. Because JSON |
|
c95f11b…
|
stephan
|
67 |
cannot, without client-specific semantic extensions to JSON, represent |
|
c95f11b…
|
stephan
|
68 |
cyclic structures, it is not anticipated that this will be a |
|
c95f11b…
|
stephan
|
69 |
problem/limitation when generating output for fossil. |
|
c95f11b…
|
stephan
|
70 |
|
|
c95f11b…
|
stephan
|
71 |
|
|
c95f11b…
|
stephan
|
72 |
|
|
c95f11b…
|
stephan
|
73 |
<a id="json-commands"></a> |
|
c95f11b…
|
stephan
|
74 |
# Architecture of JSON Commands |
|
c95f11b…
|
stephan
|
75 |
|
|
c95f11b…
|
stephan
|
76 |
In order to consolidate CLI/HTTP modes for JSON handling, this code |
|
c95f11b…
|
stephan
|
77 |
foregoes fossil's conventional command/path dispatching mechanism. Only |
|
c95f11b…
|
stephan
|
78 |
the top-most "json" command/path is dispatched directly by fossil's |
|
c95f11b…
|
stephan
|
79 |
core. The disadvantages of this are that we lose fossil's conventional |
|
c95f11b…
|
stephan
|
80 |
help text mechanism (which is based on code comments in the |
|
c95f11b…
|
stephan
|
81 |
command/path's dispatcher impl) and the ability to write abbreviated |
|
c95f11b…
|
stephan
|
82 |
command names in CLI mode ("json" itself may be abbreviated, but not the |
|
c95f11b…
|
stephan
|
83 |
subcommands). The advantages are that we can handle CLI/HTTP modes |
|
a3be0b8…
|
drh
|
84 |
almost identically (there are a couple of minor differences) by unifying |
|
c95f11b…
|
stephan
|
85 |
them under the same callback functions much more easily. |
|
c95f11b…
|
stephan
|
86 |
|
|
c95f11b…
|
stephan
|
87 |
The top-level "json" command/path uses its own dispatching mechanism |
|
c95f11b…
|
stephan
|
88 |
which uses either the path (in HTTP mode) or CLI positional arguments to |
|
c95f11b…
|
stephan
|
89 |
dispatch commands (stopping at the first "flag option" (e.g. -foo) in |
|
c95f11b…
|
stephan
|
90 |
CLI mode). The command handlers are simply callback functions which |
|
c95f11b…
|
stephan
|
91 |
return a cson\_value pointer (the C representation of an arbitrary JSON |
|
c95f11b…
|
stephan
|
92 |
value), representing the "payload" of the response (or NULL - not all |
|
c95f11b…
|
stephan
|
93 |
responses need a payload). On error these callbacks set the internal |
|
c95f11b…
|
stephan
|
94 |
JSON error state (detailed in a subsection below) and return NULL. The |
|
c95f11b…
|
stephan
|
95 |
top-level dispatcher then creates a response envelope and returns the |
|
c95f11b…
|
stephan
|
96 |
"payload" from the command (if any) to the caller. If a callback sets |
|
c95f11b…
|
stephan
|
97 |
the error state, the top-level dispatcher takes care to set the error |
|
c95f11b…
|
stephan
|
98 |
information in the response envelope. In summary: |
|
c95f11b…
|
stephan
|
99 |
|
|
c95f11b…
|
stephan
|
100 |
- The top-level dispatchers (`json_page_top()` and `json_cmd_top()`) |
|
c95f11b…
|
stephan
|
101 |
are called by fossil's core when the "json" command/path is called. |
|
c95f11b…
|
stephan
|
102 |
They initialize the JSON-mode global state, dispatch the requested |
|
c95f11b…
|
stephan
|
103 |
command, and handle the creation of the response envelope. They |
|
c95f11b…
|
stephan
|
104 |
prepare all the basic things which the individual subcommands need |
|
c95f11b…
|
stephan
|
105 |
in order to function. |
|
c95f11b…
|
stephan
|
106 |
- The command handlers (most are named `json_page_something()`) |
|
c95f11b…
|
stephan
|
107 |
implement the `fossil_json_f()` callback interface (see |
|
c95f11b…
|
stephan
|
108 |
[`src/json_detail.h`](/finfo/src/json_detail.h)). They are |
|
c95f11b…
|
stephan
|
109 |
responsible for permissions checking, setting any error state, and |
|
c95f11b…
|
stephan
|
110 |
passing back a payload (if needed - not all commands return a |
|
c95f11b…
|
stephan
|
111 |
payload). It is strictly forbidden for these callbacks to produce |
|
c95f11b…
|
stephan
|
112 |
any output on stdout/stderr, and doing so effectively corrupts the |
|
c95f11b…
|
stephan
|
113 |
out-bound JSON and HTTP headers. |
|
c95f11b…
|
stephan
|
114 |
|
|
c95f11b…
|
stephan
|
115 |
There is a wrench in all of that, however: the vast majority of fossil's |
|
c95f11b…
|
stephan
|
116 |
commands "fail fast" - they will `exit()` if they encounter an error. To |
|
c95f11b…
|
stephan
|
117 |
handle that, the fossil core error reporting routines have been |
|
c95f11b…
|
stephan
|
118 |
refactored a small bit to operate differently when we are running in |
|
c95f11b…
|
stephan
|
119 |
JSON mode. Instead of the conventional output, they generate a JSON |
|
c95f11b…
|
stephan
|
120 |
error response. In HTTP mode they exit with code 0 to avoid causing an |
|
c95f11b…
|
stephan
|
121 |
HTTP 500 error, whereas in CLI mode they will exit with a non-0 code. |
|
c95f11b…
|
stephan
|
122 |
Those routines still `exit()`, as in the conventional CLI/HTTP modes, but |
|
c95f11b…
|
stephan
|
123 |
they will exit differently. Because of this, it is perfectly fine for a |
|
c95f11b…
|
stephan
|
124 |
command handler to exit via one of fossil's conventional mechanisms |
|
c95f11b…
|
stephan
|
125 |
(e.g. `db_prepare()` can be fatal, and callbacks may call `fossil_panic()` |
|
c95f11b…
|
stephan
|
126 |
if they really want to). One exception is `fossil_exit()`, which does |
|
c95f11b…
|
stephan
|
127 |
_not_ generate any extra output and will `exit()` the app. In the JSON |
|
c95f11b…
|
stephan
|
128 |
API, as a rule of thumb, `fossil_exit()` is only used when we *want* a |
|
c95f11b…
|
stephan
|
129 |
failed request to cause an HTTP 500 error, and it is reserved for |
|
a3be0b8…
|
drh
|
130 |
allocation errors and similar truly catastrophic failures. That said... |
|
c95f11b…
|
stephan
|
131 |
libcson has been hacked to use `fossil_alloc()` and friends for memory |
|
c95f11b…
|
stephan
|
132 |
management, and those routines exit on error, so alloc error handling in |
|
c95f11b…
|
stephan
|
133 |
the JSON command handler code can afford to be a little lax (the |
|
c95f11b…
|
stephan
|
134 |
majority of *potential* errors clients get from the cson API have |
|
c95f11b…
|
stephan
|
135 |
allocation failure as their root cause). |
|
c95f11b…
|
stephan
|
136 |
|
|
c95f11b…
|
stephan
|
137 |
As a side-note: the vast majority (if not all) of the cson API calls are |
|
c95f11b…
|
stephan
|
138 |
"NULL-safe", meaning that will return an error code (or be a no-op) if |
|
c95f11b…
|
stephan
|
139 |
passed NULL arguments. e.g. the following chain of calls will not crash |
|
c95f11b…
|
stephan
|
140 |
if the value we're looking for does not exist, is-not-a String (see |
|
c95f11b…
|
stephan
|
141 |
`cson_value_get_string()` for important details), or if `myObj` is NULL: |
|
c95f11b…
|
stephan
|
142 |
|
|
c95f11b…
|
stephan
|
143 |
```c |
|
c95f11b…
|
stephan
|
144 |
const char * str = |
|
c95f11b…
|
stephan
|
145 |
cson_string_cstr( // get the C-string form of a cson_string |
|
c95f11b…
|
stephan
|
146 |
cson_value_get_string( // get its cson_string form |
|
c95f11b…
|
stephan
|
147 |
cson_object_get(myObj,"foo") // search for key in an Object |
|
c95f11b…
|
stephan
|
148 |
) |
|
c95f11b…
|
stephan
|
149 |
); |
|
c95f11b…
|
stephan
|
150 |
``` |
|
c95f11b…
|
stephan
|
151 |
|
|
c95f11b…
|
stephan
|
152 |
If `"foo"` is not found in `myObj` (or if `myObj` is NULL) then v will be |
|
c95f11b…
|
stephan
|
153 |
NULL, as opposed to stepping on a NULL pointer somewhere in that call |
|
c95f11b…
|
stephan
|
154 |
chain. |
|
c95f11b…
|
stephan
|
155 |
|
|
c95f11b…
|
stephan
|
156 |
Note that all cson JSON values except Arrays and Objects are *immutable* |
|
c95f11b…
|
stephan
|
157 |
- you cannot change a string's or number's value, for example. They also |
|
c95f11b…
|
stephan
|
158 |
use reference counting to manage ownership, as documented and |
|
c95f11b…
|
stephan
|
159 |
demonstrated on this page: |
|
c95f11b…
|
stephan
|
160 |
|
|
c95f11b…
|
stephan
|
161 |
[](https://fossil.wanderinghorse.net/wikis/cson/?page=TipsAndTricks) |
|
c95f11b…
|
stephan
|
162 |
|
|
c95f11b…
|
stephan
|
163 |
In short, after creating a new value you must eventually *either* add it |
|
c95f11b…
|
stephan
|
164 |
to a container (Object or Array) to transfer ownership *or* call |
|
c95f11b…
|
stephan
|
165 |
`cson_value_free()` to clean it up (exception: the Fossil/JSON command |
|
c95f11b…
|
stephan
|
166 |
callbacks *return* a value to transfer ownership to the dispatcher). |
|
c95f11b…
|
stephan
|
167 |
Sometimes it's more complex than that, but not normally. Any given value |
|
c95f11b…
|
stephan
|
168 |
may legally be stored in any number of containers (or multiple times |
|
c95f11b…
|
stephan
|
169 |
within one container), as long as *no cycles* are introduced (cycles |
|
c95f11b…
|
stephan
|
170 |
*will* cause undefined behaviour). Ownership is shared using reference |
|
c95f11b…
|
stephan
|
171 |
counting and the value will eventually be freed up when its last |
|
c95f11b…
|
stephan
|
172 |
remaining reference is freed (e.g. when the last container holding it is |
|
c95f11b…
|
stephan
|
173 |
cleaned up). For many examples of using cson in the context of fossil, |
|
c95f11b…
|
stephan
|
174 |
see the existing `json_page_xxx()` functions in `json_*.c`. |
|
c95f11b…
|
stephan
|
175 |
|
|
c95f11b…
|
stephan
|
176 |
<a id="reporting-errors"></a> |
|
c95f11b…
|
stephan
|
177 |
# Reporting Errors |
|
c95f11b…
|
stephan
|
178 |
|
|
c95f11b…
|
stephan
|
179 |
To report an error from a command callback, one abstractly needs to: |
|
c95f11b…
|
stephan
|
180 |
|
|
c95f11b…
|
stephan
|
181 |
- Set g.json.resultCode to one of the `FSL_JSON_E_xxx` values |
|
c95f11b…
|
stephan
|
182 |
(defined in [`src/json_detail.h`](/finfo/src/json_detail.h)). |
|
c95f11b…
|
stephan
|
183 |
- *Optionally* set `g.zErrMsg` to contain the (dynamically-allocated!) |
|
c95f11b…
|
stephan
|
184 |
error string to be sent to the client. If no error string is set |
|
c95f11b…
|
stephan
|
185 |
then a standard/generic string is used for the given error code. |
|
c95f11b…
|
stephan
|
186 |
- Clean up any resources created so far by the handler. |
|
c95f11b…
|
stephan
|
187 |
- Return NULL. If it returns non-NULL, the dispatcher will destroy the |
|
c95f11b…
|
stephan
|
188 |
value and not include it in the error response. |
|
c95f11b…
|
stephan
|
189 |
|
|
c95f11b…
|
stephan
|
190 |
That normally looks something like this: |
|
c95f11b…
|
stephan
|
191 |
|
|
c95f11b…
|
stephan
|
192 |
``` |
|
c95f11b…
|
stephan
|
193 |
if(!g.perm.Read){ |
|
c95f11b…
|
stephan
|
194 |
json_set_err(FSL_JSON_E_DENIED, "Requires 'o' permissions."); |
|
c95f11b…
|
stephan
|
195 |
return NULL; |
|
c95f11b…
|
stephan
|
196 |
} |
|
c95f11b…
|
stephan
|
197 |
``` |
|
c95f11b…
|
stephan
|
198 |
|
|
c95f11b…
|
stephan
|
199 |
`json_set_err()` is a variadic printf-like function, and can use the |
|
c95f11b…
|
stephan
|
200 |
printf extensions supported by mprintf() and friends (e.g. `%Q` and `%q`) |
|
c95f11b…
|
stephan
|
201 |
(but they are normally not needed in the context of JSON). If the error |
|
c95f11b…
|
stephan
|
202 |
string is NULL or empty then `json_err_cstr(errorCode)` is used to fetch |
|
c95f11b…
|
stephan
|
203 |
the standard/generic error string for the given code. |
|
c95f11b…
|
stephan
|
204 |
|
|
c95f11b…
|
stephan
|
205 |
When control returns to the top-level dispatching function it will check |
|
c95f11b…
|
stephan
|
206 |
`g.json.resultCode` and, if it is not 0, create an error response using |
|
c95f11b…
|
stephan
|
207 |
the `g.json.resultCode` and `g.zErrMsg` to construct the response's |
|
c95f11b…
|
stephan
|
208 |
`resultCode` and `resultText` properties. |
|
c95f11b…
|
stephan
|
209 |
|
|
c95f11b…
|
stephan
|
210 |
If a function wants to output an error and exit by itself, as opposed |
|
c95f11b…
|
stephan
|
211 |
to returning to the dispatcher, then it must behave slightly |
|
c95f11b…
|
stephan
|
212 |
differently. See the docs for `json_err()` (in |
|
c95f11b…
|
stephan
|
213 |
[`src/json.c`](/finfo/src/json.c)) for details, and search that file |
|
c95f11b…
|
stephan
|
214 |
for various examples of its usage. It is also used by fossil's core |
|
c95f11b…
|
stephan
|
215 |
error-reporting APIs, e.g. `fossil_panic()` (defined in [`src/main.c`](/finfo/src/main.c)). |
|
c95f11b…
|
stephan
|
216 |
That said, it would be "highly unusual" for a callback to need to do |
|
c95f11b…
|
stephan
|
217 |
this - it is *far* simpler (and more consistent/reliable) to set the |
|
c95f11b…
|
stephan
|
218 |
error state and return to the dispatcher. |
|
c95f11b…
|
stephan
|
219 |
|
|
c95f11b…
|
stephan
|
220 |
<a id="command-args"></a> |
|
c95f11b…
|
stephan
|
221 |
# Getting Command Arguments |
|
c95f11b…
|
stephan
|
222 |
|
|
c95f11b…
|
stephan
|
223 |
Positional parameters can be fetched usinig `json_command_arg(N)`, where |
|
c95f11b…
|
stephan
|
224 |
N is the argument position, with position 0 being the "json" |
|
c95f11b…
|
stephan
|
225 |
command/path. In CLI mode positional arguments have their obvious |
|
c95f11b…
|
stephan
|
226 |
meaning. In HTTP mode the request path (or the "command" request |
|
c95f11b…
|
stephan
|
227 |
property) is used to build up the "command path" instead. For example: |
|
c95f11b…
|
stephan
|
228 |
|
|
c95f11b…
|
stephan
|
229 |
CLI: `fossil json a b c` |
|
c95f11b…
|
stephan
|
230 |
|
|
c95f11b…
|
stephan
|
231 |
HTTP: `/json/a/b/c` |
|
c95f11b…
|
stephan
|
232 |
|
|
c95f11b…
|
stephan
|
233 |
HTTP POST or CLI with `--json-input`: /json with POSTed envelope |
|
c95f11b…
|
stephan
|
234 |
`{"command": "a/b/c" …}` |
|
c95f11b…
|
stephan
|
235 |
|
|
c95f11b…
|
stephan
|
236 |
Those will have identical "command paths," and `json_command_path(2)` |
|
c95f11b…
|
stephan
|
237 |
would return the "b" part. |
|
c95f11b…
|
stephan
|
238 |
|
|
c95f11b…
|
stephan
|
239 |
Caveat: a limitation of this support is that all CLI flags must come |
|
c95f11b…
|
stephan
|
240 |
*after* all *non-flag* positional arguments (e.g. file names or |
|
c95f11b…
|
stephan
|
241 |
subcommand names). Any argument starting with a dash ("-") is considered |
|
c95f11b…
|
stephan
|
242 |
by this code to be a potential "flag" argument, and all arguments after |
|
c95f11b…
|
stephan
|
243 |
it are ignored (because the generic handling cannot know if a flag |
|
c95f11b…
|
stephan
|
244 |
requires an argument, which changes how the rest of the arguments need |
|
c95f11b…
|
stephan
|
245 |
to be interpreted). |
|
c95f11b…
|
stephan
|
246 |
|
|
c95f11b…
|
stephan
|
247 |
To get named parameters, there are several approaches (plus some special |
|
c95f11b…
|
stephan
|
248 |
cases). Named parameters can normally come from any of the following |
|
c95f11b…
|
stephan
|
249 |
sources: |
|
c95f11b…
|
stephan
|
250 |
|
|
c95f11b…
|
stephan
|
251 |
- CLI arguments, e.g. `--foo bar` |
|
c95f11b…
|
stephan
|
252 |
- GET parameters: `/json/...?foo=bar` |
|
c95f11b…
|
stephan
|
253 |
- Properties of the POST envelope |
|
c95f11b…
|
stephan
|
254 |
- Properties of the `POST.payload` object (if any). |
|
c95f11b…
|
stephan
|
255 |
|
|
c95f11b…
|
stephan
|
256 |
To try to simplify the guessing process the API has a number of |
|
c95f11b…
|
stephan
|
257 |
functions which behave ever so slightly differently. A summary: |
|
c95f11b…
|
stephan
|
258 |
|
|
c95f11b…
|
stephan
|
259 |
- `json_getenv()` and `json_getenv_TYPE()` search the so-called "JSON |
|
c95f11b…
|
stephan
|
260 |
environment," which is a superset of the GET/POST/`POST.payload` (if |
|
c95f11b…
|
stephan
|
261 |
`POST.payload` is-a Object). |
|
c95f11b…
|
stephan
|
262 |
- `json_find_option_TYPE()`: searches the CLI args (only when in CLI |
|
c95f11b…
|
stephan
|
263 |
mode) and the JSON environment. |
|
a3be0b8…
|
drh
|
264 |
- The use of fossil's `P()` and `PD()` macros is discouraged in JSON |
|
c95f11b…
|
stephan
|
265 |
callbacks because they can only handle String data from the CLI or |
|
c95f11b…
|
stephan
|
266 |
GET parameters (not POST/`POST.payload`). (Note that `P()` and `PD()` |
|
c95f11b…
|
stephan
|
267 |
*normally* also handle POSTed keys, but they only "see" values |
|
c95f11b…
|
stephan
|
268 |
posted as form-urlencoded fields, and not JSON format.) |
|
c95f11b…
|
stephan
|
269 |
- `find_option()` (from `src/main.c`) "should" also be avoided in |
|
c95f11b…
|
stephan
|
270 |
JSON API handlers because it removes flag from the g.argv |
|
c95f11b…
|
stephan
|
271 |
arguments list. That said, the JSON API does use `find_option()` in |
|
c95f11b…
|
stephan
|
272 |
several of its option-finding convenience wrappers. |
|
c95f11b…
|
stephan
|
273 |
|
|
c95f11b…
|
stephan
|
274 |
For example code: the existing command callbacks demonstrate all kinds |
|
c95f11b…
|
stephan
|
275 |
of uses and the various styles of parameter/option inspection. Check out |
|
c95f11b…
|
stephan
|
276 |
any of the functions named `json_page_SOMETHING()`. |
|
c95f11b…
|
stephan
|
277 |
|
|
c95f11b…
|
stephan
|
278 |
<a href="creating-json"></a> |
|
c95f11b…
|
stephan
|
279 |
# Creating JSON Data |
|
c95f11b…
|
stephan
|
280 |
|
|
c95f11b…
|
stephan
|
281 |
<a href="creating-json-values"></a> |
|
c95f11b…
|
stephan
|
282 |
## Creating JSON Values |
|
c95f11b…
|
stephan
|
283 |
|
|
c95f11b…
|
stephan
|
284 |
cson has a fairly rich API for creating and manipulating the various |
|
a3be0b8…
|
drh
|
285 |
JSON-defined value types. For a detailed overview and demonstration I |
|
c95f11b…
|
stephan
|
286 |
recommend reading: |
|
c95f11b…
|
stephan
|
287 |
|
|
c95f11b…
|
stephan
|
288 |
[](https://fossil.wanderinghorse.net/wikis/cson/?page=HowTo) |
|
c95f11b…
|
stephan
|
289 |
|
|
c95f11b…
|
stephan
|
290 |
That said, the Fossil/JSON API has several convenience wrappers to save |
|
c95f11b…
|
stephan
|
291 |
a few bytes of typing: |
|
c95f11b…
|
stephan
|
292 |
|
|
c95f11b…
|
stephan
|
293 |
- `json_new_string("foo")` is easier to use than |
|
c95f11b…
|
stephan
|
294 |
`cson_value_new_string("foo", 3)`, and |
|
c95f11b…
|
stephan
|
295 |
`json_new_string_f("%s","foo")` is more flexible. |
|
c95f11b…
|
stephan
|
296 |
- `json_new_int()` is easier to type than `cson_value_new_integer()`. |
|
c95f11b…
|
stephan
|
297 |
- `cson_output_Blob()` and `cson_parse_Blob()` can write/read JSON |
|
c95f11b…
|
stephan
|
298 |
to/from fossil `Blob`-type objects. |
|
c95f11b…
|
stephan
|
299 |
|
|
c95f11b…
|
stephan
|
300 |
It also provides several lower-level JSON features which aren't of |
|
c95f11b…
|
stephan
|
301 |
general utility but provide necessary functionality for some of the |
|
c95f11b…
|
stephan
|
302 |
framework-level code (e.g. `cson_data_dest_cgi()`), which is only used |
|
c95f11b…
|
stephan
|
303 |
by the deepest of the JSON internals). |
|
c95f11b…
|
stephan
|
304 |
|
|
c95f11b…
|
stephan
|
305 |
|
|
c95f11b…
|
stephan
|
306 |
<a href="query-to-json"></a> |
|
c95f11b…
|
stephan
|
307 |
## Converting SQL Query Results to JSON |
|
c95f11b…
|
stephan
|
308 |
|
|
c95f11b…
|
stephan
|
309 |
The `cson_sqlite3_xxx()` family of functions convert `sqlite3_stmt` rows |
|
c95f11b…
|
stephan
|
310 |
to Arrays or Objects, or convert single columns to a JSON-compatible |
|
c95f11b…
|
stephan
|
311 |
form. See `json_stmt_to_array_of_obj()`, |
|
c95f11b…
|
stephan
|
312 |
`json_stmt_to_array_of_array()` (both in `src/json.c`), and |
|
c95f11b…
|
stephan
|
313 |
`cson_sqlite3_column_to_value()` and friends (in |
|
73373b9…
|
stephan
|
314 |
`extsrc/cson_amalgamation.h`). They work in an intuitive way for numeric |
|
a3be0b8…
|
drh
|
315 |
types, but they optimistically/naively *assume* that any fields of type |
|
c95f11b…
|
stephan
|
316 |
TEXT or BLOB are actually UTF8 data, and treat them as such. cson's |
|
c95f11b…
|
stephan
|
317 |
string class only handles UTF8 data and it is semantically illegal to |
|
c95f11b…
|
stephan
|
318 |
feed them anything but UTF8. Violating this will likely result in |
|
a3be0b8…
|
drh
|
319 |
down-stream errors (e.g. when emitting the JSON string output). **The |
|
c95f11b…
|
stephan
|
320 |
moral of this story is:** *do not use these APIs to fetch binary data*. |
|
c95f11b…
|
stephan
|
321 |
JSON doesn't do binary and the `cson_string` class does not |
|
c95f11b…
|
stephan
|
322 |
protect itself against clients feeding it non-UTF8 data. |
|
c95f11b…
|
stephan
|
323 |
|
|
c95f11b…
|
stephan
|
324 |
Here's a basic example of using these features: |
|
c95f11b…
|
stephan
|
325 |
|
|
c95f11b…
|
stephan
|
326 |
```c |
|
c95f11b…
|
stephan
|
327 |
Stmt q = empty_Stmt; |
|
c95f11b…
|
stephan
|
328 |
cson_value * rows = NULL; |
|
c95f11b…
|
stephan
|
329 |
db_prepare(&q, "SELECT a AS a, b AS b, c AS c FROM foo"); |
|
c95f11b…
|
stephan
|
330 |
rows = json_stmt_to_array_of_obj( &sql, NULL ); |
|
c95f11b…
|
stephan
|
331 |
db_finalize(&q); |
|
c95f11b…
|
stephan
|
332 |
// side note: if db_prepare()/finalize() fail (==they exit()) |
|
c95f11b…
|
stephan
|
333 |
// then a JSON-format error reponse will be generated. |
|
c95f11b…
|
stephan
|
334 |
``` |
|
c95f11b…
|
stephan
|
335 |
|
|
c95f11b…
|
stephan
|
336 |
On success (and if there were results), `rows` is now an Array value, |
|
c95f11b…
|
stephan
|
337 |
each entry of which contains an Object containing the fields (key/value |
|
c95f11b…
|
stephan
|
338 |
pairs) of each row. `json_stmt_to_array_of_array()` returns each row |
|
c95f11b…
|
stephan
|
339 |
as an Array containing the column values (with no column name |
|
c95f11b…
|
stephan
|
340 |
information). |
|
c95f11b…
|
stephan
|
341 |
|
|
c95f11b…
|
stephan
|
342 |
**Note the seemingly superfluous use of the "AS" clause in the above |
|
c95f11b…
|
stephan
|
343 |
SQL.** Having them is actually significant! If a query does *not* use AS |
|
c95f11b…
|
stephan
|
344 |
clauses, the row names returned by the db driver *might* be different |
|
c95f11b…
|
stephan
|
345 |
than they appear in the query (this is documented behaviour of sqlite3). |
|
c95f11b…
|
stephan
|
346 |
Because the JSON API needs to return stable field names, we need to use |
|
c95f11b…
|
stephan
|
347 |
AS clauses to be guaranteed that the db driver will return the column |
|
c95f11b…
|
stephan
|
348 |
names we want. Note that the AS clause is often used to translate column |
|
c95f11b…
|
stephan
|
349 |
names into something more JSON-conventional or user-friendly, e.g. |
|
c95f11b…
|
stephan
|
350 |
"SELECT cap AS capabilities...". Alternately, we can convert the |
|
c95f11b…
|
stephan
|
351 |
individual `sqlite3_stmt` column values to JSON using |
|
a3be0b8…
|
drh
|
352 |
`cson_sqlite3_column_to_value()`, without referring directly to the |
|
c95f11b…
|
stephan
|
353 |
db-reported column name. |