From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: David Thompson Newsgroups: gmane.lisp.guile.devel Subject: [PATCH] ice-9: Add JSON module. Date: Sat, 15 Aug 2015 17:21:38 -0400 Message-ID: <87vbcg1c4d.fsf@izanagi.i-did-not-set--mail-host-address--so-tickle-me> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: ger.gmane.org 1439673720 26059 80.91.229.3 (15 Aug 2015 21:22:00 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Sat, 15 Aug 2015 21:22:00 +0000 (UTC) To: guile-devel@gnu.org Original-X-From: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Sat Aug 15 23:21:50 2015 Return-path: Envelope-to: guile-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1ZQith-0004t0-0Q for guile-devel@m.gmane.org; Sat, 15 Aug 2015 23:21:49 +0200 Original-Received: from localhost ([::1]:50817 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZQitg-00037x-55 for guile-devel@m.gmane.org; Sat, 15 Aug 2015 17:21:48 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:38550) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZQita-0002z4-N2 for guile-devel@gnu.org; Sat, 15 Aug 2015 17:21:45 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZQitX-0008RL-T9 for guile-devel@gnu.org; Sat, 15 Aug 2015 17:21:42 -0400 Original-Received: from mail.fsf.org ([208.118.235.13]:36454) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZQitX-0008RF-PW for guile-devel@gnu.org; Sat, 15 Aug 2015 17:21:39 -0400 Original-Received: from 209-6-40-86.c3-0.smr-ubr1.sbo-smr.ma.cable.rcn.com ([209.6.40.86]:59857 helo=izanagi) by mail.fsf.org with esmtpsa (TLS-1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.69) (envelope-from ) id 1ZQitX-0002NR-Cw for guile-devel@gnu.org; Sat, 15 Aug 2015 17:21:39 -0400 User-Agent: Notmuch/0.19 (http://notmuchmail.org) Emacs/24.5.1 (x86_64-unknown-linux-gnu) X-detected-operating-system: by mail.fsf.org: GNU/Linux 2.2.x-3.x [generic] X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6.x X-Received-From: 208.118.235.13 X-BeenThere: guile-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Developers list for Guile, the GNU extensibility library" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Original-Sender: guile-devel-bounces+guile-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.lisp.guile.devel:17788 Archived-At: --=-=-= Content-Type: text/plain Hello Guilers, JSON is an commonly encountered format when writing web applications, much like XML, and I think it would be a good idea if the core Guile distribution had an SXML equivalent for JSON. This patch introduces such an interface in the (ice-9 json) module. With (ice-9 json), this expression: (@ (name . "Eva Luator") (age . 24) (schemer . #t) (hobbies "hacking" "cycling" "surfing")) serializes to this JSON (except not pretty-printed): { "name": "Eva Luator", "age": 24, "schemer": true, "hobbies": [ "hacking", "cycling", "surfing" ] } Thanks to Mark Weaver and Chris Webber for helping come to a consensus on a good syntax for JSON objects. --=-=-= Content-Type: text/x-patch Content-Disposition: inline; filename=0001-ice-9-Add-JSON-module.patch >From 2d4d8607aedaede98f413a84f135d8798d506233 Mon Sep 17 00:00:00 2001 From: David Thompson Date: Sat, 15 Aug 2015 14:09:23 -0400 Subject: [PATCH] ice-9: Add JSON module. * module/ice-9/json.scm: New file. * module/Makefile.am (ICE_9_SOURCES): Add it. * test-suite/tests/json.test: New file. * test-suite/Makefile.am (SCM_TESTS): Add it. * doc/ref/guile.texi ("Guile Modules"): Add "JSON" section. * doc/ref/json.texi: New file. * doc/ref/Makefile.am (guile_TEXINFOS): Add it. --- doc/ref/Makefile.am | 3 +- doc/ref/guile.texi | 2 + doc/ref/json.texi | 62 +++++++ module/Makefile.am | 3 +- module/ice-9/json.scm | 395 +++++++++++++++++++++++++++++++++++++++++++++ test-suite/Makefile.am | 1 + test-suite/tests/json.test | 149 +++++++++++++++++ 7 files changed, 613 insertions(+), 2 deletions(-) create mode 100644 doc/ref/json.texi create mode 100644 module/ice-9/json.scm create mode 100644 test-suite/tests/json.test diff --git a/doc/ref/Makefile.am b/doc/ref/Makefile.am index 31c26a7..5dfc019 100644 --- a/doc/ref/Makefile.am +++ b/doc/ref/Makefile.am @@ -95,7 +95,8 @@ guile_TEXINFOS = preface.texi \ goops.texi \ goops-tutorial.texi \ guile-invoke.texi \ - effective-version.texi + effective-version.texi \ + json.texi ETAGS_ARGS = $(info_TEXINFOS) $(guile_TEXINFOS) diff --git a/doc/ref/guile.texi b/doc/ref/guile.texi index db815eb..468d3a5 100644 --- a/doc/ref/guile.texi +++ b/doc/ref/guile.texi @@ -375,6 +375,7 @@ available through both Scheme and C interfaces. * Statprof:: An easy-to-use statistical profiler. * SXML:: Parsing, transforming, and serializing XML. * Texinfo Processing:: Munging documents written in Texinfo. +* JSON:: Parsing and serializing JSON. @end menu @include slib.texi @@ -397,6 +398,7 @@ available through both Scheme and C interfaces. @include statprof.texi @include sxml.texi @include texinfo.texi +@include json.texi @include goops.texi diff --git a/doc/ref/json.texi b/doc/ref/json.texi new file mode 100644 index 0000000..43dba4d --- /dev/null +++ b/doc/ref/json.texi @@ -0,0 +1,62 @@ +@c -*-texinfo-*- +@c This is part of the GNU Guile Reference Manual. +@c Copyright (C) 2015 Free Software Foundation, Inc. +@c See the file guile.texi for copying conditions. +@c + +@node JSON +@section JSON + +@cindex json +@cindex (ice-9 json) + +The @code{(ice-9 json)} module provides procedures for parsing and +serializing JSON, the JavaScript Object Notation data interchange +format. For example, the JSON document: + +@example +@verbatim +{ + "name": "Eva Luator", + "age": 24, + "schemer": true, + "hobbies": [ + "hacking", + "cycling", + "surfing" + ] +} +@end verbatim +@end example + +may be represented with the following s-expression: + +@example +@verbatim +(@ (name . "Eva Luator") + (age . 24) + (schemer . #t) + (hobbies "hacking" "cycling" "surfing")) +@end verbatim +@end example + +Strings, real numbers, @code{#t}, @code{#f}, @code{#nil}, lists, and +association lists may be serialized as JSON. Association lists +serialize to objects, and regular lists serialize to arrays. To +distinguish regular lists from association lists, the @code{@@} symbol +is used to ``tag'' the association list as a JSON object, as in the +above example. The keys of association lists may be either strings or +symbols. + +@deffn {Scheme Procedure} read-json port + +Parse JSON-encoded text from @var{port} and return its s-expression +representation. + +@end deffn + +@deffn {Scheme Procedure} write-json exp port + +Write the expression @var{exp} as JSON-encoded text to @var{port}. + +@end deffn diff --git a/module/Makefile.am b/module/Makefile.am index 7e96de7..6380953 100644 --- a/module/Makefile.am +++ b/module/Makefile.am @@ -256,7 +256,8 @@ ICE_9_SOURCES = \ ice-9/list.scm \ ice-9/serialize.scm \ ice-9/local-eval.scm \ - ice-9/unicode.scm + ice-9/unicode.scm \ + ice-9/json.scm srfi/srfi-64.go: srfi/srfi-64.scm srfi/srfi-64/testing.scm diff --git a/module/ice-9/json.scm b/module/ice-9/json.scm new file mode 100644 index 0000000..3850ee4 --- /dev/null +++ b/module/ice-9/json.scm @@ -0,0 +1,395 @@ +;;;; json.scm --- JSON reader/writer +;;;; Copyright (C) 2015 Free Software Foundation, Inc. +;;;; +;;;; This library is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU Lesser General Public +;;;; License as published by the Free Software Foundation; either +;;;; version 3 of the License, or (at your option) any later version. +;;;; +;;;; This library 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 +;;;; Lesser General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU Lesser General Public +;;;; License along with this library; if not, write to the Free Software +;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +;;;; + +(define-module (ice-9 json) + #:use-module (ice-9 match) + #:use-module (srfi srfi-1) + #:export (read-json write-json)) + +;;; +;;; Reader +;;; + +(define (json-error port) + (throw 'json-error port)) + +(define (assert-char port char) + "Read a character from PORT and throw an invalid JSON error if the +character is not CHAR." + (unless (eqv? (read-char port) char) + (json-error port))) + +(define (whitespace? char) + "Return #t if CHAR is a whitespace character." + (char-set-contains? char-set:whitespace char)) + +(define (consume-whitespace port) + "Discard characters from PORT until a non-whitespace character is +encountered.." + (match (peek-char port) + ((? eof-object?) *unspecified*) + ((? whitespace?) + (read-char port) + (consume-whitespace port)) + (_ *unspecified*))) + +(define (make-keyword-reader keyword value) + "Parse the keyword symbol KEYWORD as VALUE." + (let ((str (symbol->string keyword))) + (lambda (port) + (let loop ((i 0)) + (cond + ((= i (string-length str)) value) + ((eqv? (string-ref str i) (read-char port)) + (loop (1+ i))) + (else (json-error port))))))) + +(define read-true (make-keyword-reader 'true #t)) +(define read-false (make-keyword-reader 'false #f)) +(define read-null (make-keyword-reader 'null #nil)) + +(define (read-hex-digit port) + "Read a hexadecimal digit from PORT." + (match (read-char port) + (#\0 0) + (#\1 1) + (#\2 2) + (#\3 3) + (#\4 4) + (#\5 5) + (#\6 6) + (#\7 7) + (#\8 8) + (#\9 9) + ((or #\A #\a) 10) + ((or #\B #\b) 11) + ((or #\C #\c) 12) + ((or #\D #\d) 13) + ((or #\E #\e) 14) + ((or #\F #\f) 15) + (_ (json-error port)))) + +(define (read-utf16-character port) + "Read a hexadecimal encoded UTF-16 character from PORT." + (integer->char + (+ (* (read-hex-digit port) (expt 16 3)) + (* (read-hex-digit port) (expt 16 2)) + (* (read-hex-digit port) 16) + (read-hex-digit port)))) + +(define (read-escape-character port) + "Read escape character from PORT." + (match (read-char port) + (#\" #\") + (#\\ #\\) + (#\/ #\/) + (#\b #\backspace) + (#\f #\page) + (#\n #\newline) + (#\r #\return) + (#\t #\tab) + (#\u (read-utf16-character port)) + (_ (json-error port)))) + +(define (read-string port) + "Read a JSON encoded string from PORT." + (assert-char port #\") + (let loop ((result '())) + (match (read-char port) + ((? eof-object?) (json-error port)) + (#\" (list->string (reverse result))) + (#\\ (loop (cons (read-escape-character port) result))) + (char (loop (cons char result)))))) + +(define char-set:json-digit + (char-set #\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)) + +(define (digit? char) + (char-set-contains? char-set:json-digit char)) + +(define (read-digit port) + "Read a digit 0-9 from PORT." + (match (read-char port) + (#\0 0) + (#\1 1) + (#\2 2) + (#\3 3) + (#\4 4) + (#\5 5) + (#\6 6) + (#\7 7) + (#\8 8) + (#\9 9) + (else (json-error port)))) + +(define (read-digits port) + "Read a sequence of digits from PORT." + (let loop ((result '())) + (match (peek-char port) + ((? eof-object?) + (reverse result)) + ((? digit?) + (loop (cons (read-digit port) result))) + (else (reverse result))))) + +(define (read-zeroes port) + "Read a sequence of zeroes from PORT." + (let loop ((result '())) + (match (peek-char port) + ((? eof-object?) + result) + (#\0 + (read-char port) + (loop (cons 0 result))) + (else result)))) + +(define (list->integer digits) + "Convert the list DIGITS to an integer." + (let loop ((i (1- (length digits))) + (result 0) + (digits digits)) + (match digits + (() result) + ((n . tail) + (loop (1- i) + (+ result (* n (expt 10 i))) + tail))))) + +(define (read-positive-integer port) + "Read a positive integer with no leading zeroes from PORT." + (match (read-digits port) + ((0 . _) + (json-error port)) ; no leading zeroes allowed + ((digits ...) + (list->integer digits)))) + +(define (read-exponent port) + "Read exponent from PORT." + (define (read-expt) + (list->integer (read-digits port))) + + (unless (memv (read-char port) '(#\e #\E)) + (json-error port)) + + (match (peek-char port) + ((? eof-object?) + (json-error port)) + (#\- + (read-char port) + (- (read-expt))) + (#\+ + (read-char port) + (read-expt)) + ((? digit?) + (read-expt)) + (_ (json-error port)))) + +(define (read-fraction port) + "Read fractional number part from PORT as an inexact number." + (let* ((digits (read-digits port)) + (numerator (list->integer digits)) + (denomenator (expt 10 (length digits)))) + (/ numerator denomenator))) + +(define (read-positive-number port) + "Read a positive number from PORT." + (let* ((integer (match (peek-char port) + ((? eof-object?) + (json-error port)) + (#\0 + (read-char port) + 0) + ((? digit?) + (read-positive-integer port)) + (_ (json-error port)))) + (fraction (match (peek-char port) + (#\. + (read-char port) + (read-fraction port)) + (_ 0))) + (exponent (match (peek-char port) + ((or #\e #\E) + (read-exponent port)) + (_ 0))) + (n (* (+ integer fraction) (expt 10 exponent)))) + + ;; Keep integers as exact numbers, but convert numbers encoded as + ;; floating point numbers to an inexact representation. + (if (zero? fraction) + n + (exact->inexact n)))) + +(define (read-number port) + "Read a number from PORT" + (match (peek-char port) + ((? eof-object?) + (json-error port)) + (#\- + (read-char port) + (- (read-positive-number port))) + ((? digit?) + (read-positive-number port)) + (_ (json-error port)))) + +(define (read-object port) + "Read key/value map from PORT." + (define (read-key+value-pair) + (let ((key (read-string port))) + (consume-whitespace port) + (assert-char port #\:) + (consume-whitespace port) + (let ((value (read-value port))) + (cons key value)))) + + (assert-char port #\{) + (consume-whitespace port) + + (if (eqv? #\} (peek-char port)) + (begin + (read-char port) + '(@)) ; empty object + (let loop ((result (list (read-key+value-pair)))) + (consume-whitespace port) + (match (peek-char port) + (#\, ; read another value + (read-char port) + (consume-whitespace port) + (loop (cons (read-key+value-pair) result))) + (#\} ; end of object + (read-char port) + (cons '@ (reverse result))) + (_ (json-error port)))))) + +(define (read-array port) + "Read array from PORT." + (assert-char port #\[) + (consume-whitespace port) + + (if (eqv? #\] (peek-char port)) + (begin + (read-char port) + '()) ; empty array + (let loop ((result (list (read-value port)))) + (consume-whitespace port) + (match (peek-char port) + (#\, ; read another value + (read-char port) + (consume-whitespace port) + (loop (cons (read-value port) result))) + (#\] ; end of array + (read-char port) + (reverse result)) + (_ (json-error port)))))) + +(define (read-value port) + "Read a JSON value from PORT." + (consume-whitespace port) + (match (peek-char port) + ((? eof-object?) (json-error port)) + (#\" (read-string port)) + (#\{ (read-object port)) + (#\[ (read-array port)) + (#\t (read-true port)) + (#\f (read-false port)) + (#\n (read-null port)) + ((or #\- (? digit?)) + (read-number port)) + (_ (json-error port)))) + +(define (read-json port) + "Read JSON text from port and return an s-expression representation." + (let ((result (read-value port))) + (consume-whitespace port) + (unless (eof-object? (peek-char port)) + (json-error port)) + result)) + + +;;; +;;; Writer +;;; + +(define (write-string str port) + "Write STR to PORT in JSON string format." + (define (escape-char char) + (display (match char + (#\" "\\\"") + (#\\ "\\\\") + (#\/ "\\/") + (#\backspace "\\b") + (#\page "\\f") + (#\newline "\\n") + (#\return "\\r") + (#\tab "\\t") + (_ char)) + port)) + + (display "\"" port) + (string-for-each escape-char str) + (display "\"" port)) + +(define (write-object alist port) + "Write ALIST to PORT in JSON object format." + ;; Keys may be strings or symbols. + (define key->string + (match-lambda + ((? string? key) key) + ((? symbol? key) (symbol->string key)))) + + (define (write-pair pair) + (match pair + ((key . value) + (write-string (key->string key) port) + (display ":" port) + (write-json value port)))) + + (display "{" port) + (match alist + (() #f) + ((front ... end) + (for-each (lambda (pair) + (write-pair pair) + (display "," port)) + front) + (write-pair end))) + (display "}" port)) + +(define (write-array lst port) + "Write LST to PORT in JSON array format." + (display "[" port) + (match lst + (() #f) + ((front ... end) + (for-each (lambda (val) + (write-json val port) + (display "," port)) + front) + (write-json end port))) + (display "]" port)) + +(define (write-json exp port) + "Write EXP to PORT in JSON format." + (match exp + (#t (display "true" port)) + (#f (display "false" port)) + ;; Differentiate #nil from '(). + ((and (? boolean? ) #nil) (display "null" port)) + ((? string? s) (write-string s port)) + ((? real? n) (display n port)) + (('@ . alist) (write-object alist port)) + ((vals ...) (write-array vals port)))) diff --git a/test-suite/Makefile.am b/test-suite/Makefile.am index 3b10353..cbdfa7d 100644 --- a/test-suite/Makefile.am +++ b/test-suite/Makefile.am @@ -71,6 +71,7 @@ SCM_TESTS = tests/00-initial-env.test \ tests/iconv.test \ tests/import.test \ tests/interp.test \ + tests/json.test \ tests/keywords.test \ tests/list.test \ tests/load.test \ diff --git a/test-suite/tests/json.test b/test-suite/tests/json.test new file mode 100644 index 0000000..c94a1c7 --- /dev/null +++ b/test-suite/tests/json.test @@ -0,0 +1,149 @@ +;;;; json.test --- test JSON reader/writer -*- scheme -*- +;;;; +;;;; Copyright (C) 2015 Free Software Foundation, Inc. +;;;; +;;;; This library is free software; you can redistribute it and/or +;;;; modify it under the terms of the GNU Lesser General Public +;;;; License as published by the Free Software Foundation; either +;;;; version 3 of the License, or (at your option) any later version. +;;;; +;;;; This library 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 +;;;; Lesser General Public License for more details. +;;;; +;;;; You should have received a copy of the GNU Lesser General Public +;;;; License along with this library; if not, write to the Free Software +;;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +(define-module (test-suite test-json) + #:use-module (test-suite lib) + #:use-module (ice-9 json)) + +;;; +;;; Reader +;;; + +(define (read-json-string str) + (call-with-input-string str read-json)) + +(define (json-read=? str x) + (= x (read-json-string str))) + +(define (json-read-eq? str x) + (eq? x (read-json-string str))) + +(define (json-read-equal? str x) + (equal? x (read-json-string str))) + +(define (json-read-string=? str x) + (string=? x (read-json-string str))) + +(with-test-prefix "read-json" + (pass-if (json-read-eq? "true" #t)) + (pass-if (json-read-eq? "false" #f)) + (pass-if (json-read-eq? "null" #nil)) + (pass-if (json-read=? "0" 0)) + (pass-if (json-read=? "0.1" 0.1)) + (pass-if (json-read=? "1" 1)) + (pass-if (json-read=? "-1" -1)) + (pass-if (json-read=? "1.1" 1.1)) + (pass-if (json-read=? "1e2" 1e2)) + (pass-if (json-read=? "1.1e2" 1.1e2)) + (pass-if (json-read=? "1.1e-2" 1.1e-2)) + (pass-if (json-read=? "1.1e+2" 1.1e2)) + (pass-if (json-read=? "1.1e+02" 1.1e2)) + (pass-if (json-read-string=? "\"foo\"" "foo")) + ;; \" escape code. + (pass-if (json-read-string=? "\"\\\"\"" "\"")) + ;; \\ escape code. + (pass-if (json-read-string=? "\"\\\\\"" "\\")) + ;; \/ escape code. + (pass-if (json-read-string=? "\"\\/\"" "/")) + ;; \b escape code. + (pass-if (json-read-string=? "\"\\b\"" "\b")) + ;; \f escape code. + (pass-if (json-read-string=? "\"\\f\"" "\f")) + ;; \n escape code. + (pass-if (json-read-string=? "\"\\n\"" "\n")) + ;; \r escape code. + (pass-if (json-read-string=? "\"\\r\"" "\r")) + ;; \t escape code. + (pass-if (json-read-string=? "\"\\t\"" "\t")) + ;; Unicode in hexadecimal format. + (pass-if (json-read-string=? "\"\\u12ab\"" "\u12ab")) + (pass-if (json-read-equal? "{}" '(@))) + (pass-if (json-read-equal? "{ \"foo\": \"bar\", \"baz\": \"frob\"}" + '(@ ("foo" . "bar") ("baz" . "frob")))) + ;; Nested objects. + (pass-if (json-read-equal? "{\"foo\":{\"bar\":\"baz\"}}" + '(@ ("foo" . (@ ("bar" . "baz")))))) + (pass-if (json-read-eq? "[]" '())) + (pass-if (json-read-equal? "[1, 2, \"foo\"]" + '(1 2 "foo"))) + ;; Nested arrays. + (pass-if (json-read-equal? "[1, 2, [\"foo\", \"bar\"]]" + '(1 2 ("foo" "bar")))) + ;; Arrays and objects nested in each other. + (pass-if (json-read-equal? "{\"foo\":[{\"bar\":true},{\"baz\":[1,2,3]}]}" + '(@ ("foo" . ((@ ("bar" . #t)) + (@ ("baz" . (1 2 3)))))))) + ;; Leading/trailing whitespace. + (pass-if (json-read-eq? " true " #t))) + +;;; +;;; Writer +;;; + +(define (write-json-string exp) + (call-with-output-string + (lambda (port) + (write-json exp port)))) + +(define (json-write-string=? exp str) + (string=? str (write-json-string exp))) + +(with-test-prefix "write-json" + (pass-if (json-write-string=? #t "true")) + (pass-if (json-write-string=? #f "false")) + (pass-if (json-write-string=? #nil "null")) + (pass-if (json-write-string=? 0 "0")) + (pass-if (json-write-string=? 0.1 "0.1")) + (pass-if (json-write-string=? 1 "1")) + (pass-if (json-write-string=? -1 "-1")) + (pass-if (json-write-string=? 1.1 "1.1")) + (pass-if (json-write-string=? "foo" "\"foo\"")) + ;; \" escape code. + (pass-if (json-write-string=? "\"" "\"\\\"\"")) + ;; \\ escape code. + (pass-if (json-write-string=? "\\" "\"\\\\\"")) + ;; \/ escape code. + (pass-if (json-write-string=? "/" "\"\\/\"")) + ;; \b escape code. + (pass-if (json-write-string=? "\b" "\"\\b\"")) + ;; \f escape code. + (pass-if (json-write-string=? "\f" "\"\\f\"")) + ;; \n escape code. + (pass-if (json-write-string=? "\n" "\"\\n\"")) + ;; \r escape code. + (pass-if (json-write-string=? "\r" "\"\\r\"")) + ;; \t escape code. + (pass-if (json-write-string=? "\t" "\"\\t\"")) + (pass-if (json-write-string=? '(@) "{}")) + (pass-if (json-write-string=? '(@ ("foo" . "bar") ("baz" . "frob")) + "{\"foo\":\"bar\",\"baz\":\"frob\"}")) + ;; Nested objects. + (pass-if (json-write-string=? '(@ ("foo" . (@ ("bar" . "baz")))) + "{\"foo\":{\"bar\":\"baz\"}}")) + (pass-if (json-write-string=? '() "[]")) + (pass-if (json-write-string=? '(1 2 "foo") + "[1,2,\"foo\"]")) + ;; Nested arrays. + (pass-if (json-write-string=? '(1 2 ("foo" "bar")) + "[1,2,[\"foo\",\"bar\"]]")) + ;; Arrays and objects nested in each other. + (pass-if (json-write-string=? '(@ ("foo" . ((@ ("bar" . #t)) + (@ ("baz" . (1 2)))))) + "{\"foo\":[{\"bar\":true},{\"baz\":[1,2]}]}")) + ;; Symbol keys in alists + (pass-if (json-write-string=? '(@ (foo . 1)) "{\"foo\":1}"))) -- 2.4.3 --=-=-= Content-Type: text/plain -- David Thompson GPG Key: 0FF1D807 --=-=-=--