1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-11-23 07:19:15 +00:00

Enable sorting of JSON object keys when encoding

* lisp/json.el (json-encoding-object-sort-predicate): New variable for
specifying a sorting predicate for JSON objects during encoding.
(json--plist-to-alist): New utility function.
(json-encode-hash-table): Re-use `json-encode-alist' when object keys
are to be sorted.
(json-encode-alist): Sort output by
`json-encoding-object-sort-predicate, when set.
(json-encode-plist): Re-use `json-encode-alist' when object keys are
to be sorted.
(json-pretty-print-buffer-ordered): New command to pretty print the
buffer with object keys sorted alphabetically.
(json-pretty-print-ordered): New command to pretty print the region with
object keys sorted alphabetically.

* test/automated/json-tests.el (test-json-plist-to-alist)
(test-json-encode-plist, test-json-encode-hash-table)
(test-json-encode-alist-with-sort-predicate)
(test-json-encode-plist-with-sort-predicate): New tests.

* etc/NEWS: Add an entry for the new commands.
This commit is contained in:
Simen Heggestøyl 2015-11-12 18:30:37 +01:00
parent 9dd7da9945
commit 1e363a8ea5
3 changed files with 111 additions and 39 deletions

View File

@ -332,6 +332,10 @@ unlike `bookmark-set' which silently updates an existing bookmark.
---
*** `json-pretty-print' and `json-pretty-print-buffer' now maintain
the ordering of object keys by default.
---
*** New commands `json-pretty-print-ordered' and
`json-pretty-print-buffer-ordered' pretty prints JSON objects with
object keys sorted alphabetically.
** You can recompute the VC state of a file buffer with `M-x vc-refresh-state'
** Prog mode has some support for multi-mode indentation.

View File

@ -52,6 +52,8 @@
;;; Code:
(require 'map)
;; Parameters
(defvar json-object-type 'alist
@ -111,6 +113,13 @@ Used only when `json-encoding-pretty-print' is non-nil.")
"If non-nil, ] and } closings will be formatted lisp-style,
without indentation.")
(defvar json-encoding-object-sort-predicate nil
"Sorting predicate for JSON object keys during encoding.
If nil, no sorting is performed. Else, JSON object keys are
ordered by the specified sort predicate during encoding. For
instance, setting this to `string<' will have JSON object keys
ordered alphabetically.")
(defvar json-pre-element-read-function nil
"Function called (if non-nil) by `json-read-array' and
`json-read-object' right before reading a JSON array or object,
@ -159,6 +168,15 @@ Unlike `reverse', this keeps the property-value pairs intact."
(push prop res)))
res))
(defun json--plist-to-alist (plist)
"Return an alist of the property-value pairs in PLIST."
(let (res)
(while plist
(let ((prop (pop plist))
(val (pop plist)))
(push (cons prop val) res)))
(nreverse res)))
(defmacro json--with-indentation (body)
`(let ((json--encoding-current-indentation
(if json-encoding-pretty-print
@ -492,32 +510,39 @@ Please see the documentation of `json-object-type' and `json-key-type'."
(defun json-encode-hash-table (hash-table)
"Return a JSON representation of HASH-TABLE."
(format "{%s%s}"
(json-join
(let (r)
(json--with-indentation
(maphash
(lambda (k v)
(push (format
(if json-encoding-pretty-print
"%s%s: %s"
"%s%s:%s")
json--encoding-current-indentation
(json-encode-key k)
(json-encode v))
r))
hash-table))
r)
json-encoding-separator)
(if (or (not json-encoding-pretty-print)
json-encoding-lisp-style-closings)
""
json--encoding-current-indentation)))
(if json-encoding-object-sort-predicate
(json-encode-alist (map-into hash-table 'list))
(format "{%s%s}"
(json-join
(let (r)
(json--with-indentation
(maphash
(lambda (k v)
(push (format
(if json-encoding-pretty-print
"%s%s: %s"
"%s%s:%s")
json--encoding-current-indentation
(json-encode-key k)
(json-encode v))
r))
hash-table))
r)
json-encoding-separator)
(if (or (not json-encoding-pretty-print)
json-encoding-lisp-style-closings)
""
json--encoding-current-indentation))))
;; List encoding (including alists and plists)
(defun json-encode-alist (alist)
"Return a JSON representation of ALIST."
(when json-encoding-object-sort-predicate
(setq alist
(sort alist (lambda (a b)
(funcall json-encoding-object-sort-predicate
(car a) (car b))))))
(format "{%s%s}"
(json-join
(json--with-indentation
@ -537,25 +562,27 @@ Please see the documentation of `json-object-type' and `json-key-type'."
(defun json-encode-plist (plist)
"Return a JSON representation of PLIST."
(let (result)
(json--with-indentation
(while plist
(push (concat
json--encoding-current-indentation
(json-encode-key (car plist))
(if json-encoding-pretty-print
": "
":")
(json-encode (cadr plist)))
result)
(setq plist (cddr plist))))
(concat "{"
(json-join (nreverse result) json-encoding-separator)
(if (and json-encoding-pretty-print
(not json-encoding-lisp-style-closings))
(if json-encoding-object-sort-predicate
(json-encode-alist (json--plist-to-alist plist))
(let (result)
(json--with-indentation
(while plist
(push (concat
json--encoding-current-indentation
"")
"}")))
(json-encode-key (car plist))
(if json-encoding-pretty-print
": "
":")
(json-encode (cadr plist)))
result)
(setq plist (cddr plist))))
(concat "{"
(json-join (nreverse result) json-encoding-separator)
(if (and json-encoding-pretty-print
(not json-encoding-lisp-style-closings))
json--encoding-current-indentation
"")
"}"))))
(defun json-encode-list (list)
"Return a JSON representation of LIST.
@ -698,6 +725,18 @@ Advances point just past JSON object."
(txt (delete-and-extract-region begin end)))
(insert (json-encode (json-read-from-string txt))))))
(defun json-pretty-print-buffer-ordered ()
"Pretty-print current buffer with object keys ordered."
(interactive)
(let ((json-encoding-object-sort-predicate 'string<))
(json-pretty-print-buffer)))
(defun json-pretty-print-ordered (begin end)
"Pretty-print the region with object keys ordered."
(interactive "r")
(let ((json-encoding-object-sort-predicate 'string<))
(json-pretty-print begin end)))
(provide 'json)
;;; json.el ends here

View File

@ -28,11 +28,40 @@
(should (equal (json--plist-reverse '(:a 1 :b 2 :c 3))
'(:c 3 :b 2 :a 1))))
(ert-deftest test-json-plist-to-alist ()
(should (equal (json--plist-to-alist '()) '()))
(should (equal (json--plist-to-alist '(:a 1)) '((:a . 1))))
(should (equal (json--plist-to-alist '(:a 1 :b 2 :c 3))
'((:a . 1) (:b . 2) (:c . 3)))))
(ert-deftest test-json-encode-plist ()
(let ((plist '(:a 1 :b 2)))
(should (equal (json-encode plist) "{\"a\":1,\"b\":2}"))))
(ert-deftest json-encode-simple-alist ()
(should (equal (json-encode '((a . 1)
(b . 2)))
"{\"a\":1,\"b\":2}")))
(ert-deftest test-json-encode-hash-table ()
(let ((hash-table (make-hash-table))
(json-encoding-object-sort-predicate 'string<))
(puthash :a 1 hash-table)
(puthash :b 2 hash-table)
(puthash :c 3 hash-table)
(should (equal (json-encode hash-table)
"{\"a\":1,\"b\":2,\"c\":3}"))))
(ert-deftest test-json-encode-alist-with-sort-predicate ()
(let ((alist '((:c . 3) (:a . 1) (:b . 2)))
(json-encoding-object-sort-predicate 'string<))
(should (equal (json-encode alist) "{\"a\":1,\"b\":2,\"c\":3}"))))
(ert-deftest test-json-encode-plist-with-sort-predicate ()
(let ((plist '(:c 3 :a 1 :b 2))
(json-encoding-object-sort-predicate 'string<))
(should (equal (json-encode plist) "{\"a\":1,\"b\":2,\"c\":3}"))))
(ert-deftest json-read-simple-alist ()
(let ((json-object-type 'alist))
(should (equal (json-read-from-string "{\"a\": 1, \"b\": 2}")