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.
51 GitHub responds with a JSON object of the following form:
58 "id": "<the commit ID>",
59 "message": "<the commit message>",
60 <more fields, not important to this tutorial>
63 "id": "<the commit ID>",
64 "message": "<the commit message>",
65 <more fields, not important to this tutorial>
71 In our program, the HTTP request is sent using the following
74 static char *request(const char *url);
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
83 .. _GitHub commits API: http://develop.github.com/p/commits.html
85 .. _tutorial-the-program:
95 Like all the programs using Jansson, we need to include
98 The following definitions are used to build the GitHub commits API
101 #define URL_FORMAT "http://github.com/api/v2/json/commits/list/%s/%s/master"
104 The following function is used when formatting the result to find the
105 first newline in the commit message::
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)
111 const char *newline = strchr(text, '\n');
115 return (int)(newline - text);
118 The main function follows. In the beginning, we first declare a bunch
119 of variables and check the command line parameters::
131 fprintf(stderr, "usage: %s USER REPOSITORY\n\n", argv[0]);
132 fprintf(stderr, "List commits at USER's REPOSITORY.\n\n");
136 Then we build the request URL using the user and repository names
137 given as command line parameters::
139 snprintf(url, URL_SIZE, URL_FORMAT, argv[1], argv[2]);
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::
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
152 Next we'll call :cfunc:`json_loads()` to decode the JSON text we got
155 root = json_loads(text, &error);
160 fprintf(stderr, "error: on line %d: %s\n", error.line, error.text);
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.
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`.
174 First, we'll extract the ``commits`` array from the JSON response::
176 commits = json_object_get(root, "commits");
177 if(!json_is_array(commits))
179 fprintf(stderr, "error: commits is not an array\n");
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.
188 Then we proceed to loop over all the commits in the array::
190 for(i = 0; i < json_array_size(commits); i++)
192 json_t *commit, *id, *message;
193 const char *message_text;
195 commit = json_array_get(commits, i);
196 if(!json_is_object(commit))
198 fprintf(stderr, "error: commit %d is not an object\n", i + 1);
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.
208 Next we'll extract the commit ID and commit message, and check that
209 they both are JSON strings::
211 id = json_object_get(commit, "id");
212 if(!json_is_string(id))
214 fprintf(stderr, "error: commit %d: id is not a string\n", i + 1);
218 message = json_object_get(commit, "message");
219 if(!json_is_string(message))
221 fprintf(stderr, "error: commit %d: message is not a string\n", i + 1);
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()`::
230 message_text = json_string_value(message);
231 printf("%.8s %.*s\n",
232 json_string_value(id),
233 newline_offset(message_text),
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::
246 For a detailed explanation of reference counting in Jansson, see
247 :ref:`apiref-reference-count` in :ref:`apiref`.
249 The program's ready, let's test it and view the latest commits in
250 Jansson's repository::
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
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.
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.