;;; json.el --- JavaScript Object Notation parser / generator
-;; Copyright (C) 2006-2015 Free Software Foundation, Inc.
+;; Copyright (C) 2006-2016 Free Software Foundation, Inc.
;; Author: Edward O'Connor <ted@oconnor.cx>
;; Version: 1.4
;;; Code:
+(require 'map)
+
;; Parameters
(defvar json-object-type 'alist
"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,
(null list))
(defun json-plist-p (list)
- "Non-null if and only if LIST is a plist."
+ "Non-null if and only if LIST is a plist with keyword keys."
(while (consp list)
(setq list (if (and (keywordp (car list))
(consp (cdr list)))
(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
(unless (char-equal char (json-peek))
(signal 'json-unknown-keyword
(list (save-excursion
- (backward-word 1)
+ (backward-word-strictly 1)
(thing-at-point 'word)))))
(json-advance))
keyword)
(unless (looking-at "\\(\\s-\\|[],}]\\|$\\)")
(signal 'json-unknown-keyword
(list (save-excursion
- (backward-word 1)
+ (backward-word-strictly 1)
(thing-at-point 'word)))))
(cond ((string-equal keyword "true") t)
((string-equal keyword "false") json-false)
(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
(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.
(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