Attached is a patch that adds a new (web json) module. Some may
remember that I submitted a patch back in 2015 (time flies, eh?) for
an (ice-9 json) module that never made it in. Well, 10 years is a long
time and Guile still doesn't have a built-in JSON module. Third party
libraries like guile-json and guile-sjson are available, the latter
being an adaptation of my original patch and the former remaining the
go-to library used by larger Guile projects like Guix. There's also
SRFI-180 (which sounds like a cool surfing trick!) which was published
in 2020 but the API is, in my opinion, overly complicated due to
generators and other things. Anyway, JSON continues to be *the* data
interchange format of the web and Guile really ought to have a simple
API that can read/write JSON to/from a port using only Scheme data
types that have read syntax (i.e. no hash tables like guile-json).
This minimal, practical API is what my patch provides. I've tried my
best to make it as efficient as possible.
I've settled on the following JSON<->Scheme data type mapping which is
nearly identical to SRFI-180 with the exception of object keys:
- true and false are #t and #f
- null is the symbol 'null
- numbers are either exact integers (fixnums and bignums) or inexact
reals (flonums, NaNs and infinities excluded)
- strings are strings
- arrays are vectors
- objects are association lists with string keys (SRFI-180 chose
symbols but JSON uses strings so strings feel the most honest)
Thanks in advance for the review,
- Dave
From 104b57e2a7b4ca47096cb524aff688b0ada49f94 Mon Sep 17 00:00:00 2001
From: David Thompson
Date: Sat, 12 Apr 2025 08:27:35 -0400
Subject: [PATCH] web: Add JSON module.
* module/web/json.scm: New file.
* am/bootstrap.am (SOURCES): Add it.
* test-suite/tests/json.test: New file.
* test-suite/Makefile.am (SCM_TESTS): Add it.
* doc/ref/web.texi ("JSON"): New subsection.
---
am/bootstrap.am| 3 +-
doc/ref/web.texi | 93 +++
module/web/json.scm| 308 +
test-suite/Makefile.am | 1 +
test-suite/tests/json.test | 154 +++
5 files changed, 558 insertions(+), 1 deletion(-)
create mode 100644 module/web/json.scm
create mode 100644 test-suite/tests/json.test
diff --git a/am/bootstrap.am b/am/bootstrap.am
index 96023d83d..6806fda5d 100644
--- a/am/bootstrap.am
+++ b/am/bootstrap.am
@@ -425,7 +425,8 @@ SOURCES = \
web/response.scm\
web/server.scm\
web/server/http.scm\
- web/uri.scm
+ web/uri.scm \
+ web/json.scm
ELISP_SOURCES = \
language/elisp/boot.el
diff --git a/doc/ref/web.texi b/doc/ref/web.texi
index 607c855b6..53ce14820 100644
--- a/doc/ref/web.texi
+++ b/doc/ref/web.texi
@@ -40,6 +40,7 @@ back.
* Transfer Codings::HTTP Transfer Codings.
* Requests::HTTP requests.
* Responses:: HTTP responses.
+* JSON::The JavaScript Object Notation.
* Web Client:: Accessing web resources over HTTP.
* Web Server:: Serving HTTP to the internet.
* Web Examples::How to use this thing.
@@ -1448,6 +1449,98 @@ Return @code{#t} if @var{type}, a symbol as returned by
@end deffn
+@node JSON
+@subsection JSON
+
+@cindex json
+@cindex (web json)
+
+@example
+(use-modules (web json))
+@end example
+
+JavaScript Object Notation (JSON) is the most common data interchange
+format on the web. It is ubiquitous in HTTP APIs and has found its way
+into many other domains beyond the web, as well. The @code{(web json)}
+module makes it possible to convert a subset of Scheme data types to
+JSON text and vice versa. For example, the JSON document:
+
+@example
+@verbatim
+{
+ "name": "Eva Luator",
+ "age": 24,
+ "schemer": true,
+ "hobbies": [
+"hacking",
+"cycling",
+"surfing"
+ ]
+}
+@end verbatim
+@end example
+
+can be represented with the following Scheme expression:
+
+@example
+@verbatim
+'(("name" . "Eva Luator")
+ ("age" . 24)
+ ("schemer" . #t)
+ ("hobbies" . #("hacking" "cycling" "surfing")))
+@end verbatim
+@end example
+
+Strings, exact integers, inexact reals (excluding NaNs and infinities),
+@code{#t}, @code{#f}, the symbol @code{null}, vectors, and association
+lists may be serialized as JSON. Association lists serialize as JSON
+objects and vectors serialize as JSON arrays. The keys of association
+lists @emph{must} be strings.
+
+@deffn {Scheme Procedure} read-json [port]
+
+Parse a JSON-encoded value from @var{port} and return its Scheme
+representation. If @var{port} is unspecified, the current input port is
+used.
+
+@example
+@verbatim
+(call-with-input-string "[true,false,null,42,\"foo\"]" read-json)
+;; => #(#t #f null 42 "foo")
+
+(call-with-input-string "{\"foo\":1,\"bar\":2}" read-json)
+;; => (("foo" . 1) ("bar" . 2))
+@end verbatim
+@end example
+
+@end deffn
+
+@deftp {Exception Type} &json-read-error
+An exception type denot