6

I am trying to parse a string with the json.el package, though when the string is not json unexpected things happen. Is there a way in elisp to check whether or not a provided string is actually a valid json before trying to parse it with json-read-from-string?

Drew
  • 75,699
  • 9
  • 109
  • 225
lookyhooky
  • 949
  • 7
  • 18
  • 2
    The only way to know if a string is valid JSON is to try to parse it as JSON. – npostavs Jul 26 '16 at 22:15
  • @npostavs So I should try to parse it and then handle the error that's thrown if its not valid json? – lookyhooky Jul 26 '16 at 22:17
  • Be aware too of what kind of "valid" (i.e., well-formed) JSON you are interested in. In practice, most applications that use JSON allow lots of syntax that is not strict JSON syntax. – Drew Jul 27 '16 at 00:19
  • 1
    "try to parse it and then handle the error" - Yup. – npostavs Jul 27 '16 at 15:35

2 Answers2

2

It's possible to use functionality provided by json which ships with Emacs to validate a JSON. It's a matter of trying to parse a string with it and catching any errors it might spit out.

(require 'json)

(defun my-json-string-valid-p (string)
  "Validates a JSON string."
  (condition-case nil
      (progn
        (json-read-from-string string)
        t)
    (error nil)))
1

C-x my-validate-json-or-js-expression validate the current buffer or selected region as json.

The only requirement is you need install js2-mode. (No, you don't need setup js2-mode. My code works out of box! we use js2-mode as API collection instead of major-mode)

Here is the code,

(defun my-validate-json-or-js-expression (&optional not-json-p)
  "Validate buffer or select region as JSON.
If NOT-JSON-P is not nil, validate as Javascript expression instead of JSON."
  (interactive "P")
  (let* ((json-exp (if (region-active-p) (buffer-substring-no-properties (region-beginning) (region-end))
                     (buffer-substring-no-properties (point-min) (point-max))))
         (jsbuf-offet (if not-json-p 0 (length "var a=")))
         errs
         first-err
         (first-err-pos (if (region-active-p) (region-beginning) 0)))
    (unless not-json-p
      (setq json-exp (format "var a=%s;"  json-exp)))
    (with-temp-buffer
      (insert json-exp)
      (unless (featurep 'js2-mode)
        (require 'js2-mode))
      (js2-parse)
      (setq errs (js2-errors))
      (cond
       ((not errs)
        (message "NO error found. Good job!"))
       (t
        ;; yes, first error in buffer is the last element in errs
        (setq first-err (car (last errs)))
        (setq first-err-pos (+ first-err-pos (- (cadr first-err) jsbuf-offet)))
        (message "%d error(s), first at buffer position %d: %s"
                 (length errs)
                 first-err-pos
                 (js2-get-msg (caar first-err))))))
    (if first-err (goto-char first-err-pos))))

The bonus tip is you can also C-u M-x my-validate-json-or-js-expression to validate as javascript expression. This is only useful when you don't use js2-mode for javascript file.

chen bin
  • 4,781
  • 18
  • 36