Unify unsigned integer usage in the API
[jansson.git] / doc / tutorial.rst
1 .. _tutorial:
2
3 ********
4 Tutorial
5 ********
6
7 .. highlight:: c
8
9 In this tutorial, we create a program that fetches the latest commits
10 of a repository in GitHub_ over the web. One of the response formats
11 supported by `GitHub API`_ is JSON, so the result can be parsed using
12 Jansson.
13
14 To stick to the the scope of this tutorial, we will only cover the the
15 parts of the program related to handling JSON data. For the best user
16 experience, the full source code is available:
17 :download:`github_commits.c`. To compile it (on Unix-like systems with
18 gcc), use the following command::
19
20     gcc -o github_commits github_commits.c -ljansson -lcurl
21
22 libcurl_ is used to communicate over the web, so it is required to
23 compile the program.
24
25 The command line syntax is::
26
27     github_commits USER REPOSITORY
28
29 ``USER`` is a GitHub user ID and ``REPOSITORY`` is the repository
30 name. Please note that the GitHub API is rate limited, so if you run
31 the program too many times within a short period of time, the sever
32 starts to respond with an error.
33
34 .. _GitHub: http://github.com/
35 .. _GitHub API: http://develop.github.com/
36 .. _libcurl: http://curl.haxx.se/
37
38
39 .. _tutorial-github-commits-api:
40
41 The GitHub Commits API
42 ======================
43
44 The `GitHub commits API`_ is used by sending HTTP requests to URLs
45 starting with ``http://github.com/api/v2/json/commits/``. Our program
46 only lists the latest commits, so the rest of the URL is
47 ``list/USER/REPOSITORY/BRANCH``, where ``USER``, ``REPOSITORY`` and
48 ``BRANCH`` are the GitHub user ID, the name of the repository, and the
49 name of the branch whose commits are to be listed, respectively.
50
51 GitHub responds with a JSON object of the following form:
52
53 .. code-block:: none
54
55     {
56         "commits": [
57             {
58                 "id": "<the commit ID>",
59                 "message": "<the commit message>",
60                 <more fields, not important to this tutorial>
61             },
62             {
63                 "id": "<the commit ID>",
64                 "message": "<the commit message>",
65                 <more fields, not important to this tutorial>
66             },
67             <more commits...>
68         ]
69     }
70
71 In our program, the HTTP request is sent using the following
72 function::
73
74     static char *request(const char *url);
75
76 It takes the URL as a parameter, preforms a HTTP GET request, and
77 returns a newly allocated string that contains the response body. If
78 the request fails, an error message is printed to stderr and the
79 return value is *NULL*. For full details, refer to :download:`the code
80 <github_commits.c>`, as the actual implementation is not important
81 here.
82
83 .. _GitHub commits API: http://develop.github.com/p/commits.html
84
85 .. _tutorial-the-program:
86
87 The Program
88 ===========
89
90 First the includes::
91
92     #include <string.h>
93     #include <jansson.h>
94
95 Like all the programs using Jansson, we need to include
96 :file:`jansson.h`.
97
98 The following definitions are used to build the GitHub commits API
99 request URL::
100
101    #define URL_FORMAT   "http://github.com/api/v2/json/commits/list/%s/%s/master"
102    #define URL_SIZE     256
103
104 The following function is used when formatting the result to find the
105 first newline in the commit message::
106
107     /* Return the offset of the first newline in text or the length of
108        text if there's no newline */
109     static int newline_offset(const char *text)
110     {
111         const char *newline = strchr(text, '\n');
112         if(!newline)
113             return strlen(text);
114         else
115             return (int)(newline - text);
116     }
117
118 The main function follows. In the beginning, we first declare a bunch
119 of variables and check the command line parameters::
120
121     size_t i;
122     char *text;
123     char url[URL_SIZE];
124
125     json_t *root;
126     json_error_t error;
127     json_t *commits;
128
129     if(argc != 3)
130     {
131         fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]);
132         fprintf(stderr, "List commits at USER's REPOSITORY.\n\n");
133         return 2;
134     }
135
136 Then we build the request URL using the user and repository names
137 given as command line parameters::
138
139     snprintf(url, URL_SIZE, URL_FORMAT, argv[1], argv[2]);
140
141 This uses the ``URL_SIZE`` and ``URL_FORMAT`` constants defined above.
142 Now we're ready to actually request the JSON data over the web::
143
144     text = request(url);
145     if(!text)
146         return 1;
147
148 If an error occurs, our function ``request`` prints the error and
149 returns *NULL*, so it's enough to just return 1 from the main
150 function.
151
152 Next we'll call :cfunc:`json_loads()` to decode the JSON text we got
153 as a response::
154
155     root = json_loads(text, &error);
156     free(text);
157
158     if(!root)
159     {
160         fprintf(stderr, "error: on line %d: %s\n", error.line, error.text);
161         return 1;
162     }
163
164 We don't need the JSON text anymore, so we can free the ``text``
165 variable right after decoding it. If :cfunc:`json_loads()` fails, it
166 returns *NULL* and sets error information to the :ctype:`json_error_t`
167 structure given as the second parameter. In this case, our program
168 prints the error information out and returns 1 from the main function.
169
170 Now we're ready to extract the data out of the decoded JSON response.
171 The structure of the response JSON was explained in section
172 :ref:`tutorial-github-commits-api`.
173
174 First, we'll extract the ``commits`` array from the JSON response::
175
176     commits = json_object_get(root, "commits");
177     if(!json_is_array(commits))
178     {
179         fprintf(stderr, "error: commits is not an array\n");
180         return 1;
181     }
182
183 This is the array that contains objects describing latest commits in
184 the repository. We check that the returned value really is an array.
185 If the key ``commits`` doesn't exist, :cfunc:`json_object_get()`
186 returns *NULL*, but :cfunc:`json_is_array()` handles this case, too.
187
188 Then we proceed to loop over all the commits in the array::
189
190     for(i = 0; i < json_array_size(commits); i++)
191     {
192         json_t *commit, *id, *message;
193         const char *message_text;
194
195         commit = json_array_get(commits, i);
196         if(!json_is_object(commit))
197         {
198             fprintf(stderr, "error: commit %d is not an object\n", i + 1);
199             return 1;
200         }
201     ...
202
203 The function :cfunc:`json_array_size()` returns the size of a JSON
204 array. First, we again declare some variables and then extract the
205 i'th element of the ``commits`` array using :cfunc:`json_array_get()`.
206 We also check that the resulting value is a JSON object.
207
208 Next we'll extract the commit ID and commit message, and check that
209 they both are JSON strings::
210
211         id = json_object_get(commit, "id");
212         if(!json_is_string(id))
213         {
214             fprintf(stderr, "error: commit %d: id is not a string\n", i + 1);
215             return 1;
216         }
217
218         message = json_object_get(commit, "message");
219         if(!json_is_string(message))
220         {
221             fprintf(stderr, "error: commit %d: message is not a string\n", i + 1);
222             return 1;
223         }
224     ...
225
226 And finally, we'll print the first 8 characters of the commit ID and
227 the first line of the commit message. A C-style string is extracted
228 from a JSON string using :cfunc:`json_string_value()`::
229
230         message_text = json_string_value(message);
231         printf("%.8s %.*s\n",
232                json_string_value(id),
233                newline_offset(message_text),
234                message_text);
235     }
236
237 After sending the HTTP request, we decoded the JSON text using
238 :cfunc:`json_loads()`, remember? It returns a *new reference* to the
239 JSON value it decodes. When we're finished with the value, we'll need
240 to decrease the reference count using :cfunc:`json_decref()`. This way
241 Jansson can release the resources::
242
243     json_decref(root);
244     return 0;
245
246 For a detailed explanation of reference counting in Jansson, see
247 :ref:`apiref-reference-count` in :ref:`apiref`.
248
249 The program's ready, let's test it and view the latest commits in
250 Jansson's repository::
251
252     $ ./github_commits akheron jansson
253     86dc1d62 Fix indentation
254     b67e130f json_dumpf: Document the output shortage on error
255     4cd77771 Enhance handling of circular references
256     79009e62 json_dumps: Close the strbuffer if dumping fails
257     76999799 doc: Fix a small typo in apiref
258     22af193a doc/Makefile.am: Remove *.pyc in clean
259     951d091f Make integer, real and string mutable
260     185e107d Don't use non-portable asprintf()
261     ca7703fb Merge branch '1.0'
262     12cd4e8c jansson 1.0.4
263     <etc...>
264
265
266 Conclusion
267 ==========
268
269 In this tutorial, we implemented a program that fetches the latest
270 commits of a GitHub repository using the GitHub commits API. Jansson
271 was used to decode the JSON response and to extract the commit data.
272
273 This tutorial only covered a small part of Jansson. For example, we
274 did not create or manipulate JSON values at all. Proceed to
275 :ref:`apiref` to explore all features of Jansson.