2016-08-10 04:42:40 +00:00
|
|
|
|
;;; json.el --- JavaScript Object Notation parser / generator -*- lexical-binding: t -*-
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
2019-01-01 00:59:58 +00:00
|
|
|
|
;; Copyright (C) 2006-2019 Free Software Foundation, Inc.
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
2017-05-07 01:34:36 +00:00
|
|
|
|
;; Author: Theresa O'Connor <ted@oconnor.cx>
|
2012-12-14 14:57:37 +00:00
|
|
|
|
;; Version: 1.4
|
2008-02-21 19:41:38 +00:00
|
|
|
|
;; Keywords: convenience
|
|
|
|
|
|
|
|
|
|
;; This file is part of GNU Emacs.
|
|
|
|
|
|
2008-05-06 08:06:51 +00:00
|
|
|
|
;; GNU Emacs is free software: you can redistribute it and/or modify
|
2008-02-21 19:41:38 +00:00
|
|
|
|
;; it under the terms of the GNU General Public License as published by
|
2008-05-06 08:06:51 +00:00
|
|
|
|
;; the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
;; (at your option) any later version.
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
|
|
|
|
;; GNU Emacs is distributed in the hope that it will be useful,
|
|
|
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
;; GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
2017-09-13 22:52:52 +00:00
|
|
|
|
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
|
|
|
|
;; This is a library for parsing and generating JSON (JavaScript Object
|
|
|
|
|
;; Notation).
|
|
|
|
|
|
|
|
|
|
;; Learn all about JSON here: <URL:http://json.org/>.
|
|
|
|
|
|
|
|
|
|
;; The user-serviceable entry points for the parser are the functions
|
|
|
|
|
;; `json-read' and `json-read-from-string'. The encoder has a single
|
|
|
|
|
;; entry point, `json-encode'.
|
|
|
|
|
|
|
|
|
|
;; Since there are several natural representations of key-value pair
|
|
|
|
|
;; mappings in elisp (alist, plist, hash-table), `json-read' allows you
|
|
|
|
|
;; to specify which you'd prefer (see `json-object-type' and
|
|
|
|
|
;; `json-array-type').
|
|
|
|
|
|
|
|
|
|
;; Similarly, since `false' and `null' are distinct in JSON, you can
|
|
|
|
|
;; distinguish them by binding `json-false' and `json-null' as desired.
|
|
|
|
|
|
|
|
|
|
;;; History:
|
|
|
|
|
|
|
|
|
|
;; 2006-03-11 - Initial version.
|
|
|
|
|
;; 2006-03-13 - Added JSON generation in addition to parsing. Various
|
|
|
|
|
;; other cleanups, bugfixes, and improvements.
|
|
|
|
|
;; 2006-12-29 - XEmacs support, from Aidan Kehoe <kehoea@parhasard.net>.
|
|
|
|
|
;; 2008-02-21 - Installed in GNU Emacs.
|
2011-10-17 17:40:27 +00:00
|
|
|
|
;; 2011-10-17 - Patch `json-alist-p' and `json-plist-p' to avoid recursion -tzz
|
2012-12-14 14:57:37 +00:00
|
|
|
|
;; 2012-10-25 - Added pretty-printed reformatting -Ryan Crum (ryan@ryancrum.org)
|
2019-07-31 20:18:57 +00:00
|
|
|
|
;; 2019-02-02 - Pretty-printing now uses replace-region-contents and support for
|
|
|
|
|
;; minimization -tsdh
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
2015-11-12 17:30:37 +00:00
|
|
|
|
(require 'map)
|
2019-02-23 20:18:36 +00:00
|
|
|
|
(require 'subr-x)
|
2015-11-12 17:30:37 +00:00
|
|
|
|
|
2008-02-21 19:41:38 +00:00
|
|
|
|
;; Parameters
|
|
|
|
|
|
|
|
|
|
(defvar json-object-type 'alist
|
|
|
|
|
"Type to convert JSON objects to.
|
Fix typos in docstrings.
* image-dired.el (image-dired-display-thumbs): Fix typo in docstring.
(image-dired-read-comment): Doc fix.
* json.el (json-object-type, json-array-type, json-key-type, json-false)
(json-null, json-read-number):
* minibuffer.el (completion-in-region-functions):
* calendar/cal-tex.el (cal-tex-daily-end, cal-tex-number-weeks)
(cal-tex-cursor-week):
* emacs-lisp/trace.el (trace-function):
* eshell/em-basic.el (eshell/printnl):
* eshell/em-dirs.el (eshell-last-dir-ring, eshell-parse-drive-letter)
(eshell-read-last-dir-ring, eshell-write-last-dir-ring):
* obsolete/levents.el (allocate-event, event-key, event-object)
(event-point, event-process, event-timestamp, event-to-character)
(event-window, event-x, event-x-pixel, event-y, event-y-pixel):
* textmodes/reftex-vars.el (reftex-index-macros-builtin)
(reftex-section-levels, reftex-auto-recenter-toc, reftex-toc-mode-hook)
(reftex-cite-punctuation, reftex-search-unrecursed-path-first)
(reftex-highlight-selection): Fix typos in docstrings.
2010-03-22 16:50:29 +00:00
|
|
|
|
Must be one of `alist', `plist', or `hash-table'. Consider let-binding
|
2015-10-03 21:52:36 +00:00
|
|
|
|
this around your call to `json-read' instead of `setq'ing it. Ordering
|
|
|
|
|
is maintained for `alist' and `plist', but not for `hash-table'.")
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
|
|
|
|
(defvar json-array-type 'vector
|
|
|
|
|
"Type to convert JSON arrays to.
|
Fix typos in docstrings.
* image-dired.el (image-dired-display-thumbs): Fix typo in docstring.
(image-dired-read-comment): Doc fix.
* json.el (json-object-type, json-array-type, json-key-type, json-false)
(json-null, json-read-number):
* minibuffer.el (completion-in-region-functions):
* calendar/cal-tex.el (cal-tex-daily-end, cal-tex-number-weeks)
(cal-tex-cursor-week):
* emacs-lisp/trace.el (trace-function):
* eshell/em-basic.el (eshell/printnl):
* eshell/em-dirs.el (eshell-last-dir-ring, eshell-parse-drive-letter)
(eshell-read-last-dir-ring, eshell-write-last-dir-ring):
* obsolete/levents.el (allocate-event, event-key, event-object)
(event-point, event-process, event-timestamp, event-to-character)
(event-window, event-x, event-x-pixel, event-y, event-y-pixel):
* textmodes/reftex-vars.el (reftex-index-macros-builtin)
(reftex-section-levels, reftex-auto-recenter-toc, reftex-toc-mode-hook)
(reftex-cite-punctuation, reftex-search-unrecursed-path-first)
(reftex-highlight-selection): Fix typos in docstrings.
2010-03-22 16:50:29 +00:00
|
|
|
|
Must be one of `vector' or `list'. Consider let-binding this around
|
2008-02-21 19:41:38 +00:00
|
|
|
|
your call to `json-read' instead of `setq'ing it.")
|
|
|
|
|
|
|
|
|
|
(defvar json-key-type nil
|
|
|
|
|
"Type to convert JSON keys to.
|
|
|
|
|
Must be one of `string', `symbol', `keyword', or nil.
|
|
|
|
|
|
|
|
|
|
If nil, `json-read' will guess the type based on the value of
|
|
|
|
|
`json-object-type':
|
|
|
|
|
|
|
|
|
|
If `json-object-type' is: nil will be interpreted as:
|
|
|
|
|
`hash-table' `string'
|
|
|
|
|
`alist' `symbol'
|
|
|
|
|
`plist' `keyword'
|
|
|
|
|
|
|
|
|
|
Note that values other than `string' might behave strangely for
|
Fix typos in docstrings.
* image-dired.el (image-dired-display-thumbs): Fix typo in docstring.
(image-dired-read-comment): Doc fix.
* json.el (json-object-type, json-array-type, json-key-type, json-false)
(json-null, json-read-number):
* minibuffer.el (completion-in-region-functions):
* calendar/cal-tex.el (cal-tex-daily-end, cal-tex-number-weeks)
(cal-tex-cursor-week):
* emacs-lisp/trace.el (trace-function):
* eshell/em-basic.el (eshell/printnl):
* eshell/em-dirs.el (eshell-last-dir-ring, eshell-parse-drive-letter)
(eshell-read-last-dir-ring, eshell-write-last-dir-ring):
* obsolete/levents.el (allocate-event, event-key, event-object)
(event-point, event-process, event-timestamp, event-to-character)
(event-window, event-x, event-x-pixel, event-y, event-y-pixel):
* textmodes/reftex-vars.el (reftex-index-macros-builtin)
(reftex-section-levels, reftex-auto-recenter-toc, reftex-toc-mode-hook)
(reftex-cite-punctuation, reftex-search-unrecursed-path-first)
(reftex-highlight-selection): Fix typos in docstrings.
2010-03-22 16:50:29 +00:00
|
|
|
|
Sufficiently Weird keys. Consider let-binding this around your call to
|
2008-02-21 19:41:38 +00:00
|
|
|
|
`json-read' instead of `setq'ing it.")
|
|
|
|
|
|
|
|
|
|
(defvar json-false :json-false
|
|
|
|
|
"Value to use when reading JSON `false'.
|
|
|
|
|
If this has the same value as `json-null', you might not be able to tell
|
Fix typos in docstrings.
* image-dired.el (image-dired-display-thumbs): Fix typo in docstring.
(image-dired-read-comment): Doc fix.
* json.el (json-object-type, json-array-type, json-key-type, json-false)
(json-null, json-read-number):
* minibuffer.el (completion-in-region-functions):
* calendar/cal-tex.el (cal-tex-daily-end, cal-tex-number-weeks)
(cal-tex-cursor-week):
* emacs-lisp/trace.el (trace-function):
* eshell/em-basic.el (eshell/printnl):
* eshell/em-dirs.el (eshell-last-dir-ring, eshell-parse-drive-letter)
(eshell-read-last-dir-ring, eshell-write-last-dir-ring):
* obsolete/levents.el (allocate-event, event-key, event-object)
(event-point, event-process, event-timestamp, event-to-character)
(event-window, event-x, event-x-pixel, event-y, event-y-pixel):
* textmodes/reftex-vars.el (reftex-index-macros-builtin)
(reftex-section-levels, reftex-auto-recenter-toc, reftex-toc-mode-hook)
(reftex-cite-punctuation, reftex-search-unrecursed-path-first)
(reftex-highlight-selection): Fix typos in docstrings.
2010-03-22 16:50:29 +00:00
|
|
|
|
the difference between `false' and `null'. Consider let-binding this
|
2008-02-21 19:41:38 +00:00
|
|
|
|
around your call to `json-read' instead of `setq'ing it.")
|
|
|
|
|
|
|
|
|
|
(defvar json-null nil
|
|
|
|
|
"Value to use when reading JSON `null'.
|
|
|
|
|
If this has the same value as `json-false', you might not be able to
|
Fix typos in docstrings.
* image-dired.el (image-dired-display-thumbs): Fix typo in docstring.
(image-dired-read-comment): Doc fix.
* json.el (json-object-type, json-array-type, json-key-type, json-false)
(json-null, json-read-number):
* minibuffer.el (completion-in-region-functions):
* calendar/cal-tex.el (cal-tex-daily-end, cal-tex-number-weeks)
(cal-tex-cursor-week):
* emacs-lisp/trace.el (trace-function):
* eshell/em-basic.el (eshell/printnl):
* eshell/em-dirs.el (eshell-last-dir-ring, eshell-parse-drive-letter)
(eshell-read-last-dir-ring, eshell-write-last-dir-ring):
* obsolete/levents.el (allocate-event, event-key, event-object)
(event-point, event-process, event-timestamp, event-to-character)
(event-window, event-x, event-x-pixel, event-y, event-y-pixel):
* textmodes/reftex-vars.el (reftex-index-macros-builtin)
(reftex-section-levels, reftex-auto-recenter-toc, reftex-toc-mode-hook)
(reftex-cite-punctuation, reftex-search-unrecursed-path-first)
(reftex-highlight-selection): Fix typos in docstrings.
2010-03-22 16:50:29 +00:00
|
|
|
|
tell the difference between `false' and `null'. Consider let-binding
|
2008-02-21 19:41:38 +00:00
|
|
|
|
this around your call to `json-read' instead of `setq'ing it.")
|
|
|
|
|
|
2012-12-14 14:57:37 +00:00
|
|
|
|
(defvar json-encoding-separator ","
|
2012-12-14 20:05:03 +00:00
|
|
|
|
"Value to use as an element separator when encoding.")
|
2012-12-14 14:57:37 +00:00
|
|
|
|
|
|
|
|
|
(defvar json-encoding-default-indentation " "
|
|
|
|
|
"The default indentation level for encoding.
|
|
|
|
|
Used only when `json-encoding-pretty-print' is non-nil.")
|
|
|
|
|
|
|
|
|
|
(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.")
|
|
|
|
|
|
|
|
|
|
(defvar json-encoding-pretty-print nil
|
|
|
|
|
"If non-nil, then the output of `json-encode' will be pretty-printed.")
|
|
|
|
|
|
|
|
|
|
(defvar json-encoding-lisp-style-closings nil
|
|
|
|
|
"If non-nil, ] and } closings will be formatted lisp-style,
|
|
|
|
|
without indentation.")
|
|
|
|
|
|
2015-11-12 17:30:37 +00:00
|
|
|
|
(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.")
|
|
|
|
|
|
2015-11-08 20:44:21 +00:00
|
|
|
|
(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,
|
|
|
|
|
respectively. The function is called with one argument, which is
|
|
|
|
|
the current JSON key.")
|
|
|
|
|
|
|
|
|
|
(defvar json-post-element-read-function nil
|
|
|
|
|
"Function called (if non-nil) by `json-read-array' and
|
|
|
|
|
`json-read-object' right after reading a JSON array or object,
|
|
|
|
|
respectively.")
|
|
|
|
|
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Utilities
|
|
|
|
|
|
|
|
|
|
(defun json-join (strings separator)
|
|
|
|
|
"Join STRINGS with SEPARATOR."
|
|
|
|
|
(mapconcat 'identity strings separator))
|
|
|
|
|
|
|
|
|
|
(defun json-alist-p (list)
|
2013-11-24 22:49:37 +00:00
|
|
|
|
"Non-null if and only if LIST is an alist with simple keys."
|
2011-10-17 17:40:27 +00:00
|
|
|
|
(while (consp list)
|
2013-11-24 22:49:37 +00:00
|
|
|
|
(setq list (if (and (consp (car list))
|
|
|
|
|
(atom (caar list)))
|
2011-10-17 17:40:27 +00:00
|
|
|
|
(cdr list)
|
|
|
|
|
'not-alist)))
|
|
|
|
|
(null list))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
|
|
|
|
(defun json-plist-p (list)
|
Extend the test suite for json.el
* lisp/json.el (json-plist-p): Clarify docstring.
* test/automated/json-tests.el (json-tests--with-temp-buffer): New
macro.
(test-json-join, test-json-alist-p)
(test-json-plist-p, test-json-advance, test-json-peek)
(test-json-pop, test-json-skip-whitespace)
(test-json-read-keyword, test-json-encode-keyword)
(test-json-read-number, test-json-encode-number)
(test-json-read-escaped-char, test-json-read-string)
(test-json-encode-string, test-json-encode-key)
(test-json-new-object, test-json-add-to-object)
(test-json-read-object, test-json-encode-list)
(test-json-read-array, test-json-encode-array)
(test-json-read, test-json-read-from-string)
(test-json-encode): New tests.
(json-read-simple-alist): Merged into `test-json-read-object'.
(json-encode-string-with-special-chars): Merged into
`test-json-encode-string'.
(json-read-string-with-special-chars): Split into
`test-json-encode-string' and `test-json-read-from-string'.
2015-11-24 22:13:30 +00:00
|
|
|
|
"Non-null if and only if LIST is a plist with keyword keys."
|
2011-10-17 17:40:27 +00:00
|
|
|
|
(while (consp list)
|
|
|
|
|
(setq list (if (and (keywordp (car list))
|
|
|
|
|
(consp (cdr list)))
|
|
|
|
|
(cddr list)
|
|
|
|
|
'not-plist)))
|
|
|
|
|
(null list))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
2015-10-03 21:52:36 +00:00
|
|
|
|
(defun json--plist-reverse (plist)
|
|
|
|
|
"Return a copy of PLIST in reverse order.
|
|
|
|
|
Unlike `reverse', this keeps the property-value pairs intact."
|
|
|
|
|
(let (res)
|
|
|
|
|
(while plist
|
|
|
|
|
(let ((prop (pop plist))
|
|
|
|
|
(val (pop plist)))
|
|
|
|
|
(push val res)
|
|
|
|
|
(push prop res)))
|
|
|
|
|
res))
|
|
|
|
|
|
2015-11-12 17:30:37 +00:00
|
|
|
|
(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)))
|
|
|
|
|
|
2012-12-14 14:57:37 +00:00
|
|
|
|
(defmacro json--with-indentation (body)
|
|
|
|
|
`(let ((json--encoding-current-indentation
|
|
|
|
|
(if json-encoding-pretty-print
|
|
|
|
|
(concat json--encoding-current-indentation
|
|
|
|
|
json-encoding-default-indentation)
|
|
|
|
|
"")))
|
|
|
|
|
,body))
|
|
|
|
|
|
2008-02-21 19:41:38 +00:00
|
|
|
|
;; Reader utilities
|
|
|
|
|
|
2017-09-15 13:49:27 +00:00
|
|
|
|
(define-inline json-advance (&optional n)
|
2017-02-16 01:43:55 +00:00
|
|
|
|
"Advance N characters forward."
|
2017-09-15 13:49:27 +00:00
|
|
|
|
(inline-quote (forward-char ,n)))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
2017-09-15 13:49:27 +00:00
|
|
|
|
(define-inline json-peek ()
|
2008-02-21 19:41:38 +00:00
|
|
|
|
"Return the character at point."
|
2017-09-15 13:49:27 +00:00
|
|
|
|
(inline-quote (following-char)))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
2017-09-15 13:49:27 +00:00
|
|
|
|
(define-inline json-pop ()
|
2008-02-21 19:41:38 +00:00
|
|
|
|
"Advance past the character at point, returning it."
|
2017-09-15 14:06:56 +00:00
|
|
|
|
(inline-quote
|
|
|
|
|
(let ((char (json-peek)))
|
|
|
|
|
(if (zerop char)
|
2017-09-15 13:49:27 +00:00
|
|
|
|
(signal 'json-end-of-file nil)
|
|
|
|
|
(json-advance)
|
2017-09-15 14:06:56 +00:00
|
|
|
|
char))))
|
2017-09-15 13:49:27 +00:00
|
|
|
|
|
|
|
|
|
(define-inline json-skip-whitespace ()
|
2008-02-21 19:41:38 +00:00
|
|
|
|
"Skip past the whitespace at point."
|
2017-05-20 15:49:06 +00:00
|
|
|
|
;; See
|
|
|
|
|
;; https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
|
|
|
|
|
;; or https://tools.ietf.org/html/rfc7159#section-2 for the
|
|
|
|
|
;; definition of whitespace in JSON.
|
2017-09-15 13:49:27 +00:00
|
|
|
|
(inline-quote (skip-chars-forward "\t\r\n ")))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;; Error conditions
|
|
|
|
|
|
* lisp/subr.el (define-error): New function.
* doc/lispref/control.texi (Signaling Errors): Refer to define-error.
(Error Symbols): Add `define-error'.
* doc/lispref/errors.texi (Standard Errors): Don't refer to `error-conditions'.
* lisp/progmodes/ada-xref.el (ada-error-file-not-found): Rename from
error-file-not-found and define with define-error.
* lisp/emacs-lisp/cl-lib.el (cl-assertion-failed): Move here from subr.el
and define with define-error.
* lisp/userlock.el (file-locked, file-supersession):
* lisp/simple.el (mark-inactive):
* lisp/progmodes/js.el (js-moz-bad-rpc, js-js-error):
* lisp/progmodes/ada-mode.el (ada-mode-errors):
* lisp/play/life.el (life-extinct):
* lisp/nxml/xsd-regexp.el (xsdre-invalid-regexp, xsdre-parse-error):
* lisp/nxml/xmltok.el (xmltok-markup-declaration-parse-error):
* lisp/nxml/rng-util.el (rng-error):
* lisp/nxml/rng-uri.el (rng-uri-error):
* lisp/nxml/rng-match.el (rng-compile-error):
* lisp/nxml/rng-cmpct.el (rng-c-incorrect-schema):
* lisp/nxml/nxml-util.el (nxml-error, nxml-file-parse-error):
* lisp/nxml/nxml-rap.el (nxml-scan-error):
* lisp/nxml/nxml-outln.el (nxml-outline-error):
* lisp/net/soap-client.el (soap-error):
* lisp/net/gnutls.el (gnutls-error):
* lisp/net/ange-ftp.el (ftp-error):
* lisp/mpc.el (mpc-proc-error):
* lisp/json.el (json-error, json-readtable-error, json-unknown-keyword)
(json-number-format, json-string-escape, json-string-format)
(json-key-format, json-object-format):
* lisp/jka-compr.el (compression-error):
* lisp/international/quail.el (quail-error):
* lisp/international/kkc.el (kkc-error):
* lisp/emacs-lisp/ert.el (ert-test-failed):
* lisp/calc/calc.el (calc-error, inexact-result, math-overflow)
(math-underflow):
* lisp/bookmark.el (bookmark-error-no-filename):
* lisp/epg.el (epg-error): Define with define-error.
2013-08-09 21:22:44 +00:00
|
|
|
|
(define-error 'json-error "Unknown JSON error")
|
|
|
|
|
(define-error 'json-readtable-error "JSON readtable error" 'json-error)
|
|
|
|
|
(define-error 'json-unknown-keyword "Unrecognized keyword" 'json-error)
|
|
|
|
|
(define-error 'json-number-format "Invalid number format" 'json-error)
|
|
|
|
|
(define-error 'json-string-escape "Bad Unicode escape" 'json-error)
|
|
|
|
|
(define-error 'json-string-format "Bad string format" 'json-error)
|
|
|
|
|
(define-error 'json-key-format "Bad JSON object key" 'json-error)
|
|
|
|
|
(define-error 'json-object-format "Bad JSON object" 'json-error)
|
2015-02-05 19:52:03 +00:00
|
|
|
|
(define-error 'json-end-of-file "End of file while parsing JSON"
|
|
|
|
|
'(end-of-file json-error))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-11-08 20:44:21 +00:00
|
|
|
|
;;; Paths
|
|
|
|
|
|
|
|
|
|
(defvar json--path '()
|
|
|
|
|
"Used internally by `json-path-to-position' to keep track of
|
|
|
|
|
the path during recursive calls to `json-read'.")
|
|
|
|
|
|
|
|
|
|
(defun json--record-path (key)
|
|
|
|
|
"Record the KEY to the current JSON path.
|
|
|
|
|
Used internally by `json-path-to-position'."
|
|
|
|
|
(push (cons (point) key) json--path))
|
|
|
|
|
|
|
|
|
|
(defun json--check-position (position)
|
|
|
|
|
"Check if the last parsed JSON structure passed POSITION.
|
|
|
|
|
Used internally by `json-path-to-position'."
|
|
|
|
|
(let ((start (caar json--path)))
|
|
|
|
|
(when (< start position (+ (point) 1))
|
|
|
|
|
(throw :json-path (list :path (nreverse (mapcar #'cdr json--path))
|
|
|
|
|
:match-start start
|
|
|
|
|
:match-end (point)))))
|
|
|
|
|
(pop json--path))
|
|
|
|
|
|
|
|
|
|
(defun json-path-to-position (position &optional string)
|
|
|
|
|
"Return the path to the JSON element at POSITION.
|
|
|
|
|
|
|
|
|
|
When STRING is provided, return the path to the position in the
|
|
|
|
|
string, else to the position in the current buffer.
|
|
|
|
|
|
|
|
|
|
The return value is a property list with the following
|
|
|
|
|
properties:
|
|
|
|
|
|
|
|
|
|
:path -- A list of strings and numbers forming the path to
|
|
|
|
|
the JSON element at the given position. Strings
|
|
|
|
|
denote object names, while numbers denote array
|
|
|
|
|
indexes.
|
|
|
|
|
|
|
|
|
|
:match-start -- Position where the matched JSON element begins.
|
|
|
|
|
|
|
|
|
|
:match-end -- Position where the matched JSON element ends.
|
|
|
|
|
|
|
|
|
|
This can for instance be useful to determine the path to a JSON
|
|
|
|
|
element in a deeply nested structure."
|
|
|
|
|
(save-excursion
|
|
|
|
|
(unless string
|
|
|
|
|
(goto-char (point-min)))
|
|
|
|
|
(let* ((json--path '())
|
|
|
|
|
(json-pre-element-read-function #'json--record-path)
|
|
|
|
|
(json-post-element-read-function
|
|
|
|
|
(apply-partially #'json--check-position position))
|
|
|
|
|
(path (catch :json-path
|
|
|
|
|
(if string
|
|
|
|
|
(json-read-from-string string)
|
|
|
|
|
(json-read)))))
|
|
|
|
|
(when (plist-get path :path)
|
|
|
|
|
path))))
|
|
|
|
|
|
2008-02-21 19:41:38 +00:00
|
|
|
|
;;; Keywords
|
|
|
|
|
|
|
|
|
|
(defvar json-keywords '("true" "false" "null")
|
|
|
|
|
"List of JSON keywords.")
|
|
|
|
|
|
|
|
|
|
;; Keyword parsing
|
|
|
|
|
|
|
|
|
|
(defun json-read-keyword (keyword)
|
|
|
|
|
"Read a JSON keyword at point.
|
|
|
|
|
KEYWORD is the keyword expected."
|
|
|
|
|
(unless (member keyword json-keywords)
|
|
|
|
|
(signal 'json-unknown-keyword (list keyword)))
|
|
|
|
|
(mapc (lambda (char)
|
2017-02-19 01:25:50 +00:00
|
|
|
|
(when (/= char (json-peek))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(signal 'json-unknown-keyword
|
|
|
|
|
(list (save-excursion
|
Fix problems caused by new implementation of sub-word mode
* lisp/subr.el (forward-word-strictly, backward-word-strictly):
New functions.
(word-move-empty-char-table): New variable.
* etc/NEWS: Mention 'forward-word-strictly' and
'backward-word-strictly'.
* doc/lispref/positions.texi (Word Motion): Document
'find-word-boundary-function-table', 'forward-word-strictly', and
'backward-word-strictly'. (Bug#22560)
* src/syntax.c (syms_of_syntax)
<find-word-boundary-function-table>: Doc fix.
* lisp/wdired.el (wdired-xcase-word):
* lisp/textmodes/texnfo-upd.el (texinfo-copy-node-name)
(texinfo-copy-section-title, texinfo-start-menu-description)
(texinfo-copy-menu-title, texinfo-specific-section-type)
(texinfo-insert-node-lines, texinfo-copy-next-section-title):
* lisp/textmodes/texinfo.el (texinfo-clone-environment)
(texinfo-insert-@end):
* lisp/textmodes/texinfmt.el (texinfo-format-scan)
(texinfo-anchor, texinfo-multitable-widths)
(texinfo-multitable-item):
* lisp/textmodes/tex-mode.el (latex-env-before-change):
* lisp/textmodes/flyspell.el (texinfo-mode-flyspell-verify):
* lisp/skeleton.el (skeleton-insert):
* lisp/simple.el (count-words):
* lisp/progmodes/vhdl-mode.el (vhdl-beginning-of-libunit)
(vhdl-beginning-of-defun, vhdl-beginning-of-statement-1)
(vhdl-update-sensitivity-list, vhdl-template-block)
(vhdl-template-break, vhdl-template-case, vhdl-template-default)
(vhdl-template-default-indent, vhdl-template-for-loop)
(vhdl-template-if-then-use, vhdl-template-bare-loop)
(vhdl-template-nature, vhdl-template-procedural)
(vhdl-template-process, vhdl-template-selected-signal-asst)
(vhdl-template-type, vhdl-template-variable)
(vhdl-template-while-loop, vhdl-beginning-of-block)
(vhdl-hooked-abbrev, vhdl-port-copy, vhdl-hs-forward-sexp-func):
* lisp/progmodes/verilog-mode.el (verilog-backward-sexp)
(verilog-forward-sexp, verilog-beg-of-statement)
(verilog-set-auto-endcomments, verilog-backward-token)
(verilog-do-indent):
* lisp/progmodes/vera-mode.el (vera-guess-basic-syntax)
(vera-indent-block-closing):
* lisp/progmodes/simula.el (simula-context)
(simula-backward-up-level, simula-forward-down-level)
(simula-previous-statement, simula-next-statement)
(simula-skip-comment-backward, simula-calculate-indent)
(simula-find-if, simula-electric-keyword):
* lisp/progmodes/sh-script.el (sh-smie--rc-newline-semi-p):
* lisp/progmodes/ruby-mode.el (ruby-smie--redundant-do-p)
(ruby-smie--forward-token, ruby-smie--backward-token)
(ruby-singleton-class-p, ruby-calculate-indent)
(ruby-forward-sexp, ruby-backward-sexp):
* lisp/progmodes/ps-mode.el (ps-run-goto-error):
* lisp/progmodes/perl-mode.el (perl-syntax-propertize-function)
(perl-syntax-propertize-special-constructs)
(perl-backward-to-start-of-continued-exp):
* lisp/progmodes/pascal.el (pascal-indent-declaration):
* lisp/progmodes/octave.el (octave-function-file-p):
* lisp/progmodes/mantemp.el (mantemp-insert-cxx-syntax):
* lisp/progmodes/js.el (js--forward-function-decl):
* lisp/progmodes/idlwave.el (idlwave-show-begin-check)
(idlwave-beginning-of-block, idlwave-end-of-block)
(idlwave-block-jump-out, idlwave-determine-class):
* lisp/progmodes/icon.el (icon-is-continuation-line)
(icon-backward-to-start-of-continued-exp, end-of-icon-defun):
* lisp/progmodes/hideif.el (hide-ifdef-define):
* lisp/progmodes/f90.el (f90-change-keywords):
* lisp/progmodes/cperl-mode.el (cperl-electric-pod)
(cperl-linefeed, cperl-electric-terminator)
(cperl-find-pods-heres, cperl-fix-line-spacing)
(cperl-invert-if-unless):
* lisp/progmodes/cc-engine.el (c-forward-<>-arglist-recur):
* lisp/progmodes/cc-align.el (c-lineup-java-inher):
* lisp/progmodes/ada-mode.el (ada-compile-goto-error)
(ada-adjust-case-skeleton, ada-create-case-exception)
(ada-create-case-exception-substring)
(ada-case-read-exceptions-from-file, ada-after-keyword-p)
(ada-scan-paramlist, ada-get-current-indent, ada-get-indent-end)
(ada-get-indent-if, ada-get-indent-block-start)
(ada-get-indent-loop, ada-get-indent-type)
(ada-search-prev-end-stmt, ada-check-defun-name)
(ada-goto-decl-start, ada-goto-matching-start)
(ada-goto-matching-end, ada-looking-at-semi-or)
(ada-looking-at-semi-private, ada-in-paramlist-p)
(ada-search-ignore-complex-boolean, ada-move-to-start)
(ada-move-to-end, ada-which-function, ada-gen-treat-proc):
* lisp/net/quickurl.el (quickurl-grab-url):
* lisp/mail/sendmail.el (mail-do-fcc):
* lisp/mail/rmail.el (rmail-resend):
* lisp/mail/mailabbrev.el (mail-abbrev-complete-alias):
* lisp/mail/mail-extr.el (mail-extract-address-components):
* lisp/json.el (json-read-keyword):
* lisp/files.el (insert-directory):
* lisp/emacs-lisp/checkdoc.el (checkdoc-this-string-valid-engine):
* lisp/completion.el (symbol-under-point, symbol-before-point)
(symbol-before-point-for-complete, next-cdabbrev)
(add-completions-from-c-buffer):
* lisp/cedet/semantic/texi.el (semantic-up-context)
(semantic-beginning-of-context):
* lisp/cedet/semantic/bovine/el.el (semantic-get-local-variables):
use 'forward-word-strictly' and 'backward-word-strictly' instead
of 'forward-word' and 'backward-word'.
[This reapplies commit c1d32a65372c72d7de4808d620eefd3214a8e92a,
which was inadvertently lost by merge commit
c71e7cc113ed0d5f01aaa2e441a3e3c9fbeb9fa5.]
2016-03-22 00:42:35 +00:00
|
|
|
|
(backward-word-strictly 1)
|
2008-02-22 03:56:25 +00:00
|
|
|
|
(thing-at-point 'word)))))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(json-advance))
|
|
|
|
|
keyword)
|
2017-09-15 13:49:27 +00:00
|
|
|
|
(json-skip-whitespace)
|
2017-09-15 19:37:25 +00:00
|
|
|
|
(unless (looking-at "\\([],}]\\|$\\)")
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(signal 'json-unknown-keyword
|
|
|
|
|
(list (save-excursion
|
Fix problems caused by new implementation of sub-word mode
* lisp/subr.el (forward-word-strictly, backward-word-strictly):
New functions.
(word-move-empty-char-table): New variable.
* etc/NEWS: Mention 'forward-word-strictly' and
'backward-word-strictly'.
* doc/lispref/positions.texi (Word Motion): Document
'find-word-boundary-function-table', 'forward-word-strictly', and
'backward-word-strictly'. (Bug#22560)
* src/syntax.c (syms_of_syntax)
<find-word-boundary-function-table>: Doc fix.
* lisp/wdired.el (wdired-xcase-word):
* lisp/textmodes/texnfo-upd.el (texinfo-copy-node-name)
(texinfo-copy-section-title, texinfo-start-menu-description)
(texinfo-copy-menu-title, texinfo-specific-section-type)
(texinfo-insert-node-lines, texinfo-copy-next-section-title):
* lisp/textmodes/texinfo.el (texinfo-clone-environment)
(texinfo-insert-@end):
* lisp/textmodes/texinfmt.el (texinfo-format-scan)
(texinfo-anchor, texinfo-multitable-widths)
(texinfo-multitable-item):
* lisp/textmodes/tex-mode.el (latex-env-before-change):
* lisp/textmodes/flyspell.el (texinfo-mode-flyspell-verify):
* lisp/skeleton.el (skeleton-insert):
* lisp/simple.el (count-words):
* lisp/progmodes/vhdl-mode.el (vhdl-beginning-of-libunit)
(vhdl-beginning-of-defun, vhdl-beginning-of-statement-1)
(vhdl-update-sensitivity-list, vhdl-template-block)
(vhdl-template-break, vhdl-template-case, vhdl-template-default)
(vhdl-template-default-indent, vhdl-template-for-loop)
(vhdl-template-if-then-use, vhdl-template-bare-loop)
(vhdl-template-nature, vhdl-template-procedural)
(vhdl-template-process, vhdl-template-selected-signal-asst)
(vhdl-template-type, vhdl-template-variable)
(vhdl-template-while-loop, vhdl-beginning-of-block)
(vhdl-hooked-abbrev, vhdl-port-copy, vhdl-hs-forward-sexp-func):
* lisp/progmodes/verilog-mode.el (verilog-backward-sexp)
(verilog-forward-sexp, verilog-beg-of-statement)
(verilog-set-auto-endcomments, verilog-backward-token)
(verilog-do-indent):
* lisp/progmodes/vera-mode.el (vera-guess-basic-syntax)
(vera-indent-block-closing):
* lisp/progmodes/simula.el (simula-context)
(simula-backward-up-level, simula-forward-down-level)
(simula-previous-statement, simula-next-statement)
(simula-skip-comment-backward, simula-calculate-indent)
(simula-find-if, simula-electric-keyword):
* lisp/progmodes/sh-script.el (sh-smie--rc-newline-semi-p):
* lisp/progmodes/ruby-mode.el (ruby-smie--redundant-do-p)
(ruby-smie--forward-token, ruby-smie--backward-token)
(ruby-singleton-class-p, ruby-calculate-indent)
(ruby-forward-sexp, ruby-backward-sexp):
* lisp/progmodes/ps-mode.el (ps-run-goto-error):
* lisp/progmodes/perl-mode.el (perl-syntax-propertize-function)
(perl-syntax-propertize-special-constructs)
(perl-backward-to-start-of-continued-exp):
* lisp/progmodes/pascal.el (pascal-indent-declaration):
* lisp/progmodes/octave.el (octave-function-file-p):
* lisp/progmodes/mantemp.el (mantemp-insert-cxx-syntax):
* lisp/progmodes/js.el (js--forward-function-decl):
* lisp/progmodes/idlwave.el (idlwave-show-begin-check)
(idlwave-beginning-of-block, idlwave-end-of-block)
(idlwave-block-jump-out, idlwave-determine-class):
* lisp/progmodes/icon.el (icon-is-continuation-line)
(icon-backward-to-start-of-continued-exp, end-of-icon-defun):
* lisp/progmodes/hideif.el (hide-ifdef-define):
* lisp/progmodes/f90.el (f90-change-keywords):
* lisp/progmodes/cperl-mode.el (cperl-electric-pod)
(cperl-linefeed, cperl-electric-terminator)
(cperl-find-pods-heres, cperl-fix-line-spacing)
(cperl-invert-if-unless):
* lisp/progmodes/cc-engine.el (c-forward-<>-arglist-recur):
* lisp/progmodes/cc-align.el (c-lineup-java-inher):
* lisp/progmodes/ada-mode.el (ada-compile-goto-error)
(ada-adjust-case-skeleton, ada-create-case-exception)
(ada-create-case-exception-substring)
(ada-case-read-exceptions-from-file, ada-after-keyword-p)
(ada-scan-paramlist, ada-get-current-indent, ada-get-indent-end)
(ada-get-indent-if, ada-get-indent-block-start)
(ada-get-indent-loop, ada-get-indent-type)
(ada-search-prev-end-stmt, ada-check-defun-name)
(ada-goto-decl-start, ada-goto-matching-start)
(ada-goto-matching-end, ada-looking-at-semi-or)
(ada-looking-at-semi-private, ada-in-paramlist-p)
(ada-search-ignore-complex-boolean, ada-move-to-start)
(ada-move-to-end, ada-which-function, ada-gen-treat-proc):
* lisp/net/quickurl.el (quickurl-grab-url):
* lisp/mail/sendmail.el (mail-do-fcc):
* lisp/mail/rmail.el (rmail-resend):
* lisp/mail/mailabbrev.el (mail-abbrev-complete-alias):
* lisp/mail/mail-extr.el (mail-extract-address-components):
* lisp/json.el (json-read-keyword):
* lisp/files.el (insert-directory):
* lisp/emacs-lisp/checkdoc.el (checkdoc-this-string-valid-engine):
* lisp/completion.el (symbol-under-point, symbol-before-point)
(symbol-before-point-for-complete, next-cdabbrev)
(add-completions-from-c-buffer):
* lisp/cedet/semantic/texi.el (semantic-up-context)
(semantic-beginning-of-context):
* lisp/cedet/semantic/bovine/el.el (semantic-get-local-variables):
use 'forward-word-strictly' and 'backward-word-strictly' instead
of 'forward-word' and 'backward-word'.
[This reapplies commit c1d32a65372c72d7de4808d620eefd3214a8e92a,
which was inadvertently lost by merge commit
c71e7cc113ed0d5f01aaa2e441a3e3c9fbeb9fa5.]
2016-03-22 00:42:35 +00:00
|
|
|
|
(backward-word-strictly 1)
|
2008-02-22 03:56:25 +00:00
|
|
|
|
(thing-at-point 'word)))))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(cond ((string-equal keyword "true") t)
|
|
|
|
|
((string-equal keyword "false") json-false)
|
|
|
|
|
((string-equal keyword "null") json-null)))
|
|
|
|
|
|
|
|
|
|
;; Keyword encoding
|
|
|
|
|
|
|
|
|
|
(defun json-encode-keyword (keyword)
|
|
|
|
|
"Encode KEYWORD as a JSON value."
|
|
|
|
|
(cond ((eq keyword t) "true")
|
|
|
|
|
((eq keyword json-false) "false")
|
|
|
|
|
((eq keyword json-null) "null")))
|
|
|
|
|
|
|
|
|
|
;;; Numbers
|
|
|
|
|
|
|
|
|
|
;; Number parsing
|
|
|
|
|
|
2008-08-28 20:19:17 +00:00
|
|
|
|
(defun json-read-number (&optional sign)
|
|
|
|
|
"Read the JSON number following point.
|
Fix typos in docstrings.
* image-dired.el (image-dired-display-thumbs): Fix typo in docstring.
(image-dired-read-comment): Doc fix.
* json.el (json-object-type, json-array-type, json-key-type, json-false)
(json-null, json-read-number):
* minibuffer.el (completion-in-region-functions):
* calendar/cal-tex.el (cal-tex-daily-end, cal-tex-number-weeks)
(cal-tex-cursor-week):
* emacs-lisp/trace.el (trace-function):
* eshell/em-basic.el (eshell/printnl):
* eshell/em-dirs.el (eshell-last-dir-ring, eshell-parse-drive-letter)
(eshell-read-last-dir-ring, eshell-write-last-dir-ring):
* obsolete/levents.el (allocate-event, event-key, event-object)
(event-point, event-process, event-timestamp, event-to-character)
(event-window, event-x, event-x-pixel, event-y, event-y-pixel):
* textmodes/reftex-vars.el (reftex-index-macros-builtin)
(reftex-section-levels, reftex-auto-recenter-toc, reftex-toc-mode-hook)
(reftex-cite-punctuation, reftex-search-unrecursed-path-first)
(reftex-highlight-selection): Fix typos in docstrings.
2010-03-22 16:50:29 +00:00
|
|
|
|
The optional SIGN argument is for internal use.
|
2008-08-28 20:19:17 +00:00
|
|
|
|
|
2008-02-21 19:41:38 +00:00
|
|
|
|
N.B.: Only numbers which can fit in Emacs Lisp's native number
|
|
|
|
|
representation will be parsed correctly."
|
2008-08-28 20:19:17 +00:00
|
|
|
|
;; If SIGN is non-nil, the number is explicitly signed.
|
|
|
|
|
(let ((number-regexp
|
|
|
|
|
"\\([0-9]+\\)?\\(\\.[0-9]+\\)?\\([Ee][+-]?[0-9]+\\)?"))
|
2017-02-19 01:25:50 +00:00
|
|
|
|
(cond ((and (null sign) (= (json-peek) ?-))
|
2008-08-28 20:19:17 +00:00
|
|
|
|
(json-advance)
|
|
|
|
|
(- (json-read-number t)))
|
2017-02-19 01:25:50 +00:00
|
|
|
|
((and (null sign) (= (json-peek) ?+))
|
2008-08-28 20:19:17 +00:00
|
|
|
|
(json-advance)
|
|
|
|
|
(json-read-number t))
|
|
|
|
|
((and (looking-at number-regexp)
|
|
|
|
|
(or (match-beginning 1)
|
|
|
|
|
(match-beginning 2)))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(goto-char (match-end 0))
|
|
|
|
|
(string-to-number (match-string 0)))
|
2008-08-28 20:19:17 +00:00
|
|
|
|
(t (signal 'json-number-format (list (point)))))))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
|
|
|
|
;; Number encoding
|
|
|
|
|
|
|
|
|
|
(defun json-encode-number (number)
|
|
|
|
|
"Return a JSON representation of NUMBER."
|
|
|
|
|
(format "%s" number))
|
|
|
|
|
|
|
|
|
|
;;; Strings
|
|
|
|
|
|
|
|
|
|
(defvar json-special-chars
|
|
|
|
|
'((?\" . ?\")
|
|
|
|
|
(?\\ . ?\\)
|
|
|
|
|
(?b . ?\b)
|
|
|
|
|
(?f . ?\f)
|
|
|
|
|
(?n . ?\n)
|
|
|
|
|
(?r . ?\r)
|
|
|
|
|
(?t . ?\t))
|
|
|
|
|
"Characters which are escaped in JSON, with their elisp counterparts.")
|
|
|
|
|
|
|
|
|
|
;; String parsing
|
|
|
|
|
|
2016-10-24 19:54:51 +00:00
|
|
|
|
(defun json--decode-utf-16-surrogates (high low)
|
|
|
|
|
"Return the code point represented by the UTF-16 surrogates HIGH and LOW."
|
Audit use of lsh and fix glitches
I audited use of lsh in the Lisp source code, and fixed the
glitches that I found. While I was at it, I replaced uses of lsh
with ash when either will do. Replacement is OK when either
argument is known to be nonnegative, or when only the low-order
bits of the result matter, and is a (minor) win since ash is a bit
more solid than lsh nowadays, and is a bit faster.
* lisp/calc/calc-ext.el (math-check-fixnum):
Prefer most-positive-fixnum to (lsh -1 -1).
* lisp/vc/vc-hg.el (vc-hg-state-fast): When testing fixnum width,
prefer (zerop (ash most-positive-fixnum -32)) to (zerop (lsh -1
32)) (Bug#32485#11).
* lisp/emacs-lisp/bytecomp.el (byte-compile-lapcode):
Tighten sanity-check for bytecode overflow, by checking that the
result of (ash pc -8) is nonnegative. Formerly this check was not
needed since lsh was used and the number overflowed differently.
* lisp/net/dns.el (dns-write): Fix some obvious sign typos in
shift counts. Evidently this part of the code has never been
exercised.
* lisp/progmodes/hideif.el (hif-shiftleft, hif-shiftright):
* lisp/term/common-win.el (x-setup-function-keys):
Simplify.
* admin/unidata/unidata-gen.el, admin/unidata/uvs.el:
* doc/lispref/keymaps.texi, doc/lispref/syntax.texi:
* doc/misc/calc.texi, doc/misc/cl.texi, etc/NEWS.19:
* lisp/arc-mode.el, lisp/calc/calc-bin.el, lisp/calc/calc-comb.el:
* lisp/calc/calc-ext.el, lisp/calc/calc-math.el:
* lisp/cedet/semantic/wisent/comp.el, lisp/composite.el:
* lisp/disp-table.el, lisp/dos-fns.el, lisp/edmacro.el:
* lisp/emacs-lisp/bindat.el, lisp/emacs-lisp/byte-opt.el:
* lisp/emacs-lisp/bytecomp.el, lisp/emacs-lisp/cl-extra.el:
* lisp/erc/erc-dcc.el, lisp/facemenu.el, lisp/gnus/message.el:
* lisp/gnus/nndoc.el, lisp/gnus/nnmaildir.el, lisp/image.el:
* lisp/international/ccl.el, lisp/international/fontset.el:
* lisp/international/mule-cmds.el, lisp/international/mule.el:
* lisp/json.el, lisp/mail/binhex.el, lisp/mail/rmail.el:
* lisp/mail/uudecode.el, lisp/md4.el, lisp/net/dns.el:
* lisp/net/ntlm.el, lisp/net/sasl.el, lisp/net/socks.el:
* lisp/net/tramp.el, lisp/obsolete/levents.el:
* lisp/obsolete/pgg-parse.el, lisp/org/org.el:
* lisp/org/ox-publish.el, lisp/progmodes/cc-defs.el:
* lisp/progmodes/ebnf2ps.el, lisp/progmodes/hideif.el:
* lisp/ps-bdf.el, lisp/ps-print.el, lisp/simple.el:
* lisp/tar-mode.el, lisp/term/common-win.el:
* lisp/term/tty-colors.el, lisp/term/xterm.el, lisp/vc/vc-git.el:
* lisp/vc/vc-hg.el, lisp/x-dnd.el, test/src/data-tests.el:
Prefer ash to lsh when either will do.
2018-08-21 20:44:03 +00:00
|
|
|
|
(+ (ash (- high #xD800) 10) (- low #xDC00) #x10000))
|
2016-10-24 19:54:51 +00:00
|
|
|
|
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(defun json-read-escaped-char ()
|
|
|
|
|
"Read the JSON string escaped character at point."
|
|
|
|
|
;; Skip over the '\'
|
|
|
|
|
(json-advance)
|
|
|
|
|
(let* ((char (json-pop))
|
|
|
|
|
(special (assq char json-special-chars)))
|
|
|
|
|
(cond
|
|
|
|
|
(special (cdr special))
|
|
|
|
|
((not (eq char ?u)) char)
|
2016-10-24 19:54:51 +00:00
|
|
|
|
;; Special-case UTF-16 surrogate pairs,
|
2017-08-18 00:00:52 +00:00
|
|
|
|
;; cf. <https://tools.ietf.org/html/rfc7159#section-7>. Note that
|
2016-10-24 19:54:51 +00:00
|
|
|
|
;; this clause overlaps with the next one and therefore has to
|
|
|
|
|
;; come first.
|
|
|
|
|
((looking-at
|
2017-02-16 01:43:55 +00:00
|
|
|
|
(rx (group (any "Dd") (any "89ABab") (= 2 (any xdigit)))
|
|
|
|
|
"\\u" (group (any "Dd") (any "C-Fc-f") (= 2 (any xdigit)))))
|
2016-10-24 19:54:51 +00:00
|
|
|
|
(json-advance 10)
|
|
|
|
|
(json--decode-utf-16-surrogates
|
|
|
|
|
(string-to-number (match-string 1) 16)
|
|
|
|
|
(string-to-number (match-string 2) 16)))
|
2017-02-16 01:43:55 +00:00
|
|
|
|
((looking-at (rx (= 4 xdigit)))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(let ((hex (match-string 0)))
|
|
|
|
|
(json-advance 4)
|
2015-03-23 16:00:39 +00:00
|
|
|
|
(string-to-number hex 16)))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(t
|
|
|
|
|
(signal 'json-string-escape (list (point)))))))
|
|
|
|
|
|
|
|
|
|
(defun json-read-string ()
|
|
|
|
|
"Read the JSON string at point."
|
2017-02-16 01:43:55 +00:00
|
|
|
|
(unless (= (json-peek) ?\")
|
Go back to grave quoting in source-code docstrings etc.
This reverts almost all my recent changes to use curved quotes
in docstrings and/or strings used for error diagnostics.
There are a few exceptions, e.g., Bahá’í proper names.
* admin/unidata/unidata-gen.el (unidata-gen-table):
* lisp/abbrev.el (expand-region-abbrevs):
* lisp/align.el (align-region):
* lisp/allout.el (allout-mode, allout-solicit-alternate-bullet)
(outlineify-sticky):
* lisp/apropos.el (apropos-library):
* lisp/bookmark.el (bookmark-default-annotation-text):
* lisp/button.el (button-category-symbol, button-put)
(make-text-button):
* lisp/calc/calc-aent.el (math-read-if, math-read-factor):
* lisp/calc/calc-embed.el (calc-do-embedded):
* lisp/calc/calc-ext.el (calc-user-function-list):
* lisp/calc/calc-graph.el (calc-graph-show-dumb):
* lisp/calc/calc-help.el (calc-describe-key)
(calc-describe-thing, calc-full-help):
* lisp/calc/calc-lang.el (calc-c-language)
(math-parse-fortran-vector-end, math-parse-tex-sum)
(math-parse-eqn-matrix, math-parse-eqn-prime)
(calc-yacas-language, calc-maxima-language, calc-giac-language)
(math-read-giac-subscr, math-read-math-subscr)
(math-read-big-rec, math-read-big-balance):
* lisp/calc/calc-misc.el (calc-help, report-calc-bug):
* lisp/calc/calc-mode.el (calc-auto-why, calc-save-modes)
(calc-auto-recompute):
* lisp/calc/calc-prog.el (calc-fix-token-name)
(calc-read-parse-table-part, calc-user-define-invocation)
(math-do-arg-check):
* lisp/calc/calc-store.el (calc-edit-variable):
* lisp/calc/calc-units.el (math-build-units-table-buffer):
* lisp/calc/calc-vec.el (math-read-brackets):
* lisp/calc/calc-yank.el (calc-edit-mode):
* lisp/calc/calc.el (calc, calc-do, calc-user-invocation):
* lisp/calendar/appt.el (appt-display-message):
* lisp/calendar/diary-lib.el (diary-check-diary-file)
(diary-mail-entries, diary-from-outlook):
* lisp/calendar/icalendar.el (icalendar-export-region)
(icalendar--convert-float-to-ical)
(icalendar--convert-date-to-ical)
(icalendar--convert-ical-to-diary)
(icalendar--convert-recurring-to-diary)
(icalendar--add-diary-entry):
* lisp/calendar/time-date.el (format-seconds):
* lisp/calendar/timeclock.el (timeclock-mode-line-display)
(timeclock-make-hours-explicit, timeclock-log-data):
* lisp/calendar/todo-mode.el (todo-prefix, todo-delete-category)
(todo-item-mark, todo-check-format)
(todo-insert-item--next-param, todo-edit-item--next-key)
(todo-mode):
* lisp/cedet/ede/pmake.el (ede-proj-makefile-insert-dist-rules):
* lisp/cedet/mode-local.el (describe-mode-local-overload)
(mode-local-print-binding, mode-local-describe-bindings-2):
* lisp/cedet/semantic/complete.el (semantic-displayor-show-request):
* lisp/cedet/srecode/srt-mode.el (srecode-macro-help):
* lisp/cus-start.el (standard):
* lisp/cus-theme.el (describe-theme-1):
* lisp/custom.el (custom-add-dependencies, custom-check-theme)
(custom--sort-vars-1, load-theme):
* lisp/descr-text.el (describe-text-properties-1, describe-char):
* lisp/dired-x.el (dired-do-run-mail):
* lisp/dired.el (dired-log):
* lisp/emacs-lisp/advice.el (ad-read-advised-function)
(ad-read-advice-class, ad-read-advice-name, ad-enable-advice)
(ad-disable-advice, ad-remove-advice, ad-set-argument)
(ad-set-arguments, ad--defalias-fset, ad-activate)
(ad-deactivate):
* lisp/emacs-lisp/byte-opt.el (byte-compile-inline-expand)
(byte-compile-unfold-lambda, byte-optimize-form-code-walker)
(byte-optimize-while, byte-optimize-apply):
* lisp/emacs-lisp/byte-run.el (defun, defsubst):
* lisp/emacs-lisp/bytecomp.el (byte-compile-lapcode)
(byte-compile-log-file, byte-compile-format-warn)
(byte-compile-nogroup-warn, byte-compile-arglist-warn)
(byte-compile-cl-warn)
(byte-compile-warn-about-unresolved-functions)
(byte-compile-file, byte-compile--declare-var)
(byte-compile-file-form-defmumble, byte-compile-form)
(byte-compile-normal-call, byte-compile-check-variable)
(byte-compile-variable-ref, byte-compile-variable-set)
(byte-compile-subr-wrong-args, byte-compile-setq-default)
(byte-compile-negation-optimizer)
(byte-compile-condition-case--old)
(byte-compile-condition-case--new, byte-compile-save-excursion)
(byte-compile-defvar, byte-compile-autoload)
(byte-compile-lambda-form)
(byte-compile-make-variable-buffer-local, display-call-tree)
(batch-byte-compile):
* lisp/emacs-lisp/cconv.el (cconv-convert, cconv--analyze-use):
* lisp/emacs-lisp/chart.el (chart-space-usage):
* lisp/emacs-lisp/check-declare.el (check-declare-scan)
(check-declare-warn, check-declare-file)
(check-declare-directory):
* lisp/emacs-lisp/checkdoc.el (checkdoc-this-string-valid-engine)
(checkdoc-message-text-engine):
* lisp/emacs-lisp/cl-extra.el (cl-parse-integer)
(cl--describe-class):
* lisp/emacs-lisp/cl-generic.el (cl-defgeneric)
(cl--generic-describe, cl-generic-generalizers):
* lisp/emacs-lisp/cl-macs.el (cl--parse-loop-clause, cl-tagbody)
(cl-symbol-macrolet):
* lisp/emacs-lisp/cl.el (cl-unload-function, flet):
* lisp/emacs-lisp/copyright.el (copyright)
(copyright-update-directory):
* lisp/emacs-lisp/edebug.el (edebug-read-list):
* lisp/emacs-lisp/eieio-base.el (eieio-persistent-read):
* lisp/emacs-lisp/eieio-core.el (eieio--slot-override)
(eieio-oref):
* lisp/emacs-lisp/eieio-opt.el (eieio-help-constructor):
* lisp/emacs-lisp/eieio-speedbar.el:
(eieio-speedbar-child-make-tag-lines)
(eieio-speedbar-child-description):
* lisp/emacs-lisp/eieio.el (defclass, change-class):
* lisp/emacs-lisp/elint.el (elint-file, elint-get-top-forms)
(elint-init-form, elint-check-defalias-form)
(elint-check-let-form):
* lisp/emacs-lisp/ert.el (ert-get-test, ert-results-mode-menu)
(ert-results-pop-to-backtrace-for-test-at-point)
(ert-results-pop-to-messages-for-test-at-point)
(ert-results-pop-to-should-forms-for-test-at-point)
(ert-describe-test):
* lisp/emacs-lisp/find-func.el (find-function-search-for-symbol)
(find-function-library):
* lisp/emacs-lisp/generator.el (iter-yield):
* lisp/emacs-lisp/gv.el (gv-define-simple-setter):
* lisp/emacs-lisp/lisp-mnt.el (lm-verify):
* lisp/emacs-lisp/macroexp.el (macroexp--obsolete-warning):
* lisp/emacs-lisp/map-ynp.el (map-y-or-n-p):
* lisp/emacs-lisp/nadvice.el (advice--make-docstring)
(advice--make, define-advice):
* lisp/emacs-lisp/package-x.el (package-upload-file):
* lisp/emacs-lisp/package.el (package-version-join)
(package-disabled-p, package-activate-1, package-activate)
(package--download-one-archive)
(package--download-and-read-archives)
(package-compute-transaction, package-install-from-archive)
(package-install, package-install-selected-packages)
(package-delete, package-autoremove, describe-package-1)
(package-install-button-action, package-delete-button-action)
(package-menu-hide-package, package-menu--list-to-prompt)
(package-menu--perform-transaction)
(package-menu--find-and-notify-upgrades):
* lisp/emacs-lisp/pcase.el (pcase-exhaustive, pcase--u1):
* lisp/emacs-lisp/re-builder.el (reb-enter-subexp-mode):
* lisp/emacs-lisp/ring.el (ring-previous, ring-next):
* lisp/emacs-lisp/rx.el (rx-check, rx-anything)
(rx-check-any-string, rx-check-any, rx-check-not, rx-=)
(rx-repeat, rx-check-backref, rx-syntax, rx-check-category)
(rx-form):
* lisp/emacs-lisp/smie.el (smie-config-save):
* lisp/emacs-lisp/subr-x.el (internal--check-binding):
* lisp/emacs-lisp/tabulated-list.el (tabulated-list-put-tag):
* lisp/emacs-lisp/testcover.el (testcover-1value):
* lisp/emacs-lisp/timer.el (timer-event-handler):
* lisp/emulation/viper-cmd.el (viper-toggle-parse-sexp-ignore-comments)
(viper-toggle-search-style, viper-kill-buffer)
(viper-brac-function):
* lisp/emulation/viper-macs.el (viper-record-kbd-macro):
* lisp/env.el (setenv):
* lisp/erc/erc-button.el (erc-nick-popup):
* lisp/erc/erc.el (erc-cmd-LOAD, erc-handle-login, english):
* lisp/eshell/em-dirs.el (eshell/cd):
* lisp/eshell/em-glob.el (eshell-glob-regexp)
(eshell-glob-entries):
* lisp/eshell/em-pred.el (eshell-parse-modifiers):
* lisp/eshell/esh-opt.el (eshell-show-usage):
* lisp/facemenu.el (facemenu-add-new-face)
(facemenu-add-new-color):
* lisp/faces.el (read-face-name, read-face-font, describe-face)
(x-resolve-font-name):
* lisp/files-x.el (modify-file-local-variable):
* lisp/files.el (locate-user-emacs-file, find-alternate-file)
(set-auto-mode, hack-one-local-variable--obsolete)
(dir-locals-set-directory-class, write-file, basic-save-buffer)
(delete-directory, copy-directory, recover-session)
(recover-session-finish, insert-directory)
(file-modes-char-to-who, file-modes-symbolic-to-number)
(move-file-to-trash):
* lisp/filesets.el (filesets-add-buffer, filesets-remove-buffer):
* lisp/find-cmd.el (find-generic, find-to-string):
* lisp/finder.el (finder-commentary):
* lisp/font-lock.el (font-lock-fontify-buffer):
* lisp/format.el (format-write-file, format-find-file)
(format-insert-file):
* lisp/frame.el (get-device-terminal, select-frame-by-name):
* lisp/fringe.el (fringe--check-style):
* lisp/gnus/nnmairix.el (nnmairix-widget-create-query):
* lisp/help-fns.el (help-fns--key-bindings)
(help-fns--compiler-macro, help-fns--parent-mode)
(help-fns--obsolete, help-fns--interactive-only)
(describe-function-1, describe-variable):
* lisp/help.el (describe-mode)
(describe-minor-mode-from-indicator):
* lisp/image.el (image-type):
* lisp/international/ccl.el (ccl-dump):
* lisp/international/fontset.el (x-must-resolve-font-name):
* lisp/international/mule-cmds.el (prefer-coding-system)
(select-safe-coding-system-interactively)
(select-safe-coding-system, activate-input-method)
(toggle-input-method, describe-current-input-method)
(describe-language-environment):
* lisp/international/mule-conf.el (code-offset):
* lisp/international/mule-diag.el (describe-character-set)
(list-input-methods-1):
* lisp/mail/feedmail.el (feedmail-run-the-queue):
* lisp/mouse.el (minor-mode-menu-from-indicator):
* lisp/mpc.el (mpc-playlist-rename):
* lisp/msb.el (msb--choose-menu):
* lisp/net/ange-ftp.el (ange-ftp-shell-command):
* lisp/net/imap.el (imap-interactive-login):
* lisp/net/mairix.el (mairix-widget-create-query):
* lisp/net/newst-backend.el (newsticker--sentinel-work):
* lisp/net/newst-treeview.el (newsticker--treeview-load):
* lisp/net/rlogin.el (rlogin):
* lisp/obsolete/iswitchb.el (iswitchb-possible-new-buffer):
* lisp/obsolete/otodo-mode.el (todo-more-important-p):
* lisp/obsolete/pgg-gpg.el (pgg-gpg-process-region):
* lisp/obsolete/pgg-pgp.el (pgg-pgp-process-region):
* lisp/obsolete/pgg-pgp5.el (pgg-pgp5-process-region):
* lisp/org/ob-core.el (org-babel-goto-named-src-block)
(org-babel-goto-named-result):
* lisp/org/ob-fortran.el (org-babel-fortran-ensure-main-wrap):
* lisp/org/ob-ref.el (org-babel-ref-resolve):
* lisp/org/org-agenda.el (org-agenda-prepare):
* lisp/org/org-clock.el (org-clock-notify-once-if-expired)
(org-clock-resolve):
* lisp/org/org-ctags.el (org-ctags-ask-rebuild-tags-file-then-find-tag):
* lisp/org/org-feed.el (org-feed-parse-atom-entry):
* lisp/org/org-habit.el (org-habit-parse-todo):
* lisp/org/org-mouse.el (org-mouse-popup-global-menu)
(org-mouse-context-menu):
* lisp/org/org-table.el (org-table-edit-formulas):
* lisp/org/ox.el (org-export-async-start):
* lisp/proced.el (proced-log):
* lisp/progmodes/ada-mode.el (ada-get-indent-case)
(ada-check-matching-start, ada-goto-matching-start):
* lisp/progmodes/ada-prj.el (ada-prj-display-page):
* lisp/progmodes/ada-xref.el (ada-find-executable):
* lisp/progmodes/ebrowse.el (ebrowse-tags-apropos):
* lisp/progmodes/etags.el (etags-tags-apropos-additional):
* lisp/progmodes/flymake.el (flymake-parse-err-lines)
(flymake-start-syntax-check-process):
* lisp/progmodes/python.el (python-shell-get-process-or-error)
(python-define-auxiliary-skeleton):
* lisp/progmodes/sql.el (sql-comint):
* lisp/progmodes/verilog-mode.el (verilog-load-file-at-point):
* lisp/progmodes/vhdl-mode.el (vhdl-widget-directory-validate):
* lisp/recentf.el (recentf-open-files):
* lisp/replace.el (query-replace-read-from)
(occur-after-change-function, occur-1):
* lisp/scroll-bar.el (scroll-bar-columns):
* lisp/server.el (server-get-auth-key):
* lisp/simple.el (execute-extended-command)
(undo-outer-limit-truncate, list-processes--refresh)
(compose-mail, set-variable, choose-completion-string)
(define-alternatives):
* lisp/startup.el (site-run-file, tty-handle-args, command-line)
(command-line-1):
* lisp/subr.el (noreturn, define-error, add-to-list)
(read-char-choice, version-to-list):
* lisp/term/common-win.el (x-handle-xrm-switch)
(x-handle-name-switch, x-handle-args):
* lisp/term/x-win.el (x-handle-parent-id, x-handle-smid):
* lisp/textmodes/reftex-ref.el (reftex-label):
* lisp/textmodes/reftex-toc.el (reftex-toc-rename-label):
* lisp/textmodes/two-column.el (2C-split):
* lisp/tutorial.el (tutorial--describe-nonstandard-key)
(tutorial--find-changed-keys):
* lisp/type-break.el (type-break-noninteractive-query):
* lisp/wdired.el (wdired-do-renames, wdired-do-symlink-changes)
(wdired-do-perm-changes):
* lisp/whitespace.el (whitespace-report-region):
Prefer grave quoting in source-code strings used to generate help
and diagnostics.
* lisp/faces.el (face-documentation):
No need to convert quotes, since the result is a docstring.
* lisp/info.el (Info-virtual-index-find-node)
(Info-virtual-index, info-apropos):
Simplify by generating only curved quotes, since info files are
typically that ways nowadays anyway.
* lisp/international/mule-diag.el (list-input-methods):
Don’t assume text quoting style is curved.
* lisp/org/org-bibtex.el (org-bibtex-fields):
Revert my recent changes, going back to the old quoting style.
2015-09-07 15:41:44 +00:00
|
|
|
|
(signal 'json-string-format (list "doesn't start with `\"'!")))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
;; Skip over the '"'
|
|
|
|
|
(json-advance)
|
|
|
|
|
(let ((characters '())
|
|
|
|
|
(char (json-peek)))
|
2017-02-16 01:43:55 +00:00
|
|
|
|
(while (not (= char ?\"))
|
2017-08-18 00:00:52 +00:00
|
|
|
|
(when (< char 32)
|
|
|
|
|
(signal 'json-string-format (list (prin1-char char))))
|
2017-02-16 01:43:55 +00:00
|
|
|
|
(push (if (= char ?\\)
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(json-read-escaped-char)
|
|
|
|
|
(json-pop))
|
|
|
|
|
characters)
|
|
|
|
|
(setq char (json-peek)))
|
|
|
|
|
;; Skip over the '"'
|
|
|
|
|
(json-advance)
|
|
|
|
|
(if characters
|
2017-08-14 05:54:11 +00:00
|
|
|
|
(concat (nreverse characters))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
"")))
|
|
|
|
|
|
|
|
|
|
;; String encoding
|
|
|
|
|
|
|
|
|
|
(defun json-encode-string (string)
|
|
|
|
|
"Return a JSON representation of STRING."
|
2015-03-22 22:50:58 +00:00
|
|
|
|
;; Reimplement the meat of `replace-regexp-in-string', for
|
|
|
|
|
;; performance (bug#20154).
|
|
|
|
|
(let ((l (length string))
|
|
|
|
|
(start 0)
|
|
|
|
|
res mb)
|
2015-03-25 19:54:29 +00:00
|
|
|
|
;; Only escape quotation mark, backslash and the control
|
|
|
|
|
;; characters U+0000 to U+001F (RFC 4627, ECMA-404).
|
|
|
|
|
(while (setq mb (string-match "[\"\\[:cntrl:]]" string start))
|
2015-03-22 22:50:58 +00:00
|
|
|
|
(let* ((c (aref string mb))
|
|
|
|
|
(special (rassq c json-special-chars)))
|
|
|
|
|
(push (substring string start mb) res)
|
|
|
|
|
(push (if special
|
|
|
|
|
;; Special JSON character (\n, \r, etc.).
|
|
|
|
|
(string ?\\ (car special))
|
|
|
|
|
;; Fallback: UCS code point in \uNNNN form.
|
|
|
|
|
(format "\\u%04x" c))
|
|
|
|
|
res)
|
|
|
|
|
(setq start (1+ mb))))
|
|
|
|
|
(push (substring string start l) res)
|
|
|
|
|
(push "\"" res)
|
|
|
|
|
(apply #'concat "\"" (nreverse res))))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
2012-08-22 01:29:22 +00:00
|
|
|
|
(defun json-encode-key (object)
|
|
|
|
|
"Return a JSON representation of OBJECT.
|
|
|
|
|
If the resulting JSON object isn't a valid JSON object key,
|
|
|
|
|
this signals `json-key-format'."
|
|
|
|
|
(let ((encoded (json-encode object)))
|
|
|
|
|
(unless (stringp (json-read-from-string encoded))
|
|
|
|
|
(signal 'json-key-format (list object)))
|
|
|
|
|
encoded))
|
|
|
|
|
|
2008-02-21 19:41:38 +00:00
|
|
|
|
;;; JSON Objects
|
|
|
|
|
|
|
|
|
|
(defun json-new-object ()
|
|
|
|
|
"Create a new Elisp object corresponding to a JSON object.
|
|
|
|
|
Please see the documentation of `json-object-type'."
|
|
|
|
|
(cond ((eq json-object-type 'hash-table)
|
|
|
|
|
(make-hash-table :test 'equal))
|
|
|
|
|
(t
|
2015-10-28 00:19:51 +00:00
|
|
|
|
())))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
|
|
|
|
(defun json-add-to-object (object key value)
|
|
|
|
|
"Add a new KEY -> VALUE association to OBJECT.
|
|
|
|
|
Returns the updated object, which you should save, e.g.:
|
|
|
|
|
(setq obj (json-add-to-object obj \"foo\" \"bar\"))
|
|
|
|
|
Please see the documentation of `json-object-type' and `json-key-type'."
|
|
|
|
|
(let ((json-key-type
|
2017-09-15 13:49:27 +00:00
|
|
|
|
(or json-key-type
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(cdr (assq json-object-type '((hash-table . string)
|
|
|
|
|
(alist . symbol)
|
2017-09-15 13:49:27 +00:00
|
|
|
|
(plist . keyword)))))))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(setq key
|
|
|
|
|
(cond ((eq json-key-type 'string)
|
|
|
|
|
key)
|
|
|
|
|
((eq json-key-type 'symbol)
|
|
|
|
|
(intern key))
|
|
|
|
|
((eq json-key-type 'keyword)
|
|
|
|
|
(intern (concat ":" key)))))
|
|
|
|
|
(cond ((eq json-object-type 'hash-table)
|
|
|
|
|
(puthash key value object)
|
|
|
|
|
object)
|
|
|
|
|
((eq json-object-type 'alist)
|
|
|
|
|
(cons (cons key value) object))
|
|
|
|
|
((eq json-object-type 'plist)
|
|
|
|
|
(cons key (cons value object))))))
|
|
|
|
|
|
|
|
|
|
;; JSON object parsing
|
|
|
|
|
|
|
|
|
|
(defun json-read-object ()
|
|
|
|
|
"Read the JSON object at point."
|
|
|
|
|
;; Skip over the "{"
|
|
|
|
|
(json-advance)
|
|
|
|
|
(json-skip-whitespace)
|
|
|
|
|
;; read key/value pairs until "}"
|
|
|
|
|
(let ((elements (json-new-object))
|
|
|
|
|
key value)
|
2017-02-19 01:25:50 +00:00
|
|
|
|
(while (not (= (json-peek) ?}))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(json-skip-whitespace)
|
|
|
|
|
(setq key (json-read-string))
|
|
|
|
|
(json-skip-whitespace)
|
2017-02-19 01:25:50 +00:00
|
|
|
|
(if (= (json-peek) ?:)
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(json-advance)
|
|
|
|
|
(signal 'json-object-format (list ":" (json-peek))))
|
2015-11-08 20:44:21 +00:00
|
|
|
|
(json-skip-whitespace)
|
|
|
|
|
(when json-pre-element-read-function
|
|
|
|
|
(funcall json-pre-element-read-function key))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(setq value (json-read))
|
2015-11-08 20:44:21 +00:00
|
|
|
|
(when json-post-element-read-function
|
|
|
|
|
(funcall json-post-element-read-function))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(setq elements (json-add-to-object elements key value))
|
|
|
|
|
(json-skip-whitespace)
|
2017-02-19 01:25:50 +00:00
|
|
|
|
(when (/= (json-peek) ?})
|
|
|
|
|
(if (= (json-peek) ?,)
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(json-advance)
|
|
|
|
|
(signal 'json-object-format (list "," (json-peek))))))
|
|
|
|
|
;; Skip over the "}"
|
|
|
|
|
(json-advance)
|
2015-10-03 21:52:36 +00:00
|
|
|
|
(pcase json-object-type
|
2018-11-05 00:22:15 +00:00
|
|
|
|
('alist (nreverse elements))
|
|
|
|
|
('plist (json--plist-reverse elements))
|
2015-10-03 21:52:36 +00:00
|
|
|
|
(_ elements))))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
|
|
|
|
;; Hash table encoding
|
|
|
|
|
|
|
|
|
|
(defun json-encode-hash-table (hash-table)
|
|
|
|
|
"Return a JSON representation of HASH-TABLE."
|
2015-11-12 17:30:37 +00:00
|
|
|
|
(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))))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
|
|
|
|
;; List encoding (including alists and plists)
|
|
|
|
|
|
|
|
|
|
(defun json-encode-alist (alist)
|
|
|
|
|
"Return a JSON representation of ALIST."
|
2015-11-12 17:30:37 +00:00
|
|
|
|
(when json-encoding-object-sort-predicate
|
|
|
|
|
(setq alist
|
|
|
|
|
(sort alist (lambda (a b)
|
|
|
|
|
(funcall json-encoding-object-sort-predicate
|
|
|
|
|
(car a) (car b))))))
|
2012-12-14 14:57:37 +00:00
|
|
|
|
(format "{%s%s}"
|
|
|
|
|
(json-join
|
|
|
|
|
(json--with-indentation
|
|
|
|
|
(mapcar (lambda (cons)
|
|
|
|
|
(format (if json-encoding-pretty-print
|
|
|
|
|
"%s%s: %s"
|
|
|
|
|
"%s%s:%s")
|
|
|
|
|
json--encoding-current-indentation
|
|
|
|
|
(json-encode-key (car cons))
|
|
|
|
|
(json-encode (cdr cons))))
|
|
|
|
|
alist))
|
|
|
|
|
json-encoding-separator)
|
|
|
|
|
(if (or (not json-encoding-pretty-print)
|
|
|
|
|
json-encoding-lisp-style-closings)
|
|
|
|
|
""
|
|
|
|
|
json--encoding-current-indentation)))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
|
|
|
|
(defun json-encode-plist (plist)
|
|
|
|
|
"Return a JSON representation of PLIST."
|
2015-11-12 17:30:37 +00:00
|
|
|
|
(if json-encoding-object-sort-predicate
|
|
|
|
|
(json-encode-alist (json--plist-to-alist plist))
|
|
|
|
|
(let (result)
|
|
|
|
|
(json--with-indentation
|
|
|
|
|
(while plist
|
|
|
|
|
(push (concat
|
2012-12-14 14:57:37 +00:00
|
|
|
|
json--encoding-current-indentation
|
2015-11-12 17:30:37 +00:00
|
|
|
|
(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
|
|
|
|
|
"")
|
|
|
|
|
"}"))))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
|
|
|
|
(defun json-encode-list (list)
|
|
|
|
|
"Return a JSON representation of LIST.
|
|
|
|
|
Tries to DWIM: simple lists become JSON arrays, while alists and plists
|
|
|
|
|
become JSON objects."
|
2018-05-19 06:36:32 +00:00
|
|
|
|
(cond ((json-alist-p list) (json-encode-alist list))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
((json-plist-p list) (json-encode-plist list))
|
|
|
|
|
((listp list) (json-encode-array list))
|
|
|
|
|
(t
|
|
|
|
|
(signal 'json-error (list list)))))
|
|
|
|
|
|
|
|
|
|
;;; Arrays
|
|
|
|
|
|
|
|
|
|
;; Array parsing
|
|
|
|
|
|
|
|
|
|
(defun json-read-array ()
|
|
|
|
|
"Read the JSON array at point."
|
|
|
|
|
;; Skip over the "["
|
|
|
|
|
(json-advance)
|
|
|
|
|
(json-skip-whitespace)
|
|
|
|
|
;; read values until "]"
|
|
|
|
|
(let (elements)
|
2017-02-19 01:25:50 +00:00
|
|
|
|
(while (not (= (json-peek) ?\]))
|
2015-11-08 20:44:21 +00:00
|
|
|
|
(json-skip-whitespace)
|
|
|
|
|
(when json-pre-element-read-function
|
|
|
|
|
(funcall json-pre-element-read-function (length elements)))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(push (json-read) elements)
|
2015-11-08 20:44:21 +00:00
|
|
|
|
(when json-post-element-read-function
|
|
|
|
|
(funcall json-post-element-read-function))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(json-skip-whitespace)
|
2017-02-19 01:25:50 +00:00
|
|
|
|
(when (/= (json-peek) ?\])
|
|
|
|
|
(if (= (json-peek) ?,)
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(json-advance)
|
|
|
|
|
(signal 'json-error (list 'bleah)))))
|
|
|
|
|
;; Skip over the "]"
|
|
|
|
|
(json-advance)
|
2017-08-14 05:54:11 +00:00
|
|
|
|
(pcase json-array-type
|
2018-11-05 00:22:15 +00:00
|
|
|
|
('vector (nreverse (vconcat elements)))
|
|
|
|
|
('list (nreverse elements)))))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
|
|
|
|
;; Array encoding
|
|
|
|
|
|
|
|
|
|
(defun json-encode-array (array)
|
|
|
|
|
"Return a JSON representation of ARRAY."
|
2012-12-14 14:57:37 +00:00
|
|
|
|
(if (and json-encoding-pretty-print
|
|
|
|
|
(> (length array) 0))
|
|
|
|
|
(concat
|
|
|
|
|
(json--with-indentation
|
|
|
|
|
(concat (format "[%s" json--encoding-current-indentation)
|
|
|
|
|
(json-join (mapcar 'json-encode array)
|
|
|
|
|
(format "%s%s"
|
|
|
|
|
json-encoding-separator
|
|
|
|
|
json--encoding-current-indentation))))
|
|
|
|
|
(format "%s]"
|
|
|
|
|
(if json-encoding-lisp-style-closings
|
|
|
|
|
""
|
|
|
|
|
json--encoding-current-indentation)))
|
|
|
|
|
(concat "["
|
|
|
|
|
(mapconcat 'json-encode array json-encoding-separator)
|
|
|
|
|
"]")))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; JSON reader.
|
|
|
|
|
|
2017-09-04 07:40:30 +00:00
|
|
|
|
(defmacro json-readtable-dispatch (char)
|
|
|
|
|
"Dispatch reader function for CHAR."
|
|
|
|
|
(declare (debug (symbolp)))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(let ((table
|
|
|
|
|
'((?t json-read-keyword "true")
|
|
|
|
|
(?f json-read-keyword "false")
|
|
|
|
|
(?n json-read-keyword "null")
|
|
|
|
|
(?{ json-read-object)
|
|
|
|
|
(?\[ json-read-array)
|
2017-09-04 07:40:30 +00:00
|
|
|
|
(?\" json-read-string)))
|
|
|
|
|
res)
|
|
|
|
|
(dolist (c '(?- ?+ ?. ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9))
|
|
|
|
|
(push (list c 'json-read-number) table))
|
|
|
|
|
(pcase-dolist (`(,c . ,rest) table)
|
|
|
|
|
(push `((eq ,char ,c) (,@rest)) res))
|
2018-02-16 17:11:49 +00:00
|
|
|
|
`(cond ,@res (t (signal 'json-readtable-error (list ,char))))))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
|
|
|
|
(defun json-read ()
|
|
|
|
|
"Parse and return the JSON object following point.
|
2019-07-09 17:41:06 +00:00
|
|
|
|
Advances point just past JSON object.
|
|
|
|
|
|
|
|
|
|
If called with the following JSON after point
|
|
|
|
|
|
|
|
|
|
{\"a\": [1, 2, {\"c\": false}],
|
|
|
|
|
\"b\": \"foo\"}
|
|
|
|
|
|
|
|
|
|
you will get the following structure returned:
|
|
|
|
|
|
|
|
|
|
((a .
|
|
|
|
|
[1 2
|
|
|
|
|
((c . :json-false))])
|
|
|
|
|
(b . \"foo\"))"
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(json-skip-whitespace)
|
|
|
|
|
(let ((char (json-peek)))
|
2017-08-18 00:00:52 +00:00
|
|
|
|
(if (zerop char)
|
|
|
|
|
(signal 'json-end-of-file nil)
|
2017-09-04 07:40:30 +00:00
|
|
|
|
(json-readtable-dispatch char))))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
|
|
|
|
|
;; Syntactic sugar for the reader
|
|
|
|
|
|
|
|
|
|
(defun json-read-from-string (string)
|
|
|
|
|
"Read the JSON object contained in STRING and return it."
|
|
|
|
|
(with-temp-buffer
|
|
|
|
|
(insert string)
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(json-read)))
|
|
|
|
|
|
|
|
|
|
(defun json-read-file (file)
|
|
|
|
|
"Read the first JSON object contained in FILE and return it."
|
|
|
|
|
(with-temp-buffer
|
|
|
|
|
(insert-file-contents file)
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(json-read)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; JSON encoder
|
|
|
|
|
|
|
|
|
|
(defun json-encode (object)
|
2019-07-09 17:41:06 +00:00
|
|
|
|
"Return a JSON representation of OBJECT as a string.
|
|
|
|
|
|
|
|
|
|
OBJECT should have a structure like one returned by `json-read'.
|
2019-07-09 19:32:41 +00:00
|
|
|
|
If an error is detected during encoding, an error based on
|
|
|
|
|
`json-error' is signalled."
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(cond ((memq object (list t json-null json-false))
|
|
|
|
|
(json-encode-keyword object))
|
|
|
|
|
((stringp object) (json-encode-string object))
|
|
|
|
|
((keywordp object) (json-encode-string
|
|
|
|
|
(substring (symbol-name object) 1)))
|
2018-05-19 06:36:32 +00:00
|
|
|
|
((listp object) (json-encode-list object))
|
2008-02-21 19:41:38 +00:00
|
|
|
|
((symbolp object) (json-encode-string
|
|
|
|
|
(symbol-name object)))
|
|
|
|
|
((numberp object) (json-encode-number object))
|
|
|
|
|
((arrayp object) (json-encode-array object))
|
|
|
|
|
((hash-table-p object) (json-encode-hash-table object))
|
|
|
|
|
(t (signal 'json-error (list object)))))
|
|
|
|
|
|
2019-02-08 19:39:00 +00:00
|
|
|
|
;; Pretty printing & minimizing
|
|
|
|
|
|
|
|
|
|
(defun json-pretty-print-buffer (&optional minimize)
|
|
|
|
|
"Pretty-print current buffer.
|
|
|
|
|
With prefix argument MINIMIZE, minimize it instead."
|
|
|
|
|
(interactive "P")
|
|
|
|
|
(json-pretty-print (point-min) (point-max) minimize))
|
|
|
|
|
|
2019-07-31 20:18:57 +00:00
|
|
|
|
(defvar json-pretty-print-max-secs 2.0
|
|
|
|
|
"Maximum time for `json-pretty-print's comparison.
|
|
|
|
|
The function `json-pretty-print' uses `replace-region-contents'
|
|
|
|
|
(which see) passing the value of this variable as argument
|
|
|
|
|
MAX-SECS.")
|
|
|
|
|
|
2019-02-08 19:39:00 +00:00
|
|
|
|
(defun json-pretty-print (begin end &optional minimize)
|
|
|
|
|
"Pretty-print selected region.
|
|
|
|
|
With prefix argument MINIMIZE, minimize it instead."
|
|
|
|
|
(interactive "r\nP")
|
|
|
|
|
(let ((json-encoding-pretty-print (null minimize))
|
|
|
|
|
;; Distinguish an empty objects from 'null'
|
|
|
|
|
(json-null :json-null)
|
|
|
|
|
;; Ensure that ordering is maintained
|
2019-07-09 19:32:41 +00:00
|
|
|
|
(json-object-type 'alist)
|
2019-08-02 16:05:13 +00:00
|
|
|
|
(orig-buf (current-buffer))
|
|
|
|
|
error)
|
|
|
|
|
;; Strategy: Repeatedly `json-read' from the original buffer and
|
|
|
|
|
;; write the pretty-printed snippet to a temporary buffer. As
|
|
|
|
|
;; soon as we get an error from `json-read', simply append the
|
|
|
|
|
;; remainder which we couldn't pretty-print to the temporary
|
|
|
|
|
;; buffer as well (probably the region ends _inside_ a JSON
|
|
|
|
|
;; object).
|
|
|
|
|
;;
|
|
|
|
|
;; Finally, use `replace-region-contents' to swap the original
|
|
|
|
|
;; region with the contents of the temporary buffer so that point,
|
|
|
|
|
;; marks, etc. are kept.
|
|
|
|
|
(with-temp-buffer
|
|
|
|
|
(let ((tmp-buf (current-buffer)))
|
|
|
|
|
(set-buffer orig-buf)
|
|
|
|
|
(replace-region-contents
|
|
|
|
|
begin end
|
|
|
|
|
(lambda ()
|
|
|
|
|
(let ((pos (point))
|
|
|
|
|
(keep-going t))
|
|
|
|
|
(while keep-going
|
|
|
|
|
(condition-case err
|
|
|
|
|
;; We want to format only the JSON snippets in the
|
|
|
|
|
;; region without modifying the whitespace between
|
|
|
|
|
;; them.
|
|
|
|
|
(let ((space (buffer-substring
|
|
|
|
|
(point)
|
|
|
|
|
(+ (point)
|
|
|
|
|
(skip-chars-forward
|
|
|
|
|
" \t\n" (point-max)))))
|
|
|
|
|
(json (json-read)))
|
|
|
|
|
(setq pos (point)) ; End of last good json-read.
|
|
|
|
|
(set-buffer tmp-buf)
|
|
|
|
|
(insert space (json-encode json))
|
|
|
|
|
(set-buffer orig-buf))
|
|
|
|
|
(t
|
|
|
|
|
(setq keep-going nil)
|
|
|
|
|
(set-buffer orig-buf)
|
|
|
|
|
;; Rescue the remainder we couldn't pretty-print.
|
|
|
|
|
(append-to-buffer tmp-buf pos (point-max))
|
|
|
|
|
;; EOF is expected because we json-read until we hit
|
|
|
|
|
;; the end of the narrow region.
|
|
|
|
|
(unless (eq (car err) 'json-end-of-file)
|
|
|
|
|
(setq error err)))))
|
|
|
|
|
tmp-buf))
|
|
|
|
|
json-pretty-print-max-secs
|
|
|
|
|
;; FIXME: What's a good value here? Can we use something better,
|
|
|
|
|
;; e.g., by deriving a value from the size of the region?
|
|
|
|
|
64)))
|
|
|
|
|
;; If we got an error during JSON processing (possibly the region
|
|
|
|
|
;; starts or ends inside a JSON object), signal it to the user.
|
|
|
|
|
;; We did our best.
|
|
|
|
|
(when error
|
|
|
|
|
(signal (car error) (cdr error)))))
|
2019-02-08 19:39:00 +00:00
|
|
|
|
|
|
|
|
|
(defun json-pretty-print-buffer-ordered (&optional minimize)
|
|
|
|
|
"Pretty-print current buffer with object keys ordered.
|
|
|
|
|
With prefix argument MINIMIZE, minimize it instead."
|
2019-02-20 15:57:43 +00:00
|
|
|
|
(interactive "P")
|
2015-11-12 17:30:37 +00:00
|
|
|
|
(let ((json-encoding-object-sort-predicate 'string<))
|
2019-02-08 19:39:00 +00:00
|
|
|
|
(json-pretty-print-buffer minimize)))
|
2015-11-12 17:30:37 +00:00
|
|
|
|
|
2019-02-08 19:39:00 +00:00
|
|
|
|
(defun json-pretty-print-ordered (begin end &optional minimize)
|
|
|
|
|
"Pretty-print the region with object keys ordered.
|
|
|
|
|
With prefix argument MINIMIZE, minimize it instead."
|
|
|
|
|
(interactive "r\nP")
|
2015-11-12 17:30:37 +00:00
|
|
|
|
(let ((json-encoding-object-sort-predicate 'string<))
|
2019-02-08 19:39:00 +00:00
|
|
|
|
(json-pretty-print begin end minimize)))
|
2015-11-12 17:30:37 +00:00
|
|
|
|
|
2008-02-21 19:41:38 +00:00
|
|
|
|
(provide 'json)
|
|
|
|
|
|
|
|
|
|
;;; json.el ends here
|