summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/jsonparse/jsonparse-doc.tex
blob: 2b0eff21c805cc5a980fb04cbfbb04f571419443 (plain)
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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
% File: jsonparse-doc.tex 
% Copyright 2024 Jasper Habicht (mail@jasperhabicht.de).
% 
% This work may be distributed and/or modified under the
% conditions of the LaTeX Project Public License version 1.3c,
% available at http://www.latex-project.org/lppl/.
% 
% This file is part of the `jsonparse'  package (The Work in LPPL)
% and all files in that bundle must be distributed together.
% 
% This work has the LPPL maintenance status `author-maintained'.
% 
\documentclass[a4paper]{article}
\def\jsonparsefileversion{0.5.6}
\def\jsonparsefiledate{11 April 2024}

\usepackage[T1]{fontenc}
\usepackage{Alegreya}
\usepackage{AlegreyaSans}
\usepackage{FiraMono}

\usepackage{geometry, longtable, booktabs, tcolorbox, hyperref}
\geometry{margin=25mm, left=45mm}

\usepackage{fancyhdr}
\pagestyle{fancy}
\fancyhf{}
\renewcommand{\headrulewidth}{0pt}
\fancyfoot[R]{\thepage}

\linespread{1.1}\selectfont
\addtolength{\skip\footins}{1em plus 5pt}
\makeatletter
\renewcommand*{\thefootnote}{\fnsymbol{footnote}}
\renewcommand{\@makefntext}[1]{%
    \noindent\hbox to 0.75em{\normalfont\@thefnmark\hss}~#1%
}
\makeatother

\tcbuselibrary{listings}
\lstdefinestyle{jsonparsedocmacro}{
    basicstyle=\small\ttfamily,
    literate=*{<}{{{\color{black!50}\guilsinglleft}}}1 
        {>}{{{\color{black!50}\guilsinglright}}}1,
    keywords={},
    moredelim=[is][\bfseries]{|}{|},
    moredelim=[is][\color{black!50}]{!}{!},
}
\lstdefinestyle{jsonparsedoccodeexample}{
    basicstyle=\small\ttfamily,
    keywords={},
}

\hypersetup{colorlinks}

\ExplSyntaxOn
\int_new:N \l_jsonparse_doc_change_int
\NewDocumentCommand{\changes}{ m m m }{
    \int_incr:N \l_jsonparse_doc_change_int
    \prop_new:c { l_jsonparse_doc_change_ 
        \int_to_roman:n { \l_jsonparse_doc_change_int } _prop }
    \prop_put:cnn { l_jsonparse_doc_change_ 
        \int_to_roman:n { \l_jsonparse_doc_change_int } _prop } { version } {#1}
    \prop_put:cnn { l_jsonparse_doc_change_ 
        \int_to_roman:n { \l_jsonparse_doc_change_int } _prop } { date } {#2}
    \prop_put:cnn { l_jsonparse_doc_change_ 
        \int_to_roman:n { \l_jsonparse_doc_change_int } _prop } { changes } {#3}
}
\NewDocumentCommand{\printchanges}{ }{
    \section{Changes}
    \begin{description}
    \int_step_inline:nn { \l_jsonparse_doc_change_int } {
        \item[ 
            \prop_item:cn { l_jsonparse_doc_change_ 
            \int_to_roman:n { ##1 } _prop } { version } ] 
            ( \prop_item:cn { l_jsonparse_doc_change_ 
            \int_to_roman:n { ##1 } _prop } { date } ) \\
            \prop_item:cn { l_jsonparse_doc_change_ 
            \int_to_roman:n { ##1 } _prop } { changes }
    }
    \end{description}
}

\tl_new:N \l_jsonparse_doc_doctitle_tl
\NewExpandableDocumentCommand{\makedoctitle}{ o m o m m o m }{
    \hypersetup{
        pdfauthor={#5},
        pdftitle={\IfValueTF{#1}{#1}{#2}},
        pdfsubject={\IfValueTF{#3}{#3}{#4}}
    }
    \tl_set:Nn \l_jsonparse_doc_doctitle_tl {
        \group_begin:
            \tcbset{
                title ~ style ~ hook/.style={
                    boxrule=2pt,
                    fontupper=\huge\ttfamily
                }
            }
            \setlength{\parindent}{0pt}\sffamily
            \Huge{\bfseries #2}\par\bigskip
            \Large #4\par\bigskip
            \large #5
            \IfValueT{#6}{
                \,\footnote{#6}\par\bigskip
            }
            #7\par\bigskip
            \rule{\textwidth}{.08em}
        \group_end:
    }
}

\NewExpandableDocumentCommand{\printdoctitle}{ }{
    \tl_use:N \l_jsonparse_doc_doctitle_tl
}
\ExplSyntaxOff

\tcbset{
    size=small,
    arc=2.5pt,
    outer arc=2.5pt,
    colframe=black!10,
    colback=black!10,
    title style hook/.style={}
}

\NewTCBListing{macrodef}{ }{ 
    listing only,
    listing style={jsonparsedocmacro},
    grow to left by=2cm,
    boxrule=0pt,
    after={\par\smallskip\noindent}
}

\NewTCBListing{codeexample}{ }{
    listing only,
    listing style={jsonparsedoccodeexample},
    after={\par\smallskip\noindent}
}

\NewTCBListing{codeexamplecolumns}{ }{
    text side listing,
    lefthand width=4.5cm,
    listing style={jsonparsedoccodeexample},
    after={\par\smallskip\noindent}
}

\NewTotalTCBox{\macro}{ O{} v }{
    verbatim,
    size=tight,
    arc=1pt,
    outer arc=1pt,
    top=0.5pt, 
    bottom=0.5pt,
    left=2pt, 
    right=2pt,
    boxrule=0pt,
    colframe=black!10,
    colback=black!10,
    fontupper=\small\ttfamily,
    #1
}{\vphantom{/g}\lstinline^#2^}

\NewTotalTCBox{\titlemacro}{ s O{} m }{
    verbatim,
    size=tight,
    arc=1pt,
    outer arc=1pt,
    top=0.5pt, 
    bottom=0.5pt,
    left=2pt, 
    right=2pt,
    boxrule=0pt,
    colframe=black!10,
    colback=black!10,
    fontupper=\small\ttfamily,
    title style hook,
    #2
}{\vphantom{/g}\IfBooleanT{#1}{\textbackslash}#3}

\usepackage{jsonparse}

\makedoctitle
    [The jsonparse package]
    {The \titlemacro{jsonparse} package}
    {An easy way to parse, store and access JSON data from files or strings in LaTeX documents}
    {Jasper Habicht}[E-mail: \href{mailto:mail@jasperhabicht.de}{mail@jasperhabicht.de}]
    {Version \jsonparsefileversion, released on \jsonparsefiledate}

\changes{v0.3.0}{2024/04/08}{First public beta release.}
\changes{v0.5.0}{2024/04/09}{Changed from string token variables to token lists to support Unicode.}
\changes{v0.5.5}{2024/04/09}{Bug fixes, introduction and enhancement of user functions.}
\changes{v0.5.6}{2024/04/11}{Bug fixes, escaping of special chars added.}

\begin{document}

\printdoctitle

\bigskip

\section{Introduction}

The \macro{jsonparse} package provides an easy way to read in JSON data from files or strings in LaTeX documents, parse the data and store it in a user-defined token variable. The package allows accessing the stored data via a JavaScript-flavored syntax.

This package is still in a beta stage and not thoroughly tested. Bugs or improvements can be issued via GitHub at \url{https://github.com/jasperhabicht/jsonparse/issues}.

\section{Loading the package}

To install the package, copy the package file \macro{jsonparse.sty} into the working directory or into the \macro{texmf} directory. After the package has been installted, the \macro{jsonparse} package is loaded by calling \macro{\usepackage{jsonparse}} in the preamble of the document. 

The package does not load any dependencies.

\begin{macrodef}debug\end{macrodef}
The package can be loaded with the option \macro{debug}. It will then output to the log file every instance of a string, a boolean (true or false) value, a null value, a number as well as the start and end of every object and the start and end of every array that is found while parsing the JSON string or JSON file.

\section{Escaping and special treatment of the input}

The package allows for empty lines in JSON strings. During parsing, every instance of the TeX macro \macro{\par} is replaced by a space.

JSON strings cannot contain the two characters \macro{"} and \macro{\}. These two characters need to be escaped with a preceding backslash (\macro{\}). This package therefore redefines locally the TeX macros \macro{\"}, \macro{\/} and \macro{\\}. During parsing, \macro{\"} expands to \macro{\exp_not:N \"} (i.\,e. it is prevented to expand during parsing) and only when typeset, \macro{\"} is expanded to \macro{"}, which ensures that strings are parsed properly. Similarly, \macro{\/} expands to \macro{\exp_not:N \/} and finally to \macro{/} while \macro{\\} expands to \macro{\exp_not:N \\} and finally to \macro{\c_backslash_str} (i.\,e. a backslash with category code 12). 

Due to the above procedure, the TeX macros \macro{\"} and \macro{\\} must be escaped twice in the JSON source, so that they become \macro{\\\"} and \macro{\\\\} respectively. 

Other escape sequences defined by JSON, such as \macro{\b}, \macro{\f}, \macro{\n}, \macro{\r}, \macro{\t} or \macro{\u} (the latter followed by a hex value) are not escaped and it is up to the user to process these sequences before feeding them into the commands provided by this package.

Characters that are special to TeX are not handled in a special way and will be treated by TeX the same way as if the user had input them in the document. 

Setting the internal boolean \macro{\l_jsonparse_escape_input_bool} to false disables the treatment of the input as described in this section.

\section{Main user commands}

\begin{macrodef}
|\JSONParse|{<token variable>}{<JSON string>}
\end{macrodef}
The command \macro{\JSONParse} is used to parse a JSON string and store the parsed result in a token variable (a property list). The first argument takes the name of the token variable that is created by the command. The second argument takes the JSON string to be parsed. 

For example, using \macro{\JSONParse{\myJSONdata}{ { "key" : "value" } }}, the relevant JSON string will be parsed and the result stored in the token variable \macro{\myJSONdata} as property list. In this case, the property list only consists of one entry with the key \macro{key} and the value \macro{value}. The command \macro{\JSONParseGetValue{\myJSONdata}{key}}, for example, can then be used to extract the relevant value from this property list (see the description below). 

\begin{macrodef}
|\JSONParseFromFile|{<token variable>}{<JSON file>}
\end{macrodef}
The command \macro{\JSONParseFromFile} is used to parse a JSON file and store the parsed result in a token variable (a property list). It works the same way as \macro{\JSONParse}, but instead of a JSON string, it takes as second argument the path to the JSON file relative to the working directory.

\begin{macrodef}
|\JSONParseGetValue|{<token variable>}{<key>}
|\JSONParseGetValue*|{<token variable>}{<key>}
\end{macrodef}
The command \macro{\JSONParseGetValue} is used to select values from the token variable (property list) that has been created using the commands \macro{\JSONParse} or \macro{\JSONParseFromFile}. The first argument takes the token variable that holds the parsed JSON data. The second argument takes the key to select the relevant entry from the parsed JSON data using JavaScript syntax.

If the JSON string \macro{{ "key" : "value" }} is parsed into the token variable \macro{\myJSONdata}, using \macro{\JSONParseGetValue{\myJSONdata}{key}} would extract the value associated with the key \macro{key}, which in this case is \macro{value}, and typeset it to the document.

Nested objects and arrays are assigned keys that adhere to JavaScript syntax. For example, if the JSON string \macro{{ "outer_key" : { "inner_key" : "value" } }} is parsed into the token variable \macro{\myJSONdata}, to select the value associated with the key \macro{inner_key}, the command \macro{\JSONParseGetValue{\myJSONdata}{outer_key.inner_key}} can be used. The command \macro{\JSONParseGetValue{\myJSONdata}{key[0]}} selects the first value of the array associated with the key \macro{key} in the parsed JSON string \macro{{ "key" : [ "one" , "two" ] }}.

When a key is associated with an object or array, the whole object or array is output as JSON string. The special key \macro{.} (or the string defined using \macro{\JSONParseSetChildSeparator}) returns the whole JSON object as string. The output of whole objects or arrays is not meant to be printed, but it can again be parsed using \macro{\JSONParse}. 

The starred variant, \macro{\JSONParseGetValue*}, rescans the token list before it is typeset, making it possible to place TeX commands in the JSON file. The starred variants of this and similar commands should not be placed in a \macro{\JSONParse} command. In order to adhere to proper JSON syntax, backslashes need to be escaped in the JSON source with another backslash. 

\begin{macrodef}
|\JSONParseGetArrayValues|{<token variable>}{<key>}[<subkey>]{<string>}
|\JSONParseGetArrayValues*|{<token variable>}{<key>}[<subkey>]{<string>}
\end{macrodef}
The command \macro{\JSONParseGetArrayValues} is used to select all values from an array from a parsed JSON string or JSON file. The first argument takes the token variable that holds the parsed JSON data. The second argument takes the key to select the relevant entry from the parsed JSON data using JavaScript syntax. The third argument is optional an can be used to pass a subkey, i.\,e. a key that is used to select a value for every item. The last argument takes a string that is inserted between all values when they are typeset.

For example, let us assume the following JSON data structure is parsed into the token variable \macro{\myJSONdata}:

\begin{codeexample}
{
  "array" : [
    {
      "key_a" : "one" ,
      "key_b" : "two"
    } ,
    {
      "key_a" : "three" ,
      "key_b" : "four"
    }
  ]
}
\end{codeexample}

Then using \macro{\JSONParseGetArrayValues{\myJSONdata}{array}[key_a]{, }}, the text \macro{one, three} is typeset to the document. 

The starred variant, \macro{\JSONParseGetArrayValues*}, rescans the token lists before they are typeset. 

\begin{macrodef}
|\JSONParseUseArrayValues|{<token variable>}{<key>}[<subkey>]{<command name>}
|\JSONParseUseArrayValues*|{<token variable>}{<key>}[<subkey>]{<command name>}
\end{macrodef}
The command \macro{\JSONParseUseArrayValues} takes the same first three arguments as the command \macro{\JSONParseGetArrayValues} and works in a similar way. However, instead of a string that is added between the array items, it takes a command name as fourth argument. This command can be defined beforehand and will be called for every array item. Inside its defintion, the commands \macro{\JSONParseArrayIndex}, \macro{\JSONParseArrayKey} and \macro{\JSONParseArrayValue} can be used which are updated for each item and output the index, the key and the value of the current item respectively. 

For example, let us assume the same JSON data structure as defined above parsed into the token variable \macro{\myJSONdata}. Then, the following can be done:

\JSONParse{\myJSONdata}{ { "array" : [ { "key_a" : "one" , "key_b" : "two" } , { "key_a" : "three" , "key_b" : "four" } ] } }

\begin{codeexamplecolumns}
\newcommand{\myJSONitem}{
  \item \emph{\JSONParseArrayValue}
}

\begin{itemize}
  \JSONParseUseArrayValues{\myJSONdata}
    {array}[key_a]{myJSONitem}
\end{itemize}
\end{codeexamplecolumns}

The starred variant, \macro{\JSONParseUseArrayValues*}, rescans the token lists before they are typeset. 

\begin{macrodef}
|\JSONParseGetArrayCount|{<token variable>}{<key>}
\end{macrodef}
The command \macro{\JSONParseGetArrayCount} takes as frist argument a token variable that holds a parsed JSON string or JSON file and as second argument a key. It returns an integer representing the number of items contained in the selected array.

\subsection{Commands to alter separators and output}

The package provides a set of commands that can be used to change the separators used to select the relevant value in the JSON structure as well as the output that is generated from the JSON data.

\begin{macrodef}
|\JSONParseSetChildSeparator|{<string>}
|\JSONParseSetArraySeparator|{<string>}{<string>}
\end{macrodef}
The two commands \macro{\JSONParseSetChildSeparator} and \macro{\JSONParseSetArraySeparator} can be used to change the separators for child objects or array items in the syntax used to select a specific value in the JSON data structure. Per default, the child separator is a dot (\macro{.}) while the array separators are sqare brackets (\macro{[} and \macro{]}). The command \macro{\JSONParseSetArraySeparator} takes two arguments of which the first is the character used before the array separator and the second is used after. Changing the separators can be useful if keys in the JSON structure already use these characters.

\begin{macrodef}
|\JSONParseSetTrueString|{<string>}
|\JSONParseSetFalseString|{<string>}
|\JSONParseSetNullString|{<string>}
\end{macrodef}
The commands \macro{\JSONParseSetTrueString} and \macro{\JSONParseSetFalseString} as well as the command \macro{\JSONParseSetNullString} can be used to change the output that is typeset if the JSON value is \macro{true}, \macro{false} or \macro{null}. The default strings that are typeset are \macro{true}, \macro{false} or \macro{null} resepctively.

\begin{macrodef}
|\JSONParseSetArrayIndexZeroBased|
|\JSONParseSetArrayIndexOneBased|
\end{macrodef}
The command \macro{\JSONParseSetArrayIndexZeroBased} sets the numbering of the index of array items to zero-based. With the command \macro{\JSONParseSetArrayIndexOneBased}, the indexing starts with one instead. Per default, the package uses zero-based indexing to resemble JavaScript notation.

\subsection{Internal variables and commands}

\begin{macrodef}
|\l_jsonparse_debug_mode_bool|
\end{macrodef}
The token variable \macro{\l_jsonparse_debug_mode_bool} holds the boolean value true if the package is loaded with the \macro{debug} option and false otherwise. If set to true, the debug mode will be used for the following use of \macro{\jsonparse_parse:n}. 

\begin{macrodef}
|\l_jsonparse_child_sep_str|
|\l_jsonparse_array_sep_left_str|
|\l_jsonparse_array_sep_right_str|
|\l_jsonparse_true_str|
|\l_jsonparse_false_str|
|\l_jsonparse_null_str|
|\l_jsonparse_array_index_zero_based_bool|
\end{macrodef}
The token variable \macro{\l_jsonparse_child_sep_str} holds the string \macro{.} per default or the string set by the user command \macro{\JSONParseSetChildSeparator}.

The token variable \macro{\l_jsonparse_array_sep_left_str} holds the string \macro{[} per default or the string given as first argument to the user command \macro{\JSONParseSetArraySeparator}. 

The token variable \macro{\l_jsonparse_array_sep_right_str} holds the string \macro{]} per default or the string given as second argument to the user command \macro{\JSONParseSetArraySeparator}.

The token variable \macro{\l_jsonparse_true_str} holds the string \macro{true} per default or the string set by the user command \macro{\JSONParseSetTrueString}.

{\sloppy The token variable \macro{\l_jsonparse_false_str} holds the string \macro{false} per default or the string set by the user command \macro{\JSONParseSetFalseString}. \par}

The token variable \macro{\l_jsonparse_null_str} holds the string \macro{null} per default or the string set by the user command \macro{\JSONParseSetNullString}.

The token variable \macro{\l_jsonparse_array_index_zero_based_bool} holds a boolean value which is \macro{true} per default or if the user command \macro{\JSONParseSetArrayIndexZeroBased} was called. The boolean value is \macro{false} if the user command \macro{\JSONParseSetArrayIndexOneBased} was called.

\begin{macrodef}
|\g_jsonparse_entries_prop|
\end{macrodef}
The token variable (property list) \macro{\g_jsonparse_entries_prop} holds key-value pairs representing all elements of a JSON data structure generated by a previous run of \macro{\jsonparse_parse:n}. 

\begin{macrodef}
|\jsonparse_parse:n| {<JSON string>}
\end{macrodef}
The command \macro{\jsonparse_parse:n} takes as argument a JSON string and populates the token variable (property list) \macro{\g_jsonparse_entries_prop} with key-value pairs representing all elements of the JSON data structure represented by this string.
  
\begin{macrodef}
|\jsonparse_parse_to_prop:Nn| <token variable> {<JSON string>}
\end{macrodef}
The command \macro{\jsonparse_parse_to_prop:Nn} creates the token variable given as the first arguments as property list and, after having called \macro{\jsonparse_parse:n} using the second argument, sets this newly created property list equal to \macro{\g_jsonparse_entries_prop}.

% =====

\printchanges

\end{document}

%% End of file `jsonparse-doc.tex`.