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
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::
20 gcc -o github_commits github_commits.c -ljansson -lcurl
22 libcurl_ is used to communicate over the web, so it is required to
25 The command line syntax is::
27 github_commits USER REPOSITORY
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.
34 .. _GitHub: http://github.com/
35 .. _GitHub API: http://develop.github.com/
36 .. _libcurl: http://curl.haxx.se/
39 .. _tutorial-github-commits-api:
41 The GitHub Commits API
42 ======================
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. The
50 following definitions are used to build the request URL::
52 #define URL_FORMAT "http://github.com/api/v2/json/commits/list/%s/%s/master"
55 GitHub responds with a JSON object of the following form:
62 "id": "<the commit ID>",
63 "message": "<the commit message>",
64 <more fields, not important to this tutorial>
67 "id": "<the commit ID>",
68 "message": "<the commit message>",
69 <more fields, not important to this tutorial>
75 In our program, the HTTP request is sent using the following
78 static char *request(const char *url);
80 It takes the URL as a parameter, preforms a HTTP GET request, and
81 returns a newly allocated string that contains the response body. For
82 full details, refer to :download:`the code <github_commits.c>`, as the
83 actual implementation is not important here.
86 .. _tutorial-the-program:
96 Like all the programs using Jansson, we need to include
99 The following function is used when formatting the result to find the
100 first newline in the commit message::
102 /* Return the offset of the first newline in text or the length of
103 text if there's no newline */
104 static int newline_offset(const char *text)
106 const char *newline = strchr(text, '\n');
110 return (int)(newline - text);
113 The main function follows. In the beginning, we first declare a bunch
114 of variables and check the command line parameters::
126 fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]);
127 fprintf(stderr, "List commits at USER's REPOSITORY.\n\n");
131 Then we build the request URL using the user and repository names
132 given as command line parameters::
134 snprintf(url, URL_SIZE, URL_FORMAT, argv[1], argv[2]);
136 This uses the ``URL_SIZE`` and ``URL_FORMAT`` constants defined above.
137 Now we're ready to actually request the JSON data over the web::
143 If an error occurs, our function ``request`` prints the error and
144 returns *NULL*, so it's enough to just return 1 from the main
147 Next we'll call :cfunc:`json_loads()` to decode the JSON text we got
150 root = json_loads(text, &error);
155 fprintf(stderr, "error: on line %d: %s\n", error.line, error.text);
159 We don't need the JSON text anymore, so we can free the ``text``
160 variable right after decoding it. If :cfunc:`json_loads()` fails, it
161 returns *NULL* and sets error information to the :ctype:`json_error_t`
162 structure given as the second parameter. In this case, our program
163 prints the error information out and returns 1 from the main function.
164 This check is really only to be sure, because we can assume that the
165 GitHub API returns correct JSON to us.
167 Next, we'll extract the ``commits`` array from the JSON response::
169 commits = json_object_get(root, "commits");
170 if(!commits || !json_is_array(commits))
172 fprintf(stderr, "error: commits is not an array\n");
176 This is the array that contains objects describing latest commits in
177 the repository. If the key ``commits`` doesn't exist,
178 :cfunc:`json_object_get()` returns *NULL*. We also check that the
179 returned value really is an array.
181 Then we proceed to loop over all the commits in the array::
183 for(i = 0; i < json_array_size(commits); i++)
185 json_t *commit, *id, *message;
186 const char *message_text;
188 commit = json_array_get(commits, i);
189 if(!json_is_object(commit))
191 fprintf(stderr, "error: commit %d is not an object\n", i + 1);
196 The function :cfunc:`json_array_size()` returns the size of a JSON
197 array. First, we again declare some variables and then extract the
198 i'th element of the ``commits`` array using :cfunc:`json_array_get()`.
199 We also check that the resulting value is a JSON object. (The
200 structure of the response JSON was explained in
201 :ref:`tutorial-github-commits-api`).
203 Next we'll extract the commit ID and commit message, and check that
204 they both are JSON strings::
206 id = json_object_get(commit, "id");
207 if(!id || !json_is_string(id))
209 fprintf(stderr, "error: commit %d: id is not a string\n", i + 1);
213 message = json_object_get(commit, "message");
214 if(!message || !json_is_string(message))
216 fprintf(stderr, "error: commit %d: message is not a string\n", i + 1);
221 And finally, we'll print the first 8 characters of the commit ID and
222 the first line of the commit message. A C-style string is extracted
223 from a JSON string using :cfunc:`json_string_value()`::
225 message_text = json_string_value(message);
226 printf("%.8s %.*s\n",
227 json_string_value(id),
228 newline_offset(message_text),
232 After sending the HTTP request, we decoded the JSON text using
233 :cfunc:`json_loads()`, remember? It returns a *new reference* to a
234 JSON value it decodes. When we're finished with the value, we'll need
235 to decrease the reference count using :cfunc:`json_decref()`. This way
236 Jansson can release the resources::
241 For a detailed explanation of reference counting in Jansson, see
242 :ref:`apiref-reference-count` in :ref:`apiref`.
244 The program's ready, let's test it and view the latest commits in
245 Jansson's repository::
247 $ ./github_commits akheron jansson
248 86dc1d62 Fix indentation
249 b67e130f json_dumpf: Document the output shortage on error
250 4cd77771 Enhance handling of circular references
251 79009e62 json_dumps: Close the strbuffer if dumping fails
252 76999799 doc: Fix a small typo in apiref
253 22af193a doc/Makefile.am: Remove *.pyc in clean
254 951d091f Make integer, real and string mutable
255 185e107d Don't use non-portable asprintf()
256 ca7703fb Merge branch '1.0'
257 12cd4e8c jansson 1.0.4
264 In this tutorial, we implemented a program that fetches the latest
265 commits of a GitHub repository using the GitHub commits API. Jansson
266 was used to decode the JSON response and to extract the commit data.
268 This tutorial only covered a small part of Jansson. For example, we
269 did not create or manipulate JSON values at all. Proceed to
270 :ref:`apiref` to explore all features of Jansson.