diff options
author | Simen Heggestøyl <simenheg@gmail.com> | 2015-11-08 21:44:21 +0100 |
---|---|---|
committer | Simen Heggestøyl <simenheg@gmail.com> | 2015-11-08 21:44:21 +0100 |
commit | 29d740aac9773334d8189a1cc989634a48a70061 (patch) | |
tree | ba5f1cbec0b0b413570cdfed8fd9fd5e19bda6e6 /lisp | |
parent | 5193ad1bcbb4ec80f25cfbfae8e168360fc00534 (diff) | |
download | emacs-29d740aac9773334d8189a1cc989634a48a70061.tar.gz |
Add support for retrieving paths to JSON elements
Add support for retrieving the path to a JSON element. This can for
instance be useful to retrieve paths in deeply nested JSON
structures.
* lisp/json.el (json-pre-element-read-function)
(json-post-element-read-function): New variables to hold pre- and post
read callback functions for `json-read-array' and `json-read-object'.
(json--path): New variable used internally by `json-path-to-position'.
(json--record-path, json--check-position): New functions used
internally by `json-path-to-position'.
(json-path-to-position): New function for retrieving the path to a
JSON element at a given position.
(json-read-object, json-read-array): Call
`json-pre-element-read-function' and `json-post-element-read-function'
when set.
* test/automated/json-tests.el (test-json-path-to-position-with-objects)
(test-json-path-to-position-with-arrays)
(test-json-path-to-position-no-match): New tests for
`json-path-to-position'.
Diffstat (limited to 'lisp')
-rw-r--r-- | lisp/json.el | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/lisp/json.el b/lisp/json.el index b23d12ad0ed..97cf9934c34 100644 --- a/lisp/json.el +++ b/lisp/json.el @@ -111,6 +111,17 @@ Used only when `json-encoding-pretty-print' is non-nil.") "If non-nil, ] and } closings will be formatted lisp-style, without indentation.") +(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.") + ;;; Utilities @@ -196,6 +207,61 @@ Unlike `reverse', this keeps the property-value pairs intact." +;;; 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)))) + ;;; Keywords (defvar json-keywords '("true" "false" "null") @@ -403,7 +469,12 @@ Please see the documentation of `json-object-type' and `json-key-type'." (if (char-equal (json-peek) ?:) (json-advance) (signal 'json-object-format (list ":" (json-peek)))) + (json-skip-whitespace) + (when json-pre-element-read-function + (funcall json-pre-element-read-function key)) (setq value (json-read)) + (when json-post-element-read-function + (funcall json-post-element-read-function)) (setq elements (json-add-to-object elements key value)) (json-skip-whitespace) (unless (char-equal (json-peek) ?}) @@ -509,7 +580,12 @@ become JSON objects." ;; read values until "]" (let (elements) (while (not (char-equal (json-peek) ?\])) + (json-skip-whitespace) + (when json-pre-element-read-function + (funcall json-pre-element-read-function (length elements))) (push (json-read) elements) + (when json-post-element-read-function + (funcall json-post-element-read-function)) (json-skip-whitespace) (unless (char-equal (json-peek) ?\]) (if (char-equal (json-peek) ?,) |