aconchillo / guile-json

JSON module for Guile
GNU General Public License v3.0
101 stars 34 forks source link
guile json scheme

guile-json

GNU Guile 2.2 GNU Guile 3.0

guile-json is a JSON module for Guile. It supports parsing and building JSON documents according to the http://json.org specification.

Installation

Download the latest tarball and untar it:

If you are cloning the repository make sure you run this first:

$ autoreconf -vif

Then, run the typical sequence:

$ ./configure --prefix=<guile-prefix>
$ make
$ sudo make install

Where <guile-prefix> should preferably be the same as your system Guile installation directory (e.g. /usr).

If everything installed successfully you should be up and running:

$ guile
scheme@(guile-user)> (use-modules (json))
scheme@(guile-user)> (scm->json #(1 2 3))
[1,2,3]

It might be that you installed guile-json somewhere differently than your system's Guile. If so, you need to indicate Guile where to find guile-json, for example:

$ GUILE_LOAD_PATH=/usr/local/share/guile/site guile

Usage

guile-json provides a few procedures to parse and build a JSON document. A JSON document is transformed into or from native Guile values according to the following table:

JSON Guile
string string
number number
object alist
array vector
true #t
false #f
null 'null

Why are JSON arrays converted to vectors and JSON objects to alists? See this discussion for details.

By default the value of JSON "null" is mapped to the symbol 'null. However, all guile-json functions allow changing the default null value by specifying the #:null keyword argument with another value. This other value needs to be recognized by eq?.

To start using guile-json procedures and macros you first need to load the module:

scheme@(guile-user)> (use-modules (json))

Reading JSON documents

Building JSON documents

Reading JSON Text Sequences

Building JSON Text Sequences

Exceptions

A json-invalid exception is thrown if an error is found during the JSON parsing with a single port argument. The line or column where the error occured can be easily obtained from the port by calling port-line or port-column.

When building a JSON document from a native type a json-invalid exception might be thrown with the offending value as an argument (see table above for supported types).

JSON Objects and Records

guile-json 4.5.0 introduces JSON types, a new feature that allows converting JSON objects into record types and vice versa in a very straight forward way. This was built on top of define-json-mapping which was introduced in version 4.2.0.

Let's take a look at an example. Imagine we have the following user account information:

{
  "id": 1234,
  "username": "jane"
}

We can easily create a record representing that data with define-json-type by simply doing:

> (define-json-type <account>
    (id)
    (username))

This will define the record constructor, the predicate and conversion procedures like json->account or account->json (see define-json-type for more details).

We can now create a new account and check its contents as with regular records:

> (define account (make-account "1234" "jane"))
> (account-id account)
"1234"
> (account-username account)
"jane"

Or we can use the auto-generated scm->account to create the account:

> (define account (scm->account '(("id" . "1234") ("username" . "jane"))))

It is also possible to convert the record to a JSON string:

> (account->json account)
"{\"id\":\"1234\",\"username\":\"jane\"}"

Or from a JSON string to a new record:

> (define json-account "{\"id\":\"1234\",\"username\":\"jane\"}")
> (json->account json-account)
#<<account> id: "1234" username: "jane">

We could also create a list of accounts:

> (define-json-type <accounts-list>
    (accounts "accounts" #(<account)))

In which case we would do:

> (scm->accounts-list '(("accounts" . #((("id" . "1234") ("username" . "jane"))
                                        (("id" . "4321") ("username" . "joe"))))))
#<<accounts-list> accounts: (#<<account> id: "1234" username: "jane"> #<<account> id: "4321" username: "joe">)>

Note how the accounts field is stored as a list inside the record field, this is to simplify creating records and working with field getters. For example, to create the same but directly using records we would use:

> (make-accounts-list (list (make-account "1234" "jane") (make-account "4321" "joe")))

Macros

Records and null fields

When serializing a record to a JSON object it is possible to set a field to the *unspecified* value in order to omit it from serialization. Also, when deserializing a JSON object to a record, missing record fields in the JSON object will be set to *unspecified* in the record.

Examples

> (scm->json "hello world")
"hello world"
> (scm->json #(1 2 3))
[1,2,3]
> (scm->json '(("project" . "foo") ("author" . "bar")))
{"project":"foo","author":"bar"}
> (scm->json '((project . foo) ("author" . "bar")))
{"project":"foo","author":"bar"}
> (scm->json '(("values" . #(234 98.56))))
{"values":[234,98.56]}
> (define values #(234 98.56))
> (scm->json `(("values" . ,values)))
{"values":[234,98.56]}
> (scm->json 'null)
null
> (scm->json #nil #:null #nil)
null

License

Copyright (C) 2013-2022 Aleix Conchillo Flaque aconchillo@gmail.com

guile-json is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.

guile-json is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with guile-json. If not, see https://www.gnu.org/licenses/.