Various json.el improvements

* etc/NEWS: Announce that json-read-number is now stricter.

* json.el: Bump package version.
(json-encoding-lisp-style-closings, json-pre-element-read-function)
(json-post-element-read-function, json-advance, json-peek)
(json--path): Clarify and improve style of doc strings.
(json-join): Define as an obsolete alias of string-join.
(json-alist-p, json-plist-p): Refactor for speed and declare as
pure, side-effect-free, and error-free.
(json--plist-reverse): Rename function...
(json--plist-nreverse): ...to this, making it destructive for speed.
All callers changed.
(json--plist-to-alist): Remove, replacing single use with map-pairs.
(json--with-indentation): Accept multiple forms as arguments, fix
their indentation, and allow them to be instrumented for debugging.
Add docstring.
(json-pop, json-read-keyword, json-add-to-object)
(json-encode-array): Simplify for speed.
(json-skip-whitespace): Put newline before carriage return for
likely frequency of occurrence, and so that the characters appear in
increasing order.
(json--check-position): Use 1+.
(json-path-to-position): Open code apply-partially.
(json-keywords): Turn into a defconst and mark as obsolete now that
it is no longer used.
(json--post-value, json--number, json--escape): New rx definitions.
(json-encode-keyword): Declare as side-effect-free.
(json-read-number): Reject leading zeros and plus signs, and make
integer part mandatory in accordance with JSON standards and for
consistency with native JSON parsing functions.  Eagerly signal
json-number-format when garbage follows a valid number, e.g., when
reading "1.1.1", instead of leaving that up to the caller.  Remove
optional internal argument from advertised calling convention now
that the function is no longer recursive.
(json-encode-number): Define as an alias of number-to-string.
(json-special-chars): Turn into a defconst.
(json-read-escaped-char, json-new-object, json-read-file)
(json-pretty-print): Simplify.
(json-read-string): For consistency with other json.el error
reporting, remove check for leading '"', and use the integer value
rather than the printed representation of characters in error data.
At EOB signal json-end-of-file instead of json-string-format.
(json--long-string-threshold, json--string-buffer): New variables.
(json-encode-string): Reimplement in terms of buffer manipulation
for speed (bug#20154).
(json-read-object): Escape ?\} properly.
(json--encode-alist): New function extracted from json-encode-alist.
(json-encode-hash-table, json-encode-alist, json-encode-plist): Use
it to avoid destructively modifying the argument when
json-encoding-object-sort-predicate is non-nil without incurring
unnecessary copying (bug#40693).  Encode empty object as "{}" even
when pretty-printing.  Simplify for speed.
(json-read-array): Avoid recomputing list length on each iteration
when json-pre-element-read-function is non-nil.  Make first element
of json-array-format error data a string for consistency with
json-object-format and to make the displayed error message clearer.
(json-readtable-dispatch): Accept any kind of argument, not just
symbols.  Generate the table in a simpler manner so the dispatch
order is clearer.  Remove dispatch on ?+ and ?. now that
json-read-number is stricter and for consistency with native JSON
parsing functions.  Signal json-end-of-file if argument is nil.
(json-read): Simplify accordingly.
(json-encode): Avoid allocating a list on each invocation.

* lisp/jsonrpc.el (jsonrpc--json-read, jsonrpc--json-encode): Check
whether native JSON functions are fboundp only once, at load time.

* lisp/progmodes/python.el (python--parse-json-array): New function.
(python-shell-prompt-detect): Use it to parse JSON directly as a
list rather than converting from a vector.

* test/lisp/json-tests.el (json-tests--with-temp-buffer): Allow
instrumenting for debugging.
(test-json-join, test-json-plist-to-alist): Remove tests.

(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-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-encode-hash-table, test-json-encode-plist)
(test-json-encode-list, test-json-read-array)
(test-json-encode-array, test-json-read)
(test-json-read-from-string, test-json-encode): Extend tests.

(test-json-plist-reverse): Rename test...
(test-json-plist-nreverse): ...to this and avoid modifying literal
lists.
(test-json-read-number): Rename test...
(test-json-read-integer): ...to this, focusing on integers.
(test-json-add-to-object): Rename test...
(test-json-add-to-alist): ...to this, focusing on alists.
(json-encode-simple-alist): Rename test...
(test-json-encode-alist): ...to this, extending it.
(test-json-encode-alist-with-sort-predicate): Rename test...
(test-json-encode-alist-sort): ...to this, extending it.
(test-json-encode-plist-with-sort-predicate): Rename test...
(test-json-encode-plist-sort): ...to this, extending it.

(test-json-read-keyword-invalid, test-json-read-fraction)
(test-json-read-exponent, test-json-read-fraction-exponent)
(test-json-read-number-invalid)
(test-json-read-escaped-char-invalid, test-json-add-to-plist)
(test-json-add-to-hash-table, test-json-read-object-empty)
(test-json-read-object-invalid, test-json-read-object-function)
(test-json-encode-hash-table-pretty)
(test-json-encode-hash-table-lisp-style)
(test-json-encode-hash-table-sort, test-json-encode-alist-pretty)
(test-json-encode-alist-lisp-style, test-json-encode-plist-pretty)
(test-json-encode-plist-lisp-style, test-json-read-array-function)
(test-json-encode-array-pretty, test-json-encode-array-lisp-style)
(test-json-read-invalid): New tests.

(test-json-path-to-position-no-match): Use should-not.
(test-json-read-object): Move error check to new test
test-json-read-object-invalid.
(test-json-pretty-print-object): Adapt test now that empty objects
are pretty-printed as "{}".
parent d714aa75
Pipeline #5639 passed with stage
in 60 minutes and 50 seconds
......@@ -360,6 +360,15 @@ either an internal or external browser.
*** New user option 'project-vc-merge-submodules'.
** json.el
---
*** JSON number parsing is now stricter.
Numbers with a leading plus sign, leading zeros, or a missing integer
component are now rejected by 'json-read' and friends. This makes
them more compliant with the JSON specification and consistent with
the native JSON parsing functions.
* New Modes and Packages in Emacs 28.1
......
This diff is collapsed.
......@@ -37,7 +37,6 @@
;;; Code:
(require 'cl-lib)
(require 'json)
(require 'eieio)
(eval-when-compile (require 'subr-x))
(require 'warnings)
......@@ -470,26 +469,35 @@ With optional CLEANUP, kill any associated buffers."
;;;
(define-error 'jsonrpc-error "jsonrpc-error")
(defun jsonrpc--json-read ()
"Read JSON object in buffer, move point to end of buffer."
;; TODO: I guess we can make these macros if/when jsonrpc.el
;; goes into Emacs core.
(cond ((fboundp 'json-parse-buffer) (json-parse-buffer
:object-type 'plist
:null-object nil
:false-object :json-false))
(t (let ((json-object-type 'plist))
(json-read)))))
(defun jsonrpc--json-encode (object)
"Encode OBJECT into a JSON string."
(cond ((fboundp 'json-serialize) (json-serialize
object
:false-object :json-false
:null-object nil))
(t (let ((json-false :json-false)
(json-null nil))
(json-encode object)))))
(defalias 'jsonrpc--json-read
(if (fboundp 'json-parse-buffer)
(lambda ()
(json-parse-buffer :object-type 'plist
:null-object nil
:false-object :json-false))
(require 'json)
(defvar json-object-type)
(declare-function json-read "json" ())
(lambda ()
(let ((json-object-type 'plist))
(json-read))))
"Read JSON object in buffer, move point to end of buffer.")
(defalias 'jsonrpc--json-encode
(if (fboundp 'json-serialize)
(lambda (object)
(json-serialize object
:false-object :json-false
:null-object nil))
(require 'json)
(defvar json-false)
(defvar json-null)
(declare-function json-encode "json" (object))
(lambda (object)
(let ((json-false :json-false)
(json-null nil))
(json-encode object))))
"Encode OBJECT into a JSON string.")
(cl-defun jsonrpc--reply
(connection id &key (result nil result-supplied-p) (error nil error-supplied-p))
......
......@@ -261,7 +261,6 @@
(require 'ansi-color)
(require 'cl-lib)
(require 'comint)
(require 'json)
(require 'tramp-sh)
;; Avoid compiler warnings
......@@ -2276,6 +2275,18 @@ Do not set this variable directly, instead use
Do not set this variable directly, instead use
`python-shell-prompt-set-calculated-regexps'.")
(defalias 'python--parse-json-array
(if (fboundp 'json-parse-string)
(lambda (string)
(json-parse-string string :array-type 'list))
(require 'json)
(defvar json-array-type)
(declare-function json-read-from-string "json" (string))
(lambda (string)
(let ((json-array-type 'list))
(json-read-from-string string))))
"Parse the JSON array in STRING into a Lisp list.")
(defun python-shell-prompt-detect ()
"Detect prompts for the current `python-shell-interpreter'.
When prompts can be retrieved successfully from the
......@@ -2324,11 +2335,11 @@ detection and just returns nil."
(catch 'prompts
(dolist (line (split-string output "\n" t))
(let ((res
;; Check if current line is a valid JSON array
(and (string= (substring line 0 2) "[\"")
;; Check if current line is a valid JSON array.
(and (string-prefix-p "[\"" line)
(ignore-errors
;; Return prompts as a list, not vector
(append (json-read-from-string line) nil)))))
;; Return prompts as a list.
(python--parse-json-array line)))))
;; The list must contain 3 strings, where the first
;; is the input prompt, the second is the block
;; prompt and the last one is the output prompt. The
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment