-
Notifications
You must be signed in to change notification settings - Fork 2
/
json-to-org-table.el
200 lines (179 loc) · 6.96 KB
/
json-to-org-table.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
;;; json-to-org-table.el --- Converts json string to linked org table -*- lexical-binding: t; -*-
;;
;; Copyright (C) 2020 Joshua Person
;;
;; Author: Joshua Person <http://github.com/noonker>
;; Maintainer: Joshua Person <[email protected]>
;; Created: December 06, 2020
;; Modified: December 06, 2020
;; Version: 0.0.1
;; Keywords:
;; Homepage: https://github.com/noonker/json-to-org-table
;; Package-Requires: ((emacs 27.1))
;;
;; This file is not part of GNU Emacs.
;;
;;; Commentary:
;;
;; Converts json string to linked org table
;;
;;; Code:
;;; TODO: Better Examples
(defvar j2t-debug nil)
(defvar j2t-cs-map [("\r" "")
("\n" "")]
"Map of characters to replace in json string.")
(defun j2t-cs (str)
"Clean String.
Replace any string org mode wouldn't like according to the j2t-cs-map
STR: String to be replaced
RETURNS: A string with problematic characters returned"
(seq-reduce
(lambda (s m) (replace-regexp-in-string (car m) (cadr m) s))
j2t-cs-map (format "%s" str)))
(defun j2t-lf (key &optional ref i)
"Convert to link Link Format based on available args.
KEY: String or Symbol that becomes the name of the table
REF: If there is a Reference that becomes a subkey of the link
I: Is the Index for links in vectors"
(cond (i (format "[[%s_%s%s]]" key ref (number-to-string i)))
(ref (format "[[%s_%s]]" key ref))
(t (format "[[%s]]" key))))
(defun j2t-hp (key value)
"Hashmap Print prints a hashmap key-value table row.
KEY: Hashmap key column
VALUE: Hashmap value column"
(format "|%s|%s|\n" (j2t-cs key) (j2t-cs value)))
(defmacro j2t-c+ (&rest str)
"HACK: Concatenates all args and update the value of cur with new STR.
There's probably a better way to do this but this keeps things as clean
as possible in the =tablify= function."
`(setq cur (concat cur (concat ,@str ))))
(defun j2t-parse-vector-vector (elt)
"The row parser for a vector of vectors.
ELT: Is a vector to be turned into a table row
RETURNS: A table row representing the values of a vector"
(let ((cur ""))
(j2t-c+ "|")
(mapc (lambda (x) (j2t-c+ (j2t-cs (format "%s" x)) "|" )) elt)
(j2t-c+ "\n")
cur
)
)
(defun j2t-parse-hashmap-vector (elt &optional ref i)
"A row parser for a vector element composed of hashmaps.
ELT: A dotted pair cons representing a json hashmap
REF: Reference if this is a linked table
I: Optional Index for multiple linked tables
RETURNS: The table row representing values of a hashmap and a
list of subtables to create if applicable
EXAMPLE: ((a . (b . 2)) (c . d) (e . f)) -> '(\"|[[a]]|d|f|]\" '(a (b .2) 1))"
(let ((cur "")
(keys (mapcar #'car elt))
(nex '()))
(mapcar (lambda (key)
(let ((value (alist-get key elt)))
(if (consp value)
(progn
(j2t-c+ (j2t-lf key ref i) "|")
(setq nex (append nex '('(key value i)))))
(j2t-c+ (j2t-cs value) "|" )))
) keys)
`(,cur ,nex)
))
(defun j2t-parse-hash-element (elt &optional ref)
"A row parser for elements of a hash map.
ELT: A dotted pair cons representing a json hashmap
REF: Reference if this is a linked table
RETURNS: Return an object who's first element is the generated string
and the second element is the key if a new table is required.
EXAMPLE: (a . b) -> '(\"|a|b|\n\" '())"
(let ((key (car elt))
(val (cdr elt)))
(cond ((not val) `(,(j2t-hp key "") nil))
((vectorp val) `(,(j2t-hp key (j2t-lf key ref)) ,key))
((consp val) `(,(j2t-hp key (j2t-lf key ref)) ,key))
(t `(,(j2t-hp key (format "%s" val)) nil))
)))
(defun j2t-tablify (elt &optional ref)
"Function to be called recusrively to build an table.
ELT: a json object
REF: a reference is this is a linked table"
(let ((cur "")
(nex '()))
(if j2t-debug (message (format "Got here! I was called with:\n elt: %s\n ref: %s\n" elt ref)))
(if ref (j2t-c+ (format "#+name: %s\n" ref))) ;; If there's a reference add a name block to establish the linkage
(cond
;; ----- Element is an empty vector -----
((and (vectorp elt)
(= (length elt) 0))
(j2t-c+ "| |\n"))
;; ----- Element is a hash-map -----
((consp elt)
(progn
(j2t-c+ "|key|value|\n|-\n") ;; Add headers for hashmap table
;; For each element in the hashmap either add the value or add a link to the table of values
(mapc (lambda (x) (let ((parsed (j2t-parse-hash-element x ref)))
(format "x: %s\nparsed: %s" x parsed)
(j2t-c+ (car parsed))
(if (cadr parsed) (setq nex (append (cdr parsed) nex))))) elt)
(j2t-c+ "\n")
;; Recursively call this function to create any subtables
(mapc (lambda (x) (progn (if j2t-debug (message (format "\nThe symbol I'm going to look up is: %s\n it's type is: %s\n and the value is: %s" x (type-of x) (alist-get x elt))))
(if ref
(j2t-c+ (j2t-tablify (alist-get x elt) (format "%s_%s" x ref)))
(j2t-c+ (j2t-tablify (alist-get x elt) (format "%s" x)))))) nex)
))
;; ----- Element is a vector and is a vector of hash-maps -----
((and (vectorp elt)
(consp (aref elt 0)))
(let ((keys (mapc #'car (aref elt 0)))
)
(j2t-c+ (format "|%s|\n" (string-join (mapcar (lambda (x) (format "%s" (car x))) keys) "|")))
(j2t-c+ "|-\n")
(seq-map-indexed
(lambda (elt idx)
(let ((parsed (j2t-parse-hashmap-vector elt ref idx)))
(j2t-c+ "|")
(j2t-c+ (car parsed))
(j2t-c+ "\n")
(if (cadr parsed) (setq nex (append (cdr parsed) nex))))
) elt)
)
;; Recursively call this function to create any subtables
(mapc (lambda (x) (let ((key (nth 0 x))
(value (nth 1 x))
(i (nth 2 x)))
(j2t-c+ (j2t-tablify value (format "%s_%s%s" key ref (format "%s" i)) )))) nex)
)
;; ----- Element is a vector of vectors -----
((and (vectorp elt)
(vectorp (aref elt 0)))
(let ((a nil))
(mapc (lambda (x) (j2t-c+ (j2t-parse-vector-vector x))) elt)
(j2t-c+ "\n")
))
;; ----- Element is a vector of strings -----
((vectorp elt)
(j2t-c+ (format "|%s|\n|-\n" ref))
(mapc (lambda (x) (j2t-c+ "|" (j2t-cs x) "|" "\n")) elt)
)
)
cur
)
)
(defun json-to-org-table-parse-json-string (str)
"Read a json string, parse it, and return a tablified string.
STR: json string"
(j2t-tablify (json-read-from-string str)))
(defun json-to-org-table-parse-json (js)
"Read an Emacs json object, parse it, and return a tablified string.
The json should be in the format:
- lists -> vectors
- hashmaps -> alist cons
- null -> \"\"
- bool -> :json-true / :json-false
JS: json object"
(j2t-tablify js))
(provide 'json-to-org-table)
;;; json-to-org-table.el ends here