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:
parent
9dd7da9945
commit
1e363a8ea5
4
etc/NEWS
4
etc/NEWS
@ -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.
|
||||
|
117
lisp/json.el
117
lisp/json.el
@ -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
|
||||
|
@ -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}")
|
||||
|
Loading…
Reference in New Issue
Block a user