0

I'm making diagrams with plantuml and org mode, when preparing the JSONs, in order to plot well the diagram I need this representation of JSON:

(with-temp-buffer 
  (insert (json-encode '((calimero . pato) (gato . 3))))
  (json-pretty-print-buffer) 
  (buffer-string))

Whit this result:

"{
  \"calimero\": \"pato\",
  \"gato\": 3
}"

but I really need, this in one line with tabs and returns:

"{\n  \"calimero\": \"pato\",\n  \"gato\": 3\n}"

I tried with this but I get many extras things that I do not need:

(with-temp-buffer (insert (json-encode '((calimero . pato) (gato . 3))))
                         (let ((print-escape-newlines t))
                         (json-pretty-print-buffer)
                         (prin1-to-string (buffer-string))))

"\"{\\n  \\\"calimero\\\": \\\"pato\\\",\\n  \\\"gato\\\": 3\\n}\""

I want to fullfill this org mode plantuml block, with a json, with returns and spaces in order to view it as emacs pretty json but in the drawing inthe palntuml doc says that it needs to be in one line with "\n" and spaces:

Begining from this:

'((calimero . pato) (gato . 3))

getting this:

{\n  "calimero": "pato",\n  "gato": 3\n}

I will use in a plantuml diagram like this:

#+BEGIN_SRC plantuml :file hola.png
Alice -> Bob: {\n  "calimero": "pato",\n  "gato": 3\n}
Bob --> Alice: 
#+END_SRC

#+RESULTS:
[[file:hola.png]]

Final result

anquegi
  • 739
  • 5
  • 21
  • 1
    No sure if I understand your question. Literal newline is the same data as `\n`, like space, `(string= "\s" " ") => t`. – xuchunyang Nov 24 '18 at 08:05
  • The important thing is that I want to achieve this string "{\n \"calimero\": \"pato\",\n \"gato\": 3\n}" begining with this alist ((calimero . pato) (gato . 3)) – anquegi Nov 24 '18 at 08:52
  • 1
    More context is needed. How do you embed the elisp fragments into your code. Background: The string you get by the code block with `prin1-to-string` is exactly the elisp representation of what you want. So it depends on how you further process that string whether that is already your answer. If you put that elisp fragment into an org source block with header args `:results drawer return` or `:results raw return` the output of that block is identical to the desired string. – Tobias Nov 24 '18 at 14:18
  • I added the final result, hope this helps@Tobias – anquegi Nov 25 '18 at 14:45
  • Putting the `prin1` variant into a source block and using its result for an `:var` argument of the `plantuml` source block almost works. The bad thing is that `org-babel-variable-assignments:plantuml` strips all double-quotes from the argument. – Tobias Nov 26 '18 at 17:34

1 Answers1

2

The following lisp code modifies org-babel-variable-assignments:plantuml. If you prefix a variable of a plantuml source block with json: newlines escaped with the character ?\\ are not stripped from the value of that variable. Instead the substring "\\\"" is reduced to "\"".

If you want to use the Elisp snippet put it into your init file and restart Emacs.

(defun my-remove-double-quotes (str)
  "Remove non-escaped double-quotes from STR."
  (let ((pos 0))
    (while (string-match "\\([\\]*\\)\"" str pos)
      (setq pos (1- (match-end 0)))
      (if (evenp (length (match-string 1 str)))
      ;; even number of escape characters => remove double-quote
      (setq str (concat (substring str nil pos)
                (substring str (match-end 0))))
    ;; kill one escape character
    (setq str (concat (substring str nil (1- pos))
              (substring str pos))))))
  str)

(defconst my-length-json: (length "json:"))

(defun ad-org-babel-variable-assignments:plantuml (params)
  "Return a list of PlantUML statements assigning the block's variables.
PARAMS is a property list of source block parameters, which may
contain multiple entries for the key `:var'.  `:var' entries in PARAMS
are expected to be scalar variables."
  (mapcar
   (lambda (pair)
     (let* ((var-name (symbol-name (car pair)))
        (is-json (string-equal (substring var-name nil my-length-json:) "json:"))
        (val (cdr pair)))
       (when is-json
     (setq var-name (substring var-name my-length-json:)))
       (format "!define %s %s"
           var-name
           (if is-json
           (my-remove-double-quotes val)
         (cl-remove ?\" val)))))
   (org-babel--get-vars params)))

(eval-after-load "ob-plantuml"
  (advice-add 'org-babel-variable-assignments:plantuml :override #'ad-org-babel-variable-assignments:plantuml))

Therewith the following org-file contents should do what you want:

#+NAME: createJSON
#+BEGIN_SRC emacs-lisp
(with-temp-buffer (insert (json-encode '((calimero . pato) (gato . 3))))
                  (let ((print-escape-newlines t))
                    (json-pretty-print-buffer)
                    (prin1-to-string (buffer-string))))
#+END_SRC

#+RESULTS: createJSON
: "{\n  \"calimero\": \"pato\",\n  \"gato\": 3\n}"

#+BEGIN_SRC plantuml :var json:myJSON=createJSON :file hola.png
Alice -> Bob: myJSON
Bob --> Alice: 
#+END_SRC

Note that I don't have JAVA installed on my machine. So the final test of the solution is your task.

Nevertheless, I checked the generated full-body in function org-babel-execute:plantuml:

@startuml
!define myJSON {\n  "calimero": "pato",\n  "gato": 3\n}
Alice -> Bob: myJSON
Bob --> Alice: 
@enduml

The author of this answer tested the plantuml code on the plantuml home page. There it works.

The OP of the question tested the code on a computer with the java requirement and emacs, and it worked well:

working on mac os x with java

Tobias
  • 32,569
  • 1
  • 34
  • 75
  • Thanks for your time, and for this learning hack on org-mode, this is a really complete answer that has more useful things that only answering the question – anquegi Nov 27 '18 at 16:47