Subject: bug#21798: closed (Re: bug#21798: 25.0.50; [PATCH] Add support for retrieving paths to JSON elements)

From: [email-masked] (GNU bug Tracking System)
Date: Sun, 08 Nov 2015 21:13:03 +0000

Your bug report

#21798: 25.0.50; [PATCH] Add support for retrieving paths to JSON elements

which was filed against the emacs package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
21798: http://debbugs.gnu.org/cgi/bugreport.cgi?bug=21798
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: bug#21798: 25.0.50; [PATCH] Add support for retrieving paths to JSON elements
From: Simen Heggestøyl <[email-masked]>
To: Dmitry Gutov <[email-masked]>
Cc: [email-masked]
Date: Sun, 08 Nov 2015 22:12:39 +0100
List-Id: <debbugs-submit.debbugs.gnu.org>

On Sun, Nov 8, 2015 at 5:16 PM, Dmitry Gutov <[email-masked]> wrote:
> Thank you, but since `json-read' needs an optional argument in this 
> case (which is not trivial to describe), I'd rather we go back to the 
> previous patch.
> 
> How about renaming json-pre-read-function to 
> json-pre-element-read-function, maybe? And same for the other one.
> 
> If you're fine with that, please install the result. (Or without the 
> rename, it's up to you).

I installed the previous patch with the renaming you suggested.

Thanks for your reviews!

By the way, if you find the time, could you give bug #21616 a quick
look as well? It teaches the JSON encoder how to sort objects by their
keys. I think it would be nice to get it in before the feature freeze,
since it's the last piece of the puzzle to enable the new json-mode to
achieve feature parity with the old one.

-- Simen

Subject: 25.0.50; [PATCH] Add support for retrieving paths to JSON elements
From: Simen Heggestøyl <[email-masked]>
To: [email-masked]
Date: Sat, 31 Oct 2015 09:46:02 +0100
List-Id: <debbugs-submit.debbugs.gnu.org>

Sometimes when working with a JSON structure, one needs to find the
path to a particular element. That can be tiresome to do manually,
especially if the structure is deep.

The proposed patch extends json.el with the ability to retrieve the
path to a particular JSON element.

See the following video for an example of how it can be used by an
interactive command, to show the path to the JSON element at point:

http://folk.uio.no/simenheg/json-mode.webm

The patch follows!

-- Simen

 From f6ddd3b797d6b0d92a1ffa0f5db59543ac7cdc11 Mon Sep 17 00:00:00 2001
 From: =?UTF-8?q?Simen=20Heggest=C3=B8yl?= <[email-masked]>
Date: Sun, 25 Oct 2015 14:44:59 +0100
Subject: [PATCH] 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-path-to-position): New function for retrieving the
path to a JSON object at a given position.
(json--path-to-position, json--path): New variables, used internally by
`json-path-to-position'.
(json-read-object, json-read-array): Respect `json--path-to-position'.

* test/automated/json-tests.el (test-json-path-to-position-with-objects)
(test-json-path-to-position-with-arrays): New tests for
`json-path-to-position'.
---
 lisp/json.el | 67 ++++++++++++++++++++++++++++++++++++++++++--
 test/automated/json-tests.el | 14 +++++++++
 2 files changed, 79 insertions(+), 2 deletions(-)

diff --git a/lisp/json.el b/lisp/json.el
index b23d12a..2c82eee 100644
--- a/lisp/json.el
+++ b/lisp/json.el
@@ -196,6 +196,49 @@ 'json-end-of-file

+;;; Paths
+
+(defvar json--path-to-position nil
+ "When set to a position, `json-read' will return the path to
+the JSON element at the specified position, instead of returning
+the parsed JSON object.")
+
+(defvar json--path '()
+ "Used internally by `json-path-to-position' to keep track of
+the path during recursive calls to `json-read'.")
+
+(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--path-to-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")
@@ -399,11 +442,21 @@ json-read-object
     (while (not (char-equal (json-peek) ?}))
       (json-skip-whitespace)
       (setq key (json-read-string))
+ (when json--path-to-position
+ (push key json--path))
       (json-skip-whitespace)
       (if (char-equal (json-peek) ?:)
           (json-advance)
         (signal 'json-object-format (list ":" (json-peek))))
- (setq value (json-read))
+ (json-skip-whitespace)
+ (let ((start (point)))
+ (setq value (json-read))
+ (when json--path-to-position
+ (when (< start json--path-to-position (+ (point) 1))
+ (throw :json-path (list :path (nreverse json--path)
+ :match-start start
+ :match-end (point))))
+ (pop json--path)))
       (setq elements (json-add-to-object elements key value))
       (json-skip-whitespace)
       (unless (char-equal (json-peek) ?})
@@ -509,7 +562,17 @@ json-read-array
   ;; read values until "]"
   (let (elements)
     (while (not (char-equal (json-peek) ?\]))
- (push (json-read) elements)
+ (json-skip-whitespace)
+ (when json--path-to-position
+ (push (length elements) json--path))
+ (let ((start (point)))
+ (push (json-read) elements)
+ (when json--path-to-position
+ (when (< start json--path-to-position (+ (point) 1))
+ (throw :json-path (list :path (nreverse json--path)
+ :match-start start
+ :match-end (point))))
+ (pop json--path)))
       (json-skip-whitespace)
       (unless (char-equal (json-peek) ?\])
         (if (char-equal (json-peek) ?,)
diff --git a/test/automated/json-tests.el b/test/automated/json-tests.el
index d1b7a2f..e0672dd 100644
--- a/test/automated/json-tests.el
+++ b/test/automated/json-tests.el
@@ -49,5 +49,19 @@
   (should (equal (json-read-from-string 
"\"\\nasd\\u0444\\u044b\\u0432fgh\\t\"")
                  "\nasdфывfgh\t")))

+(ert-deftest test-json-path-to-position-with-objects ()
+ (let* ((json-string "{\"foo\": {\"bar\": {\"baz\": \"value\"}}}")
+ (matched-path (json-path-to-position 32 json-string)))
+ (should (equal (plist-get matched-path :path) '("foo" "bar" "baz")))
+ (should (equal (plist-get matched-path :match-start) 25))
+ (should (equal (plist-get matched-path :match-end) 32))))
+
+(ert-deftest test-json-path-to-position-with-arrays ()
+ (let* ((json-string "{\"foo\": [\"bar\", [\"baz\"]]}")
+ (matched-path (json-path-to-position 20 json-string)))
+ (should (equal (plist-get matched-path :path) '("foo" 1 0)))
+ (should (equal (plist-get matched-path :match-start) 18))
+ (should (equal (plist-get matched-path :match-end) 23))))
+
 (provide 'json-tests)
 ;;; json-tests.el ends here
--
2.6.1