Package: emacs;
Reported by: Simen Heggestøyl <simenheg <at> gmail.com>
Date: Sun, 4 Oct 2015 19:16:01 UTC
Severity: normal
Tags: patch
Done: Simen Heggestøyl <simenheg <at> gmail.com>
Bug is archived. No further changes may be made.
View this message in rfc822 format
From: help-debbugs <at> gnu.org (GNU bug Tracking System) To: Simen Heggestøyl <simenheg <at> gmail.com> Cc: tracker <at> debbugs.gnu.org Subject: bug#21616: closed ([PATCH] Enable sorting of JSON object keys when encoding) Date: Thu, 12 Nov 2015 17:38:02 +0000
[Message part 1 (text/plain, inline)]
Your message dated Thu, 12 Nov 2015 18:37:33 +0100 with message-id <1447349853.2400.0 <at> smtp.gmail.com> and subject line Re: bug#21616: [PATCH] Enable sorting of JSON object keys when encoding has caused the debbugs.gnu.org bug report #21616, regarding [PATCH] Enable sorting of JSON object keys when encoding to be marked as done. (If you believe you have received this mail in error, please contact help-debbugs <at> gnu.org.) -- 21616: http://debbugs.gnu.org/cgi/bugreport.cgi?bug=21616 GNU Bug Tracking System Contact help-debbugs <at> gnu.org with problems
[Message part 2 (message/rfc822, inline)]
From: Simen Heggestøyl <simenheg <at> gmail.com> To: bug-gnu-emacs <at> gnu.org Subject: [PATCH] Enable sorting of JSON object keys when encoding Date: Sun, 04 Oct 2015 21:15:21 +0200When 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 <at> 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
[Message part 3 (message/rfc822, inline)]
From: Simen Heggestøyl <simenheg <at> gmail.com> To: Dmitry Gutov <dgutov <at> yandex.ru> Cc: 21616-done <at> debbugs.gnu.org Subject: Re: bug#21616: [PATCH] Enable sorting of JSON object keys when encoding Date: Thu, 12 Nov 2015 18:37:33 +0100[Message part 4 (text/plain, inline)]Thanks for your time, Dmitry. Installed! On Thu, Nov 12, 2015 at 3:39 AM, Dmitry Gutov <dgutov <at> yandex.ru> wrote: > Hi Simen, > > On 11/11/2015 08:59 PM, Simen Heggestøyl wrote: > >> How about keeping the old encoding code as the default, and only do >> the >> {hash-table, plist} → alist transform when the output is to be >> sorted? >> That keeps new code to a minimum, and the they would need to be >> transformed to an intermediate structure to be sorted anyway. >> >> A patch implementing this suggestion is attached. Here are the same >> benchmarks with the new patch applied: > > LGTM, please install. Thanks.[Message part 5 (text/html, inline)]
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.