summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2024-04-24 20:20:30 +0000
committerKarl Berry <karl@freefriends.org>2024-04-24 20:20:30 +0000
commitcb73611e2934dbe2cdd34bcf5b8d2dcc4b2fdeec (patch)
tree88a334136c8788ff45aaf3aa43b86ceaff746981
parent892a35d802c0f8440e326b50aae63c728895bb2d (diff)
jsonparse (24apr24)
git-svn-id: svn://tug.org/texlive/trunk@71065 c570f23f-e606-0410-a88d-b1316a301751
-rw-r--r--Master/texmf-dist/doc/latex/jsonparse/README.md6
-rw-r--r--Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.pdfbin394387 -> 410605 bytes
-rw-r--r--Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.tex44
-rw-r--r--Master/texmf-dist/tex/latex/jsonparse/jsonparse.sty297
4 files changed, 173 insertions, 174 deletions
diff --git a/Master/texmf-dist/doc/latex/jsonparse/README.md b/Master/texmf-dist/doc/latex/jsonparse/README.md
index ba872453747..6c268a9372a 100644
--- a/Master/texmf-dist/doc/latex/jsonparse/README.md
+++ b/Master/texmf-dist/doc/latex/jsonparse/README.md
@@ -1,10 +1,10 @@
-![Version 0.7.1](https://img.shields.io/badge/version-0.7.1-blue)
+![Version 0.8.0](https://img.shields.io/badge/version-0.8.0-blue)
# The `jsonparse` package
The `jsonparse` package provides a handy 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.
-Using the commands `\JSONParseFromFile` or `\JSONParse`, JSON data can be stored in a token variable. Using the command `\JSONParseValue`, certain entried can be extracted from the stored data.
+Using the commands `\JSONParseFromFile` or `\JSONParse`, JSON data can be stored in a token variable. Using the command `\JSONParseValue`, entries can be extracted from the stored data.
---
@@ -43,3 +43,5 @@ Let's assume a file with the name `example.json` is stored in the working direct
We can store it in the token variable `\myJSONdata` using the command `\JSONParseFromFile{\myJSONdata}{example.json}`. Calling the command `\JSONParseValue{\myJSONdata}{contactPoint[0].telephone}` would then result in the output `+1 (555) 555-1234` (indices are zero-based per default).
This package including all files is subject to the LPPL 1.3c license.
+Copyright 2024 Jasper Habicht (mail(at)jasperhabicht.de).
+
diff --git a/Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.pdf b/Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.pdf
index eae5ebc7ac1..3c5e5cb9593 100644
--- a/Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.pdf
+++ b/Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.tex b/Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.tex
index 90de47fa5d1..359cab1869b 100644
--- a/Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.tex
+++ b/Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.tex
@@ -1,5 +1,5 @@
% File: jsonparse-doc.tex
-% Copyright 2024 Jasper Habicht (mail@jasperhabicht.de).
+% Copyright 2024 Jasper Habicht (mail(at)jasperhabicht.de).
%
% This work may be distributed and/or modified under the
% conditions of the LaTeX Project Public License version 1.3c,
@@ -11,8 +11,8 @@
% This work has the LPPL maintenance status `maintained'.
%
\documentclass[a4paper]{article}
-\def\jsonparsefileversion{0.7.1}
-\def\jsonparsefiledate{20 April 2024}
+\def\jsonparsefileversion{0.8.0}
+\def\jsonparsefiledate{24 April 2024}
\usepackage[T1]{fontenc}
\usepackage{Alegreya}
@@ -194,6 +194,7 @@
\changes{v0.6.0}{2024/04/15}{Bug fixes, renaming of several commands.}
\changes{v0.7.0}{2024/04/18}{Renaming and rearranging of keys, escaping of special JSON escape sequences added.}
\changes{v0.7.1}{2024/04/20}{Access to first-level keys of object added.}
+\changes{v0.8.0}{2024/04/24}{Internal rewrite, escaping procedures changed.}
\begin{document}
@@ -220,25 +221,30 @@ The package can be loaded with the option \macro{debug}. It will then output to
\section{Escaping and special treatment of the input}\label{sec:escaping}
-\begin{macrodef}
-|escape|={<choice>}
-\end{macrodef}
-
-In general, characters in the JSON source 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. However, certain escaping procedures are available to conform with the way JSON treats certain escape sequences.
+In general, the package reads the JSON source as string, which means that all characters have category code 12 (other), except for spaces which have category code 10 (space). The \macro{\endlinechar} value is set to $-1$. Furthermore, if PDFLaTeX is used, the upper-half of the 8-bit range is set to ``active''. JSON, however, defines a small set of escape sequences and in order to be able to process these, the category code of the backslash is set to 0 (escpape).
-The key \macro{escape} takes one of the values \macro{false}, \macro{basic} or \macro{full}. If no value is given, \macro{basic} is assumed and \macro{basic} is also the default setting if the key is not set at all. See more on setting keys below in section \ref{sec:settings}. The three values select different escape modes that are designed to have as little influence as possible to how TeX normally acts. While it would surely be possible to parse and rescan the input by changing category codes and rescanning the output per default, such practice might result un unintended output in special cases. Therefore, the current approach was chosen where changes to category codes are only introduced with the most most extensive escaping mode and rescanning of token sequences can be controlled by the user.
+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 control symbols \macro{\"}, \macro{\/}, \macro{\\}, \macro{\b}, \macro{\f}, \macro{\n}, \macro{\r}, \macro{\t} and \macro{\u}. These control symbols are prevented from expanding during parsing. For example, \macro{\"} is first defined as \macro{\exp_not:N \"} and only hen typeset, \macro{\"} is expanded to \macro{"}, which ensures that strings are parsed properly.
-Setting the key to \macro{false} disables the treatment of the input as described in the following of this section. The source code will be read and parsed as is. If the source does not contain any escape sequences, empty lines or TeX macro, this choice should be used.
+Similarly, the control symbol \macro{\/} expands eventually to \macro{/} and \macro{\\} to \macro{\c_backslash_str} (i.\,e. a backslash with category code 12). The escape sequence \macro{\u} followed by a hex value consisting of four digits eventually eventually expands to \macro{\char"} followed by the relevant four hex digits. The JSON escape sequences \macro{\b}, \macro{\f}, \macro{\n}, \macro{\r}, \macro{\t} or \macro{\u} (followed by a hex value) eventually expand to token variables of which the contents can be set using the relevant \macro{replacement} key. See more on setting keys below in section \ref{sec:settings}.
-If the key \macro{escape} is set to \macro{basic}, the package allows for empty lines in JSON strings. During parsing, every instance of the TeX macro \macro{\par} is replaced by a space.
+It is possible to insert TeX macros to the JSON source that will eventually be parsed when typesetting. Backslashes of TeX macros need to be escaped by another backslash. The TeX macros \macro{\"} and \macro{\\} must be escaped twice in the JSON source, so that they become \macro{\\\"} and \macro{\\\\} respectively.
-JSON strings cannot contain the two characters \macro{"} and \macro{\}. These two characters need to be escaped with a preceding backslash (\macro{\}). If the key \macro{escape} is set to \macro{basic}, this package therefore redefines locally the TeX control symbols \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, the control symbol \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 this procedure, the TeX macros \macro{\"} and \macro{\\} must be escaped twice in the JSON source, so that they become \macro{\\\"} and \macro{\\\\} respectively.
+\begin{macrodef}
+|\x|[<token variable name>][<key>]
+\end{macrodef}
+Using the control sequence \macro{\x}, it is possible to nest JSON strings into each other. The control sequence takes two arguments delimited by square brackets. The first argument represents the name of the token variable that holds the parsed JSON data where the inserted JSON string should be taken from. The second argument sets the key that should be selected. The following example shows a simple use case:
-With the key \macro{escape} set to \macro{basic}, 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.
+\begin{codeexamplecolumns}
+ \JSONParse{\myJSONdataA}{
+ { "a" : { "b" : "c" } }
+ }
-If the key \macro{escape} is set to \macro{full}, apart from the above, the JSON escape sequences \macro{\b}, \macro{\f}, \macro{\n}, \macro{\r}, \macro{\t} or \macro{\u} (followed by a hex value) are parsed if the JSON source is read in as file using \macro{\JSONParseFromFile} (in other words, full escaping functionality is not supported for the command \macro{\JSONParse}). During parsing, these escape sequences are not expanded and only when being typeset expand to their relevant replacement. The escape sequence \macro{\u} followed by a hex value consisting of four digits eventually expands to \macro{\char"} followed by the relevant four hex digits. The escape sequences \macro{\b} (backspace), \macro{\f} (formfeed), \macro{\n} (linefeed), \macro{\r} (carriage return) and \macro{\t} (horizontal tab) expand to token variables of which the contents can be set using the relevant \macro{replacement} key. See more on setting keys below in section \ref{sec:settings}.
+ \JSONParse{\myJSONdataB}{
+ { "d" : \x[myJSONdataA][a] }
+ }
-Note that if the key \macro{escape} is set to \macro{full}, the category code of \macro{b}, \macro{f}, \macro{n}, \macro{r}, \macro{t} and \macro{u} will be set to 12 (other). Changing the category codes is necessary to be able to define single-letter control sequences in TeX. The category codes of these characters are changed back to 10 (letter) when stored in the property list that contains the parsed JSON string.
+ \JSONParseValue{\myJSONdataB}{d.b}
+\end{codeexamplecolumns}
\begin{macrodef}
replacement/|backspace|={<string>}
@@ -281,11 +287,11 @@ If the JSON string \macro{{ "key" : "value" }} is parsed into the token variable
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{\JSONParseValue{\myJSONdata}{outer_key.inner_key}} can be used. To give an example for an array, the command \macro{\JSONParseValue{\myJSONdata}{key[0]}} selects the first value of the array associated with the key \macro{key} in the JSON string \macro{{ "key" : [ "one" , "two" ] }}.
-The starred variant, \macro{\JSONParseValue*}, rescans the token list before it is typeset (which means that all category codes that may have been changed before are set to the default values), 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.
+The non-starred variant of this command, \macro{\JSONParseValue}, rescans the token list before it is typeset (which means that all category codes that may have been changed before are set to the default values). The starred variant, \macro{\JSONParseValue*}, does not rescan the ouput. The non-starred variants of this and similar commands should therefore not be placed in a \macro{\JSONParse} command.
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 the key \macro{child sep}) returns the whole JSON object as string.
-If the output of whole objects or arrays is meant to be parsed again using \macro{\JSONParse}, the expandable command \macro{\JSONParseExpandableValue} is to be used.
+Whole objects or arrays can also be output as JSON string for further use in other macros using the expandable command \macro{\JSONParseExpandableValue}.
\begin{macrodef}
|\JSONParseArrayValues|{<token variable>}{<key>}[<subkey>]{<string>}
@@ -312,7 +318,7 @@ For example, let us assume the following JSON data structure is parsed into the
Then, when using \macro{\JSONParseArrayValues{\myJSONdata}{array}[key_a]{, }}, `one, three' is typeset to the document.
-The starred variant, \macro{\JSONParseArrayValues*}, rescans the token lists before they are typeset.
+The starred variant, \macro{\JSONParseArrayValues*}, does not rescan the token lists before they are typeset.
\begin{macrodef}
|\JSONParseArrayValuesMap|{<token variable>}{<key>}[<subkey>]{<command name>}
@@ -335,7 +341,7 @@ For example, let us assume the same JSON data structure as defined above parsed
\end{itemize}
\end{codeexamplecolumns}
-The starred variant, \macro{\JSONParseArrayValuesMap*}, rescans the token lists before they are typeset.
+The starred variant, \macro{\JSONParseArrayValuesMap*}, does not rescan the token lists before they are typeset.
\begin{macrodef}
|\JSONParseArrayCount|{<token variable>}{<key>}
diff --git a/Master/texmf-dist/tex/latex/jsonparse/jsonparse.sty b/Master/texmf-dist/tex/latex/jsonparse/jsonparse.sty
index dc42c7a2291..447c70ae1b9 100644
--- a/Master/texmf-dist/tex/latex/jsonparse/jsonparse.sty
+++ b/Master/texmf-dist/tex/latex/jsonparse/jsonparse.sty
@@ -1,5 +1,5 @@
% File: jsonparse.sty
-% Copyright 2024 Jasper Habicht (mail@jasperhabicht.de).
+% Copyright 2024 Jasper Habicht (mail(at)jasperhabicht.de).
%
% This work may be distributed and/or modified under the
% conditions of the LaTeX Project Public License version 1.3c,
@@ -10,7 +10,7 @@
%
% This work has the LPPL maintenance status `maintained'.
%
-\ProvidesExplPackage {jsonparse} {2024-04-20} {0.7.1}
+\ProvidesExplPackage {jsonparse} {2024-04-24} {0.8.0}
{JSON Parse}
\bool_new:N \l__jsonparse_debug_mode_bool
@@ -55,8 +55,6 @@
\str_new:N \l__jsonparse_false_str
\str_new:N \l__jsonparse_null_str
\bool_new:N \l__jsonparse_array_index_zero_based_bool
-\bool_new:N \l__jsonparse_escape_basic_bool
-\bool_new:N \l__jsonparse_escape_full_bool
\str_new:N \l__jsonparse_backspace_str
\str_new:N \l__jsonparse_formfeed_str
@@ -65,21 +63,6 @@
\str_new:N \l__jsonparse_horizontal_tab_str
\keys_define:nn { jsonparse } {
- escape .choice: ,
- escape / false .code:n = {
- \bool_set_false:N \l__jsonparse_escape_basic_bool
- \bool_set_false:N \l__jsonparse_escape_full_bool
- } ,
- escape / basic .code:n = {
- \bool_set_true:N \l__jsonparse_escape_basic_bool
- \bool_set_false:N \l__jsonparse_escape_full_bool
- } ,
- escape / full .code:n = {
- \bool_set_true:N \l__jsonparse_escape_basic_bool
- \bool_set_true:N \l__jsonparse_escape_full_bool
- } ,
- escape .default:n = { basic } ,
- escape .initial:n = { basic } ,
array ~ index ~ zero-based .bool_set:N = \l__jsonparse_array_index_zero_based_bool ,
array ~ index ~ zero-based .default:n = { true } ,
array ~ index ~ zero-based .initial:n = { true } ,
@@ -119,33 +102,31 @@
\cs_new:Npn \str_casefold:n { \str_foldcase:n }
}
+\cs_generate_variant:Nn \tl_gset_rescan:Nnn { Nne }
\cs_generate_variant:Nn \tl_range:nnn { nne , nen }
\cs_generate_variant:Nn \tl_range:Nnn { Nne , Nen }
\cs_generate_variant:Nn \tl_remove_once:Nn { NV }
-\cs_generate_variant:Nn \tl_replace_all:Nnn { Nen }
-\cs_generate_variant:Nn \tl_rescan:nn { ne }
+\cs_generate_variant:Nn \tl_rescan:nn { no , ne }
\cs_generate_variant:Nn \tl_set:Nn { Ne }
-\cs_generate_variant:Nn \tl_set_rescan:Nnn { Nne }
\cs_generate_variant:Nn \tl_trim_spaces:n { e }
\cs_generate_variant:Nn \str_case_e:nn { en }
\cs_generate_variant:Nn \str_casefold:n { e }
\cs_generate_variant:Nn \str_head_ignore_spaces:n { e }
\cs_generate_variant:Nn \prop_gput:Nnn { Nee }
-\cs_generate_variant:Nn \prop_item:Nn { Ne }
-\cs_generate_variant:Nn \prop_put:Nnn { Nee }
+\cs_generate_variant:Nn \prop_item:Nn { Ne , ce }
+\cs_generate_variant:Nn \prop_put:Nnn { Nen , Nee }
\cs_generate_variant:Nn \msg_error:nnnn { nnee }
\cs_generate_variant:Nn \msg_log:nnn { nne }
\prg_generate_conditional_variant:Nnn \tl_if_eq:nn { en } { T }
\prg_generate_conditional_variant:Nnn \tl_if_head_eq_charcode:nN { eN } { T , TF }
-\prg_generate_conditional_variant:Nnn \tl_if_head_is_group:n { e } { TF }
\prg_generate_conditional_variant:Nnn \tl_if_in:nn { nV } { F }
-\prg_generate_conditional_variant:Nnn \str_if_eq:nn { en , eV } { T }
+\prg_generate_conditional_variant:Nnn \str_if_eq:nn { en , eV } { T , TF }
\prop_new:N \g_jsonparse_entries_prop
\prop_new:N \l__jsonparse_temp_prop
-\tl_new:N \l__jsonparse_json_tl
+\tl_new:N \g__jsonparse_json_tl
\tl_new:N \l__jsonparse_input_tl
\tl_new:N \l__jsonparse_temp_tl
\tl_new:N \l__jsonparse_prefix_tl
@@ -166,14 +147,11 @@
% ===
\cctab_const:Nn \c__jsonparse_json_escape_cctab {
- \char_set_catcode_group_begin:n { 123 } % left brace
- \char_set_catcode_group_end:n { 125 } % right brace
- \char_set_catcode_other:N b
- \char_set_catcode_other:N f
- \char_set_catcode_other:N n
- \char_set_catcode_other:N r
- \char_set_catcode_other:N t
- \char_set_catcode_other:N u
+ \cctab_select:N \c_str_cctab
+ \char_set_catcode_escape:n { 92 }
+ \bool_lazy_or:nnF
+ { \sys_if_engine_xetex_p: } { \sys_if_engine_luatex_p: }
+ { \int_step_function:nnN { 128 } { 255 } \char_set_catcode_active:n }
}
% ===
@@ -186,24 +164,8 @@
}
}
\prop_gclear:N \g_jsonparse_entries_prop
- \group_begin:
- \bool_if:NT \l__jsonparse_escape_full_bool {
- \cs_set:Npn \b { \exp_not:N \b }
- \cs_set:Npn \f { \exp_not:N \f }
- \cs_set:Npn \n { \exp_not:N \n }
- \cs_set:Npn \r { \exp_not:N \r }
- \cs_set:Npn \t { \exp_not:N \t }
- \cs_set:Npn \u { \exp_not:N \u }
- }
- \bool_if:NT \l__jsonparse_escape_basic_bool {
- \cs_set:Npn \par { \c_space_tl }
- \cs_set:Npn \" { \exp_not:N \" }
- \cs_set:Npn \/ { \exp_not:N \/ }
- \cs_set:Npn \\ { \exp_not:N \\ }
- }
- \jsonparse_parse:n {#2}
- \group_end:
- \prop_set_eq:NN #1 \g_jsonparse_entries_prop
+ \jsonparse_parse:n {#2}
+ \prop_gset_eq:NN #1 \g_jsonparse_entries_prop
\bool_if:NT \l__jsonparse_debug_mode_bool {
\msg_log:nne { jsonparse } { debug-info } {
JSON ~ parsing ~ done. \iow_newline:
@@ -213,26 +175,46 @@
\cs_new_protected:Npn \jsonparse_parse:n #1 {
\tl_set:Ne \l__jsonparse_input_tl { \tl_trim_spaces:n {#1} }
- \tl_if_head_is_group:eTF { \l__jsonparse_input_tl } {
- \exp_last_unbraced:Ne
- \__jsonparse_parse_object:w \l__jsonparse_input_tl \q_stop
+ \cs_if_exist_use:cTF { __jsonparse_parse_ \str_head_ignore_spaces:e { \l__jsonparse_input_tl } :w } {
+ \l__jsonparse_input_tl \q_stop
} {
- \cs_if_exist_use:cTF { __jsonparse_parse_ \str_head_ignore_spaces:e { \l__jsonparse_input_tl } :w } {
- \l__jsonparse_input_tl \q_stop
- } {
- % other
- \exp_last_unbraced:Ne
- \__jsonparse_parse_other:w \l__jsonparse_input_tl \q_stop
- }
+ % other
+ \exp_last_unbraced:Ne
+ \__jsonparse_parse_other:w \l__jsonparse_input_tl \q_stop
}
}
% ===
-\cs_new:Npn \__jsonparse_parse_object:w #1#2 \q_stop {
+\cs_new:cpn { __jsonparse_parse_ \c_left_brace_str :w } #1 \q_stop {
+ \exp_last_unbraced:Ne
+ \__jsonparse_parse_object_begin:w #1 \q_stop
+}
+
+\cs_new:cpn { __jsonparse_parse_ \c_right_brace_str :w } #1 \q_stop {
+ \exp_last_unbraced:Ne
+ \__jsonparse_parse_object_end:w #1 \q_stop
+}
+
+\cs_new:cpn { __jsonparse_parse_ [ :w } #1 \q_stop {
+ \exp_last_unbraced:Ne
+ \__jsonparse_parse_array_begin:w #1 \q_stop
+}
+
+\cs_new:cpn { __jsonparse_parse_ ] :w } #1 \q_stop {
+ \exp_last_unbraced:Ne
+ \__jsonparse_parse_array_end:w #1 \q_stop
+}
+
+\cs_new:cpn { __jsonparse_parse_ " :w } #1 \q_stop {
+ \exp_last_unbraced:Ne
+ \__jsonparse_parse_string_key:w #1 \q_stop
+}
+
+\exp_last_unbraced:NNo \cs_new:Npn \__jsonparse_parse_object_begin:w \c_left_brace_str #1 \q_stop {
\__jsonparse_array_key_set:
- \tl_set:Nn \l__jsonparse_remainder_tl {#2}
\group_begin:
+ \tl_set:Nn \l__jsonparse_remainder_tl {#1}
% object begin
\bool_if:NT \l__jsonparse_debug_mode_bool {
\msg_log:nnn { jsonparse } { debug-info } {
@@ -244,10 +226,19 @@
} {
\tl_set_eq:NN \l__jsonparse_object_array_key_tl \l__jsonparse_key_tl
\tl_set:Ne \l__jsonparse_prefix_tl { \l__jsonparse_key_tl \l__jsonparse_child_sep_str }
- \tl_set:Nn \l__jsonparse_val_tl {#1}
}
- \tl_set:Nn \l__jsonparse_object_array_val_tl { { #1 } }
- \exp_args:Ne \jsonparse_parse:n {#1}
+ \tl_set:Nn \l__jsonparse_object_array_val_tl { \c_left_brace_str #1 }
+ \__jsonparse_parse_remainder:
+}
+
+\exp_last_unbraced:NNo \cs_new:Npn \__jsonparse_parse_object_end:w \c_right_brace_str #1 \q_stop {
+ \tl_set:Ne \l__jsonparse_object_array_val_tl {
+ \tl_range:Nne \l__jsonparse_object_array_val_tl { 1 } {
+ \int_eval:n {
+ -1 * \tl_count:n {#1} - 1
+ }
+ }
+ }
\prop_gput:Nee \g_jsonparse_entries_prop
{ \l__jsonparse_object_array_key_tl } { \l__jsonparse_object_array_val_tl }
\bool_if:NT \l__jsonparse_debug_mode_bool {
@@ -256,31 +247,17 @@
\iow_char:N \ \iow_char:N \ (obj) ~ \str_use:N \l__jsonparse_object_array_val_tl
}
}
- % object end
- \bool_if:NT \l__jsonparse_debug_mode_bool {
- \msg_log:nnn { jsonparse } { debug-info } {
- (obj ~ end)
- }
- }
\group_end:
+ % object end
+ \bool_if:NT \l__jsonparse_debug_mode_bool {
+ \msg_log:nnn { jsonparse } { debug-info } {
+ (obj ~ end)
+ }
+ }
+ \tl_set:Nn \l__jsonparse_remainder_tl {#1}
\__jsonparse_parse_remainder:
}
-\cs_new:cpn { __jsonparse_parse_ [ :w } #1 \q_stop {
- \exp_last_unbraced:Ne
- \__jsonparse_parse_array_begin:w #1 \q_stop
-}
-
-\cs_new:cpn { __jsonparse_parse_ ] :w } #1 \q_stop {
- \exp_last_unbraced:Ne
- \__jsonparse_parse_array_end:w #1 \q_stop
-}
-
-\cs_new:cpn { __jsonparse_parse_ " :w } #1 \q_stop {
- \exp_last_unbraced:Ne
- \__jsonparse_parse_string_key:w #1 \q_stop
-}
-
\cs_new:Npn \__jsonparse_parse_array_begin:w [ #1 \q_stop {
\__jsonparse_array_key_set:
\group_begin:
@@ -318,13 +295,13 @@
}
}
\group_end:
- \tl_set:Nn \l__jsonparse_remainder_tl {#1}
% array end
\bool_if:NT \l__jsonparse_debug_mode_bool {
\msg_log:nnn { jsonparse } { debug-info } {
(arr ~ end)
}
}
+ \tl_set:Nn \l__jsonparse_remainder_tl {#1}
\__jsonparse_parse_remainder:
}
@@ -349,50 +326,18 @@
% key or string?
\tl_if_head_eq_charcode:eNTF { \l__jsonparse_remainder_tl } : {
\tl_remove_once:NV \l__jsonparse_remainder_tl \c_colon_str
- \bool_if:NT \l__jsonparse_escape_full_bool {
- \clist_map_inline:nn { \b , \f , \n , \r , \t , \u } {
- \tl_if_in:nnT {#1} {##1} {
- \msg_error:nne { jsonparse } { escape-in-key } {
- \token_to_str:N ##1
- }
- }
- }
- }
\tl_set:Ne \l__jsonparse_key_tl { \l__jsonparse_prefix_tl #1 }
- } {
- \group_begin:
- \bool_if:NT \l__jsonparse_escape_full_bool {
- \cs_set:Npn \b { \l__jsonparse_backspace_str }
- \cs_set:Npn \f { \l__jsonparse_formfeed_str }
- \cs_set:Npn \n { \l__jsonparse_linefeed_str }
- \cs_set:Npn \r { \l__jsonparse_carriage_return_str }
- \cs_set:Npn \t { \l__jsonparse_horizontal_tab_str }
- \cs_set:Npn \u { \char" }
- }
- \bool_if:NT \l__jsonparse_escape_basic_bool {
- \cs_set:Npn \" { " }
- \cs_set:Npn \/ { / }
- \cs_set:Npn \\ { \c_backslash_str }
- }
- \tl_set:Nn \l__jsonparse_val_tl {#1}
- \bool_if:NT \l__jsonparse_escape_full_bool {
- \tl_replace_all:Nen \l__jsonparse_val_tl { \char_generate:nn { 98 } { 12 } } { b }
- \tl_replace_all:Nen \l__jsonparse_val_tl { \char_generate:nn { 102 } { 12 } } { f }
- \tl_replace_all:Nen \l__jsonparse_val_tl { \char_generate:nn { 110 } { 12 } } { n }
- \tl_replace_all:Nen \l__jsonparse_val_tl { \char_generate:nn { 114 } { 12 } } { r }
- \tl_replace_all:Nen \l__jsonparse_val_tl { \char_generate:nn { 116 } { 12 } } { t }
- \tl_replace_all:Nen \l__jsonparse_val_tl { \char_generate:nn { 117 } { 12 } } { u }
- }
- \prop_gput:Nee \g_jsonparse_entries_prop
- { \l__jsonparse_key_tl } { \l__jsonparse_val_tl }
- % string
- \bool_if:NT \l__jsonparse_debug_mode_bool {
- \msg_log:nne { jsonparse } { debug-info } {
- (key) ~ \str_use:N \l__jsonparse_key_tl : \iow_newline:
- \iow_char:N \ \iow_char:N \ (str) ~ \str_use:N \l__jsonparse_val_tl
- }
- }
- \group_end:
+ } {
+ \tl_set:Nn \l__jsonparse_val_tl {#1}
+ \prop_gput:Nee \g_jsonparse_entries_prop
+ { \l__jsonparse_key_tl } { \l__jsonparse_val_tl }
+ % string
+ \bool_if:NT \l__jsonparse_debug_mode_bool {
+ \msg_log:nne { jsonparse } { debug-info } {
+ (key) ~ \str_use:N \l__jsonparse_key_tl : \iow_newline:
+ \iow_char:N \ \iow_char:N \ (str) ~ \str_use:N \l__jsonparse_val_tl
+ }
+ }
}
\__jsonparse_parse_remainder:
}
@@ -402,12 +347,13 @@
\tl_set:Nn \l__jsonparse_remainder_tl {#1}
\tl_set:Nn \l__jsonparse_temp_tl { #1 , }
\tl_replace_once:Nnn \l__jsonparse_temp_tl { ] } { , }
+ \tl_replace_once:Nen \l__jsonparse_temp_tl { \c_right_brace_str } { , }
\exp_last_unbraced:Ne
\__jsonparse_parse_other_aux:w \l__jsonparse_temp_tl \q_stop
}
\cs_new:Npn \__jsonparse_parse_other_aux:w #1 , #2 \q_stop {
- \tl_set:Nn \l__jsonparse_temp_tl {#1}
+ \tl_set:Ne \l__jsonparse_temp_tl { \tl_trim_spaces:n {#1} }
\cs_if_exist_use:cF { __jsonparse_parse_ \str_casefold:e { \l__jsonparse_temp_tl } : } {
\fp_if_nan:nTF {#1} {
% nan
@@ -493,11 +439,11 @@
\tl_range:nne {##1} { 1 } { \int_eval:n { \tl_count:n {#2} + 1 } }
} {
{ #2 \l__jsonparse_child_sep_str } {
- \prop_put:Nee \l__jsonparse_temp_prop
+ \prop_put:Nen \l__jsonparse_temp_prop
{ \tl_range:nen {##1} { \int_eval:n { \tl_count:n {#2} + 2 } } { -1 } } {##2}
}
{ #2 \l__jsonparse_array_sep_left_str } {
- \prop_put:Nee \l__jsonparse_temp_prop
+ \prop_put:Nen \l__jsonparse_temp_prop
{ \tl_range:nen {##1} { \int_eval:n { \tl_count:n {#2} + 1 } } { -1 } } {##2}
}
}
@@ -507,12 +453,26 @@
% ===
-\NewDocumentCommand { \JSONParse } { m +m } {
+\NewDocumentCommand { \JSONParse } { m +v } {
\tl_if_eq:enT { \tl_head:n {#2} } { \JSONParseValue } {
\msg_error:nn { jsonparse } { nested-non-expandable }
}
\prop_new:N #1
- \jsonparse_parse_to_prop:Nn #1 {#2}
+ \tl_gclear:N \g__jsonparse_json_tl
+ \group_begin:
+ \cs_set:Npn \" { \exp_not:N \" }
+ \cs_set:Npn \/ { \exp_not:N \/ }
+ \cs_set:Npn \\ { \exp_not:N \\ }
+ \cs_set:Npn \b { \exp_not:N \b }
+ \cs_set:Npn \f { \exp_not:N \f }
+ \cs_set:Npn \n { \exp_not:N \n }
+ \cs_set:Npn \r { \exp_not:N \r }
+ \cs_set:Npn \t { \exp_not:N \t }
+ \cs_set:Npn \u { \exp_not:N \u }
+ \cs_set:Npn \x [ ##1 ] [ ##2 ] { \prop_item:ce {##1} {##2} }
+ \tl_gset_rescan:Nne \g__jsonparse_json_tl { \cctab_select:N \c__jsonparse_json_escape_cctab } {#2}
+ \exp_args:NNe \jsonparse_parse_to_prop:Nn #1 { \g__jsonparse_json_tl }
+ \group_end:
}
\NewDocumentCommand { \JSONParseFromFile } { m m } {
@@ -520,25 +480,55 @@
\msg_error:nnn { jsonparse } { file-not-found }
{#2}
}
- \tl_clear:N \l__jsonparse_json_tl
- \bool_if:NTF \l__jsonparse_escape_full_bool {
- \file_get:nnN {#2} { \cctab_select:N \c__jsonparse_json_escape_cctab } \l__jsonparse_json_tl
- } {
- \file_get:nnN {#2} { } \l__jsonparse_json_tl
- }
\prop_new:N #1
- \jsonparse_parse_to_prop:Nn #1 { \l__jsonparse_json_tl }
+ \tl_gclear:N \g__jsonparse_json_tl
+ \group_begin:
+ \cs_set:Npn \" { \exp_not:N \" }
+ \cs_set:Npn \/ { \exp_not:N \/ }
+ \cs_set:Npn \\ { \exp_not:N \\ }
+ \cs_set:Npn \b { \exp_not:N \b }
+ \cs_set:Npn \f { \exp_not:N \f }
+ \cs_set:Npn \n { \exp_not:N \n }
+ \cs_set:Npn \r { \exp_not:N \r }
+ \cs_set:Npn \t { \exp_not:N \t }
+ \cs_set:Npn \u { \exp_not:N \u }
+ \cs_set:Npn \x [ ##1 ] [ ##2 ] { \prop_item:ce {##1} {##2} }
+ \file_get:nnN {#2} { \cctab_select:N \c__jsonparse_json_escape_cctab } \g__jsonparse_json_tl
+ \exp_args:NNe \jsonparse_parse_to_prop:Nn #1 { \g__jsonparse_json_tl }
+ \group_end:
}
\NewExpandableDocumentCommand { \JSONParseExpandableValue } { m m } {
\prop_item:Ne #1 {#2}
}
+\cs_set_eq:NN \__jsonparse_tex_quote \"
+\cs_set_eq:NN \__jsonparse_tex_backslash \\
+
+\cs_new:Npn \__jsonparse_rescan:n #1 {
+ \group_begin:
+ \cs_set:Npn \" { " }
+ \cs_set:Npn \/ { / }
+ \cs_set:Npn \\ { \c_backslash_str }
+ \cs_set:Npn \b { \l__jsonparse_backspace_str }
+ \cs_set:Npn \f { \l__jsonparse_formfeed_str }
+ \cs_set:Npn \n { \l__jsonparse_linefeed_str }
+ \cs_set:Npn \r { \l__jsonparse_carriage_return_str }
+ \cs_set:Npn \t { \l__jsonparse_horizontal_tab_str }
+ \cs_set:Npn \u { \char" }
+ \tl_set:Ne \l__jsonparse_temp_tl {#1}
+ \tl_analysis_log:N \l__jsonparse_temp_tl
+ \cs_set:Npn \" { \__jsonparse_tex_quote }
+ \cs_set:Npn \\ { \__jsonparse_tex_backslash }
+ \tl_rescan:no { } { \l__jsonparse_temp_tl }
+ \group_end:
+}
+
\NewDocumentCommand { \JSONParseValue } { s m m } {
\bool_if:NTF #1 {
- \tl_rescan:ne { } { \prop_item:Ne #2 {#3} }
+ \prop_item:Ne #2 {#3}
} {
- \prop_item:Ne #2 {#3}
+ \__jsonparse_rescan:n { \prop_item:Ne #2 {#3} }
}
}
@@ -564,9 +554,10 @@
\NewDocumentCommand { \JSONParseArrayValues } { s m m O{} m } {
\group_begin:
- \jsonparse_filter:Nn #2 {#3}
+ \tl_set:Nn \l__jsonparse_temp_tl {#2}
+ \jsonparse_filter:Nn \l__jsonparse_temp_tl {#3}
\bool_set_true:N \l__jsonparse_prop_map_first_bool
- \prop_map_inline:Nn #2 {
+ \prop_map_inline:Nn \l__jsonparse_temp_tl {
\str_if_eq:enT {
\tl_range:nen {##1} { \int_eval:n { -1 * \tl_count:n {#4} } } { -1 }
} {#4} {
@@ -576,9 +567,9 @@
#5
}
\bool_if:NTF #1 {
- \tl_rescan:ne { } {##2}
+ ##2
} {
- ##2
+ \__jsonparse_rescan:n {##2}
}
}
}
@@ -619,9 +610,9 @@
\tl_set:Ne \JSONParseArrayIndex { \__jsonparse_get_array_index:w ##1 \q_stop }
\tl_set:Ne \JSONParseArrayKey {##1}
\bool_if:NTF #1 {
- \tl_set_rescan:Nne \JSONParseArrayValue { } { \prop_item:Nn #2 {##1} }
+ \tl_set:Nn \JSONParseArrayValue { \prop_item:Nn #2 {##1} }
} {
- \tl_set:Ne \JSONParseArrayValue { \prop_item:Nn #2 {##1} }
+ \tl_set:Nn \JSONParseArrayValue { \__jsonparse_rescan:n { \prop_item:Nn #2 {##1} } }
}
\use:c {#5}
}