Skip to content

Commit

Permalink
support for handling duplicate keys
Browse files Browse the repository at this point in the history
This patch adds support for handling duplicate keys. If a duplicate key is found
in an object the last value will be used. If #ordered is used, the first
appearance of the key is considered in the order.

Fixes #84
  • Loading branch information
aconchillo committed Dec 17, 2023
1 parent 7e3d5bf commit 70c79ab
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 7 deletions.
20 changes: 14 additions & 6 deletions json/parser.scm
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
;;; (json parser) --- Guile JSON implementation.

;; Copyright (C) 2013-2020 Aleix Conchillo Flaque <[email protected]>
;; Copyright (C) 2013-2023 Aleix Conchillo Flaque <[email protected]>
;;
;; This file is part of guile-json.
;;
Expand Down Expand Up @@ -212,25 +212,33 @@

(define (json-read-object port null ordered)
(expect-delimiter port #\{)
(let loop ((pairs '()) (added #t))
(let loop ((items-hash (make-hash-table))
(items-order '())
(added #t))
(skip-whitespaces port)
(let ((ch (peek-char port)))
(cond
;; End of object.
((eqv? ch #\})
(read-char port)
(cond
(added (if ordered (reverse! pairs) pairs))
(added (if ordered
(map (lambda (pair) (hash-ref items-hash (car pair)))
(reverse! items-order))
(hash-map->list (lambda (key value) value) items-hash)))
(else (json-exception port))))
;; Read one pair and continue.
((eqv? ch #\")
(let ((pair (read-pair port null ordered)))
(loop (cons pair pairs) #t)))
(let* ((pair (read-pair port null ordered))
(key (car pair))
(key-exists (hash-ref items-hash key) ))
(hash-set! items-hash key pair)
(loop items-hash (if key-exists items-order (cons pair items-order)) #t)))
;; Skip comma and read more pairs.
((eqv? ch #\,)
(read-char port)
(cond
(added (loop pairs #f))
(added (loop items-hash items-order #f))
(else (json-exception port))))
;; Invalid object.
(else (json-exception port))))))
Expand Down
6 changes: 5 additions & 1 deletion tests/test-parser.scm
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
;;; (tests test-parser) --- Guile JSON implementation.

;; Copyright (C) 2018-2022 Aleix Conchillo Flaque <[email protected]>
;; Copyright (C) 2018-2023 Aleix Conchillo Flaque <[email protected]>
;;
;; This file is part of guile-json.
;;
Expand Down Expand Up @@ -86,6 +86,7 @@
(test-error #t (json-string->scm "[,1]"))
(test-error #t (json-string->scm "[1,2,,,5]"))
(test-error #t (json-string->scm "[1,2"))
(test-error #t (json-string->scm "[1,2,]"))

;; Objects
(test-equal '() (json-string->scm "{}"))
Expand All @@ -102,6 +103,9 @@
(test-equal '() (json-string->scm "{}" #:ordered #t))
(test-equal '(("green" . 1) ("eggs" . 2) ("ham" . 3)) (json-string->scm "{\"green\":1, \"eggs\":2, \"ham\":3}" #:ordered #t))

;; Objects with duplicate keys
(test-equal '(("foo" . "last") ("bar" . 2) ("baz" . #(1 2 3))) (json-string->scm "{\"foo\": \"first\", \"bar\": 2, \"foo\": \"second\", \"baz\": [1, 2, 3], \"foo\": \"last\"}" #:ordered #t))

;; Since the following JSON object contains more than one key-value pair, we
;; can't use "test-equal" directly since the output could be unordered.
(define book (json-string->scm "{\"title\":\"A book\",\"author\":\"An author\",\"price\":29.99}"))
Expand Down

0 comments on commit 70c79ab

Please sign in to comment.