From: "Simen Heggestøyl" <simenheg@gmail.com>
To: 21616@debbugs.gnu.org
Subject: bug#21616: [PATCH] Enable sorting of JSON object keys when encoding
Date: Sun, 04 Oct 2015 21:15:21 +0200 [thread overview]
Message-ID: <87twq6e986.fsf@gmail.com> (raw)
When working with JSON data, it is often convenient to be able to
prettify the data, and having object keys sorted in a fixed order.
This patch adds the variable `json-encoding-object-sort-key' which
enables defining a sort key to be used when encoding JSON objects.
Additionally, the commands `json-pretty-print-ordered' and
`json-pretty-print-buffer-ordered' are added for convenience,
providing prettification with alphabetical ordering of object keys.
This gets rid of a lot of redundant code from `json-encode-hash-table'
and `json-encode-plist' by going via alists, and using the logic of
`json-encode-alist' commonly for all the structures.
I was in doubt whether to make `json-pretty-print-ordered' and
`json-pretty-print-buffer-ordered' their own commands, or if it would
be better to provide this functionality by having `json-pretty-print'
and `json-pretty-print-buffer' accept prefix arguments. I decided on
the former, because I think it makes the commands easier to discover.
A proposed patch follows!
-- Simen
From bf32702520b17e9bb975c68adb79c0d84e2cbf41 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Simen=20Heggest=C3=B8yl?= <simenheg@gmail.com>
Date: Sun, 4 Oct 2015 12:21:57 +0200
Subject: [PATCH] Enable sorting of JSON object keys when encoding
* lisp/json.el (json-encoding-object-sort-key): New variable for
specifying a sort key for JSON objects during encoding.
(json--plist-to-alist): New utility function.
(json-encode-hash-table): Re-use `json-encode-alist'.
(json-encode-alist): Sort output by `json-encoding-object-sort-key, when
set.
(json-encode-plist): Re-use `json-encode-alist'.
(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-with-sort-key): New tests.
* etc/NEWS: Add an entry for the new commands.
---
etc/NEWS | 4 +++
lisp/json.el | 77 +++++++++++++++++++++-----------------------
test/automated/json-tests.el | 22 +++++++++++++
3 files changed, 63 insertions(+), 40 deletions(-)
diff --git a/etc/NEWS b/etc/NEWS
index dbe0de3..062f17a 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -318,6 +318,10 @@ standards.
---
*** `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.
diff --git a/lisp/json.el b/lisp/json.el
index e2c7cc7..33247bf 100644
--- a/lisp/json.el
+++ b/lisp/json.el
@@ -52,6 +52,8 @@
;;; Code:
+(require 'map)
+
;; Parameters
(defvar json-object-type 'alist
@@ -100,6 +102,13 @@ this around your call to `json-read' instead of `setq'ing it.")
"The default indentation level for encoding.
Used only when `json-encoding-pretty-print' is non-nil.")
+(defvar json-encoding-object-sort-key nil
+ "Sort key for JSON object keys during encoding.
+If nil, no sorting is performed. Else, JSON object keys are
+ordered by the specified sort key during encoding. For instance,
+setting this to `string<' will have JSON object keys ordered
+alphabetically.")
+
(defvar json--encoding-current-indentation "\n"
"Internally used to keep track of the current indentation level of encoding.
Used only when `json-encoding-pretty-print' is non-nil.")
@@ -148,6 +157,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
@@ -421,32 +439,17 @@ 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)))
+ (json-encode-alist (map-into hash-table 'list)))
;; List encoding (including alists and plists)
(defun json-encode-alist (alist)
"Return a JSON representation of ALIST."
+ (when json-encoding-object-sort-key
+ (setq alist
+ (sort alist (lambda (a b)
+ (funcall json-encoding-object-sort-key
+ (car a) (car b))))))
(format "{%s%s}"
(json-join
(json--with-indentation
@@ -466,25 +469,7 @@ 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))
- json--encoding-current-indentation
- "")
- "}")))
+ (json-encode-alist (json--plist-to-alist plist)))
(defun json-encode-list (list)
"Return a JSON representation of LIST.
@@ -622,6 +607,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-key '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-key 'string<))
+ (json-pretty-print begin end)))
+
(provide 'json)
;;; json.el ends here
diff --git a/test/automated/json-tests.el b/test/automated/json-tests.el
index d1b7a2f..cdf85cc 100644
--- a/test/automated/json-tests.el
+++ b/test/automated/json-tests.el
@@ -28,11 +28,33 @@
(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-key 'string<))
+ (puthash :a 1 hash-table)
+ (puthash :b 2 hash-table)
+ (should (equal (json-encode hash-table) "{\"a\":1,\"b\":2}"))))
+
+(ert-deftest test-json-encode-with-sort-key ()
+ (let ((alist '((:a . 1) (:b . 2) (:c . 3)))
+ (json-encoding-object-sort-key 'string>))
+ (should (equal (json-encode alist) "{\"c\":3,\"b\":2,\"a\":1}"))))
+
(ert-deftest json-read-simple-alist ()
(let ((json-object-type 'alist))
(should (equal (json-read-from-string "{\"a\": 1, \"b\": 2}")
--
2.5.3
next reply other threads:[~2015-10-04 19:15 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-10-04 19:15 Simen Heggestøyl [this message]
2015-11-09 0:01 ` bug#21616: [PATCH] Enable sorting of JSON object keys when encoding Dmitry Gutov
2015-11-11 18:59 ` Simen Heggestøyl
2015-11-12 2:39 ` Dmitry Gutov
2015-11-12 17:37 ` Simen Heggestøyl
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87twq6e986.fsf@gmail.com \
--to=simenheg@gmail.com \
--cc=21616@debbugs.gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this external index
https://git.savannah.gnu.org/cgit/emacs.git
https://git.savannah.gnu.org/cgit/emacs/org-mode.git
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.