summaryrefslogtreecommitdiff
path: root/lisp/json.el
diff options
context:
space:
mode:
authorSimen Heggestøyl <simenheg@gmail.com>2015-11-08 21:44:21 +0100
committerSimen Heggestøyl <simenheg@gmail.com>2015-11-08 21:44:21 +0100
commit29d740aac9773334d8189a1cc989634a48a70061 (patch)
treeba5f1cbec0b0b413570cdfed8fd9fd5e19bda6e6 /lisp/json.el
parent5193ad1bcbb4ec80f25cfbfae8e168360fc00534 (diff)
downloademacs-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/json.el')
-rw-r--r--lisp/json.el76
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) ?,)