texlive[71031] Master/texmf-dist: jsonparse (21apr24)

commits+karl at tug.org commits+karl at tug.org
Sun Apr 21 22:15:42 CEST 2024


Revision: 71031
          https://tug.org/svn/texlive?view=revision&revision=71031
Author:   karl
Date:     2024-04-21 22:15:42 +0200 (Sun, 21 Apr 2024)
Log Message:
-----------
jsonparse (21apr24)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/latex/jsonparse/README.md
    trunk/Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.pdf
    trunk/Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.tex
    trunk/Master/texmf-dist/tex/latex/jsonparse/jsonparse.sty

Modified: trunk/Master/texmf-dist/doc/latex/jsonparse/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/latex/jsonparse/README.md	2024-04-21 20:15:32 UTC (rev 71030)
+++ trunk/Master/texmf-dist/doc/latex/jsonparse/README.md	2024-04-21 20:15:42 UTC (rev 71031)
@@ -1,10 +1,10 @@
-![Version 0.5.6](https://img.shields.io/badge/version-0.5.6-blue)
+![Version 0.7.1](https://img.shields.io/badge/version-0.7.1-blue)
 
 # The `jsonparse` package
 
-The `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.
+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 `\JSONParseGetValue`, 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`, certain entried can be extracted from the stored data.
 
 ---
 
@@ -32,7 +32,7 @@
     }
   ],
   "children": [
-    "Tom,
+    "Tom",
     "Doug",
     "Harry"
   ],
@@ -40,6 +40,6 @@
 }
 ```
 
-We can store it in the token variable `\myJSONdata` using the command `\JSONParseFromFile{\myJSONdata}{example.json}`. Calling the command `\JSONParseGetValue{\myJSONdata}{contactPoint[0].telephone}` would then result in the output `+1 (555) 555-1234` (indices are zero-based per default). 
+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.

Modified: trunk/Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.tex
===================================================================
--- trunk/Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.tex	2024-04-21 20:15:32 UTC (rev 71030)
+++ trunk/Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.tex	2024-04-21 20:15:42 UTC (rev 71031)
@@ -5,14 +5,14 @@
 % 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)
+% 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'.
+% This work has the LPPL maintenance status `maintained'.
 % 
 \documentclass[a4paper]{article}
-\def\jsonparsefileversion{0.5.6}
-\def\jsonparsefiledate{11 April 2024}
+\def\jsonparsefileversion{0.7.1}
+\def\jsonparsefiledate{20 April 2024}
 
 \usepackage[T1]{fontenc}
 \usepackage{Alegreya}
@@ -190,6 +190,10 @@
 \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.}
+\changes{v0.5.7}{2024/04/14}{Bug fixes, key-value option setting added.}
+\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.}
 
 \begin{document}
 
@@ -199,33 +203,52 @@
 
 \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.
+The \macro{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.
 
 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. 
+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 installed, 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}
+\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}
+\section{Escaping and special treatment of the input}\label{sec:escaping}
 
-The package allows for empty lines in JSON strings. During parsing, every instance of the TeX macro \macro{\par} is replaced by a space.
+\begin{macrodef}
+|escape|={<choice>}
+\end{macrodef}
 
-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). 
+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.
 
-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. 
+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.
 
-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.
+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.
 
-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. 
+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. 
 
-Setting the internal boolean \macro{\l_jsonparse_escape_input_bool} to false disables the treatment of the input as described in this section.
+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. 
 
+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.
+
+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}.
+
+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.
+
+\begin{macrodef}
+replacement/|backspace|={<string>}
+replacement/|formfeed|={<string>}
+replacement/|linefeed|={<string>}
+replacement/|carriage return|={<string>}
+replacement/|horizontal tab|={<string>}
+\end{macrodef}
+These keys can be used to set the replacement text for the JSON escape sequences \macro{\b} (backspace), \macro{\f} (formfeed), \macro{\n} (linefeed), \macro{\r} (carriage return) and \macro{\t} (horizontal tab). The default replacement string is a space. Only strings can be used as replacement.
+
 \section{Main user commands}
 
 \begin{macrodef}
@@ -233,7 +256,7 @@
 \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). 
+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{\JSONParseValue{\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>}
@@ -240,25 +263,35 @@
 \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.
 
+This command will temporarily change the category code of \macro{b}, \macro{f}, \macro{n}, \macro{r}, \macro{t} and \macro{u} to 12 (other) if full escaping is activated. See more on escaping above in section \ref{sec:escaping}. 
+
 \begin{macrodef}
-|\JSONParseGetValue|{<token variable>}{<key>}
-|\JSONParseGetValue*|{<token variable>}{<key>}
+|\JSONParseKeys|{<token variable>}{<token variable>}
 \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.
+The command \macro{\JSONParseKeys} is used to store all top-level keys of a parsed JSON object as array into a token variable. The command takes as first argument the token variable that holds the parsed JSON data. The second argument takes the token variable that is assigned a JSON array containing the top-level keys of the object represented by the token variable in the first argument. The token variable to store the keys as array is created if it does not exist.
 
-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.
+\begin{macrodef}
+|\JSONParseValue|{<token variable>}{<key>}
+|\JSONParseValue*|{<token variable>}{<key>}
+|\JSONParseExpandableValue|{<token variable>}{<key>}
+\end{macrodef}
+The command \macro{\JSONParseValue} 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.
 
-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" ] }}.
+If the JSON string \macro{{ "key" : "value" }} is parsed into the token variable \macro{\myJSONdata}, using \macro{\JSONParseValue{\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.
 
-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}. 
+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{\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. 
+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. 
 
+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. 
+
 \begin{macrodef}
-|\JSONParseGetArrayValues|{<token variable>}{<key>}[<subkey>]{<string>}
-|\JSONParseGetArrayValues*|{<token variable>}{<key>}[<subkey>]{<string>}
+|\JSONParseArrayValues|{<token variable>}{<key>}[<subkey>]{<string>}
+|\JSONParseArrayValues*|{<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.
+The command \macro{\JSONParseArrayValues} 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 and 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}:
 
@@ -277,15 +310,15 @@
 }
 \end{codeexample}
 
-Then using \macro{\JSONParseGetArrayValues{\myJSONdata}{array}[key_a]{, }}, the text \macro{one, three} is typeset to the document. 
+Then, when using \macro{\JSONParseArrayValues{\myJSONdata}{array}[key_a]{, }}, `one, three' is typeset to the document. 
 
-The starred variant, \macro{\JSONParseGetArrayValues*}, rescans the token lists before they are typeset. 
+The starred variant, \macro{\JSONParseArrayValues*}, rescans the token lists before they are typeset. 
 
 \begin{macrodef}
-|\JSONParseUseArrayValues|{<token variable>}{<key>}[<subkey>]{<command name>}
-|\JSONParseUseArrayValues*|{<token variable>}{<key>}[<subkey>]{<command name>}
+|\JSONParseArrayValuesMap|{<token variable>}{<key>}[<subkey>]{<command name>}
+|\JSONParseArrayValuesMap*|{<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. 
+The command \macro{\JSONParseArrayValuesMap} takes the same first three arguments as the command \macro{\JSONParseArrayValues} 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 definition, 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:
 
@@ -297,86 +330,68 @@
 }
 
 \begin{itemize}
-  \JSONParseUseArrayValues{\myJSONdata}
+  \JSONParseArrayValuesMap{\myJSONdata}
     {array}[key_a]{myJSONitem}
 \end{itemize}
 \end{codeexamplecolumns}
 
-The starred variant, \macro{\JSONParseUseArrayValues*}, rescans the token lists before they are typeset. 
+The starred variant, \macro{\JSONParseArrayValuesMap*}, rescans the token lists before they are typeset. 
 
 \begin{macrodef}
-|\JSONParseGetArrayCount|{<token variable>}{<key>}
+|\JSONParseArrayCount|{<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.
+The command \macro{\JSONParseArrayCount} takes as first argument a token variable holding 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}
+\subsection{Changing separators, output and other settings}\label{sec:settings}
 
-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.
+The package provides a set of keys can be set to change the separators used to select the relevant value in the JSON structure, the output that is generated from the JSON data as well as other things.
 
 \begin{macrodef}
-|\JSONParseSetChildSeparator|{<string>}
-|\JSONParseSetArraySeparator|{<string>}{<string>}
+|\JSONParseSet|{<key-value list>}
 \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.
+The commands \macro{\JSONParseSet} can be used to specify settings via key-value pairs (separated by commas). Keys that are presented here as a subkey (i.\,e. preceded by another key and a slash) can also be set using the syntax \macro{key={subkey}} and multiple subkeys belonging to one key can be combined using a comma as separator. The following keys are available:
 
 \begin{macrodef}
-|\JSONParseSetTrueString|{<string>}
-|\JSONParseSetFalseString|{<string>}
-|\JSONParseSetNullString|{<string>}
+separator/|child|={<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.
+With the key \macro{child sep}, the separator for child objects that is used in the syntax to select a specific value in the JSON data structure can be changed. Per default, the child separator is a dot (\macro{.}). Changing the separator can be useful if keys in the JSON structure already use these characters. 
 
 \begin{macrodef}
-|\JSONParseSetArrayIndexZeroBased|
-|\JSONParseSetArrayIndexOneBased|
+separator/|array left|={<string>}
+separator/|array right|={<string>}
 \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.
+With the keys \macro{array sep left} and \macro{array sep right}, the separators for arrays that are used in the syntax to select a specific value in the JSON data structure can be changed. Per default, the separators are square brackets (\macro{[} and \macro{]}). Changing the separators can be useful if keys in the JSON structure already use these characters. 
 
-\subsection{Internal variables and commands}
-
 \begin{macrodef}
-|\l_jsonparse_debug_mode_bool|
+replacement/|true|={<string>}
+replacement/|false|={<string>}
+replacement/|null|={<string>}
 \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}. 
+With the keys \macro{true}, \macro{false} and \macro{null}, the string that is typeset for true, false and null values can be changed. The default strings that are typeset are \macro{true}, \macro{false} and \macro{null} respectively. Only strings can be used as replacement.
 
 \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|
+|array index zero-based|
+|array index zero-based|={<boolean>}
 \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}.
+If set (or explicitly set to \macro{true}), the key \macro{array index zero-based} sets the numbering of the index of array items to zero-based. If set to false, the indexing starts with one instead. Per default, the package uses zero-based indexing to resemble JavaScript notation.
 
-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}. 
+\subsection{L3 commands}
 
-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.
+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. This command does not escape the input in any way.
   
 \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}.
+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}. If escaping is activated, this command will pre-process the input according to the selected escaping mode before forwarding it to \macro{\jsonparse_parse:n}. See more on escaping above in section \ref{sec:escaping}. 
 
+\begin{macrodef}
+|\jsonparse_filter:Nn| <token variable> {<key>}
+\end{macrodef}
+The command \macro{\jsonparse_parse_to_prop:Nn} processes the token variable given as the first arguments as property list and filters it according to the key given as second argument. Filtering means that for every entry in the property list, the key of this entry is compared against the key given to the command. If the key in the property list starts with the given key, the matching part is removed from the key in the property list. If the keys do not match, the entry is completely removed from the property list. 
+
 % =====
 
 \printchanges

Modified: trunk/Master/texmf-dist/tex/latex/jsonparse/jsonparse.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/jsonparse/jsonparse.sty	2024-04-21 20:15:32 UTC (rev 71030)
+++ trunk/Master/texmf-dist/tex/latex/jsonparse/jsonparse.sty	2024-04-21 20:15:42 UTC (rev 71031)
@@ -5,104 +5,142 @@
 % 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)
+% 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'.
+% This work has the LPPL maintenance status `maintained'.
 % 
-\ProvidesExplPackage {jsonparse} {2024-04-11} {0.5.6} 
+\ProvidesExplPackage {jsonparse} {2024-04-20} {0.7.1} 
   {JSON Parse}
 
-\bool_new:N \l_jsonparse_debug_mode_bool
+\bool_new:N \l__jsonparse_debug_mode_bool
 \keys_define:nn { jsonparse } { 
-    debug .bool_set:N = \l_jsonparse_debug_mode_bool ,
-    debug .default:n  = { true } ,
+  debug .bool_set:N = \l__jsonparse_debug_mode_bool ,
+  debug .default:n  = { true } ,
 }
 \ProcessKeyOptions [ jsonparse ]
 
+\msg_new:nnn { jsonparse } { debug-info } {
+  #1
+}
+
 \msg_new:nnn { jsonparse } { parsing-error } {
-  \msg_warning_text:n { jsonparse } \iow_newline:
+  \msg_error_text:n { jsonparse } \iow_newline:
   Could ~ not ~ parse ~ JSON. \iow_newline:
   Parsing ~ error ~ at ~ key ~ `#1` ~ with ~ value ~ `#2`. 
 }
 
-\msg_new:nnn { jsonparse } { debug-info } {
-  #1
+\msg_new:nnn { jsonparse } { nested-non-expandable } {
+  \msg_error_text:n { jsonparse } \iow_newline:
+  Nested ~ use ~ of ~ \token_to_str:N \JSONParseValue \c_space_tl not ~ allowed. \iow_newline:
+  Use ~ \token_to_str:N \JSONParseExpandableValue \c_space_tl instead. 
 }
 
-% ===
-	
-\str_new:N \l_jsonparse_child_sep_str
-\str_set:Nn \l_jsonparse_child_sep_str { . }
-\str_new:N \l_jsonparse_array_sep_left_str
-\str_set:Nn \l_jsonparse_array_sep_left_str { [ }
-\str_new:N \l_jsonparse_array_sep_right_str
-\str_set:Nn \l_jsonparse_array_sep_right_str { ] }
-\str_new:N \l_jsonparse_true_str
-\str_set:Nn \l_jsonparse_true_str { true }
-\str_new:N \l_jsonparse_false_str
-\str_set:Nn \l_jsonparse_false_str { false }
-\str_new:N \l_jsonparse_null_str
-\str_set:Nn \l_jsonparse_null_str { null }
-
-\bool_new:N \l_jsonparse_array_index_zero_based_bool
-\bool_set_true:N \l_jsonparse_array_index_zero_based_bool
-
-\bool_new:N \l_jsonparse_escape_input_bool
-\bool_set_true:N \l_jsonparse_escape_input_bool
-
-\NewDocumentCommand { \JSONParseSetChildSeparator } { m } {
-  \str_set:Nn \l_jsonparse_child_sep_str { #1 }
+\msg_new:nnn { jsonparse } { file-not-found } {
+  \msg_error_text:n { jsonparse } \iow_newline:
+  Could ~ not ~ find ~ file ~ #1.
 }
 
-\NewDocumentCommand { \JSONParseSetArraySeparator } { m m } {
-  \str_set:Nn \l_jsonparse_array_sep_left_str { #1 }
-  \str_set:Nn \l_jsonparse_array_sep_right_str { #2 }
+\msg_new:nnn { jsonparse } { escape-in-key } {
+  \msg_error_text:n { jsonparse } \iow_newline:
+  Invalid ~ escape ~ sequence ~ #1 ~ in ~ key.
 }
 
-\NewDocumentCommand { \JSONParseSetTrueString } { m } {
-  \str_set:Nn \l_jsonparse_true_str { #1 }
-}
+% ===
+	
+\str_new:N \l__jsonparse_child_sep_str
+\str_new:N \l__jsonparse_array_sep_left_str
+\str_new:N \l__jsonparse_array_sep_right_str
+\str_new:N \l__jsonparse_true_str
+\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
 
-\NewDocumentCommand { \JSONParseSetFalseString } { m } {
-  \str_set:Nn \l_jsonparse_false_str { #1 }
-}
+\str_new:N \l__jsonparse_backspace_str
+\str_new:N \l__jsonparse_formfeed_str
+\str_new:N \l__jsonparse_linefeed_str
+\str_new:N \l__jsonparse_carriage_return_str
+\str_new:N \l__jsonparse_horizontal_tab_str
 
-\NewDocumentCommand { \JSONParseSetNullString } { m } {
-  \str_set:Nn \l_jsonparse_null_str { #1 }
+\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 } ,
+  separator                       .code:n     = { \keys_set:nn { jsonparse / separator } {#1} },
+  separator / child               .str_set:N  = \l__jsonparse_child_sep_str ,
+  separator / child               .initial:n  = { . } ,
+  separator / array ~ left        .str_set:N  = \l__jsonparse_array_sep_left_str ,
+  separator / array ~ left        .initial:n  = { [ } ,
+  separator / array ~ right       .str_set:N  = \l__jsonparse_array_sep_right_str ,
+  separator / array ~ right       .initial:n  = { ] } ,
+  replacement                     .code:n     = { \keys_set:nn { jsonparse / replacement } {#1} },
+  replacement / true              .str_set:N  = \l__jsonparse_true_str ,
+  replacement / true              .initial:n  = { true } ,
+  replacement / false             .str_set:N  = \l__jsonparse_false_str ,
+  replacement / false             .initial:n  = { false } ,
+  replacement / null              .str_set:N  = \l__jsonparse_null_str ,
+  replacement / null              .initial:n  = { null } ,
+  replacement / backspace         .str_set:N  = \l__jsonparse_backspace_str ,
+  replacement / backspace         .initial:n  = { ~ } ,
+  replacement / formfeed          .str_set:N  = \l__jsonparse_formfeed_str ,
+  replacement / formfeed          .initial:n  = { ~ } ,
+  replacement / linefeed          .str_set:N  = \l__jsonparse_linefeed_str ,
+  replacement / linefeed          .initial:n  = { ~ } ,
+  replacement / carriage ~ return .str_set:N  = \l__jsonparse_carriage_return_str ,
+  replacement / carriage ~ return .initial:n  = { ~ } ,
+  replacement / horizontal ~ tab  .str_set:N  = \l__jsonparse_horizontal_tab_str ,
+  replacement / horizontal ~ tab  .initial:n  = { ~ } 
 }
 
-\NewDocumentCommand { \JSONParseSetArrayIndexZeroBased } { } {
-  \bool_set_true:N \l_jsonparse_array_index_zero_based_bool
+\NewDocumentCommand { \JSONParseSet } { m } {
+  \keys_set:nn { jsonparse } {#1}
 }
 
-\NewDocumentCommand { \JSONParseSetArrayIndexOneBased } { } {
-  \bool_set_false:N \l_jsonparse_array_index_zero_based_bool
+% ===
+
+\cs_if_exist:NF \str_casefold:n { 
+  \cs_new:Npn \str_casefold:n { \str_foldcase:n }
 }
 
-% ===
-
-\cs_generate_variant:Nn \tl_count:n { e }
 \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 { Ne }
+\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_set:Nn { Ne }
 \cs_generate_variant:Nn \tl_set_rescan:Nnn { Nne }
-\cs_generate_variant:Nn \tl_trim_spaces:n { V }
+\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 \str_set:Nn { Ne }
 \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 \seq_use:Nn { Ne }
-\cs_generate_variant:Nn \msg_log:nnn { nno }
+\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 \str_case:nn { en } { F }
-\prg_generate_conditional_variant:Nnn \str_case_e:nn { en } { F }
-\prg_generate_conditional_variant:Nnn \str_if_eq:nn { ee } { T }
-\prg_generate_conditional_variant:Nnn \str_if_eq:nn { en } { T , TF }
+\prg_generate_conditional_variant:Nnn \tl_if_in:nn { nV } { F }
+\prg_generate_conditional_variant:Nnn \str_if_eq:nn { en , eV } { T }
 
 \prop_new:N \g_jsonparse_entries_prop
 \prop_new:N \l__jsonparse_temp_prop
@@ -115,7 +153,6 @@
 \tl_new:N \l__jsonparse_val_tl
 \tl_new:N \l__jsonparse_object_array_key_tl
 \tl_new:N \l__jsonparse_object_array_val_tl
-\tl_new:N \l__jsonparse_array_prefix_tl
 \tl_new:N \l__jsonparse_remainder_tl
 
 \int_new:N \l__jsonparse_array_index_int
@@ -122,14 +159,27 @@
 \int_new:N \l__jsonparse_array_count_int
 \int_new:N \l__jsonparse_array_count_last_int
 
-\bool_new:N \l__prop_map_first_bool
+\bool_new:N \l__jsonparse_prop_map_first_bool
 
 \ior_new:N \l__jsonparse_json_ior
 
 % ===
 
-\cs_new:Npn \jsonparse_parse_to_prop:Nn #1#2 {
-  \bool_if:NT \l_jsonparse_debug_mode_bool {
+\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
+}
+
+% ===
+
+\cs_new_protected:Npn \jsonparse_parse_to_prop:Nn #1#2 {
+  \bool_if:NT \l__jsonparse_debug_mode_bool {
     \msg_log:nne { jsonparse } { debug-info } {
       \iow_newline: 
       Parsing ~ JSON ~ ... 
@@ -137,16 +187,24 @@
   }
   \prop_gclear:N \g_jsonparse_entries_prop
   \group_begin:
-    \bool_if:NT \l_jsonparse_escape_input_bool {
-      \tl_set_eq:NN \par \c_space_tl
-      \tl_set:Nn \" { \exp_not:N \" }
-      \tl_set:Nn \/ { \exp_not:N \/ }
-      \tl_set:Nn \\ { \exp_not:N \\ }
+    \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 
-  \bool_if:NT \l_jsonparse_debug_mode_bool {
+  \bool_if:NT \l__jsonparse_debug_mode_bool {
     \msg_log:nne { jsonparse } { debug-info } {
       JSON ~ parsing ~ done. \iow_newline: 
     } 
@@ -153,25 +211,14 @@
   }
 }
 
-\cs_new:Npn \jsonparse_parse:n #1 {
+\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 
   } {
-    \str_case_e:enF { \str_head_ignore_spaces:e { \l__jsonparse_input_tl } } {
-      { [ } { 
-        \exp_last_unbraced:Ne 
-          \__jsonparse_parse_array_begin:w \l__jsonparse_input_tl \q_stop 
-      }
-      { ] } { 
-        \exp_last_unbraced:Ne 
-          \__jsonparse_parse_array_end:w \l__jsonparse_input_tl \q_stop 
-      }
-      { " } { 
-        \exp_last_unbraced:Ne 
-          \__jsonparse_parse_string_key: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 
@@ -184,26 +231,26 @@
 
 \cs_new:Npn \__jsonparse_parse_object:w #1#2 \q_stop {
   \__jsonparse_array_key_set:
-  \tl_set:Ne \l__jsonparse_remainder_tl {#2}
+  \tl_set:Nn \l__jsonparse_remainder_tl {#2}
   \group_begin:
     % object begin
-    \bool_if:NT \l_jsonparse_debug_mode_bool {
-      \msg_log:nne { jsonparse } { debug-info } {
+    \bool_if:NT \l__jsonparse_debug_mode_bool {
+      \msg_log:nnn { jsonparse } { debug-info } {
         (obj ~ begin) 
       } 
     }
     \tl_if_empty:NTF \l__jsonparse_key_tl {
-      \tl_set:Ne \l__jsonparse_object_array_key_tl { \l_jsonparse_child_sep_str }
+      \tl_set_eq:NN \l__jsonparse_object_array_key_tl \l__jsonparse_child_sep_str 
     } {
-      \tl_set:Ne \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:Ne \l__jsonparse_val_tl {#1}
+      \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:Ne \l__jsonparse_object_array_val_tl { { #1 } }
+    \tl_set:Nn \l__jsonparse_object_array_val_tl { { #1 } }
     \exp_args:Ne \jsonparse_parse:n {#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 {
+    \bool_if:NT \l__jsonparse_debug_mode_bool {
       \msg_log:nne { jsonparse } { debug-info } {
         (key) ~ \str_use:N \l__jsonparse_object_array_key_tl : \iow_newline:
         \iow_char:N \  \iow_char:N \  (obj) ~ \str_use:N \l__jsonparse_object_array_val_tl 
@@ -210,8 +257,8 @@
       } 
     } 
     % object end
-    \bool_if:NT \l_jsonparse_debug_mode_bool {
-      \msg_log:nne { jsonparse } { debug-info } {
+    \bool_if:NT \l__jsonparse_debug_mode_bool {
+      \msg_log:nnn { jsonparse } { debug-info } {
         (obj ~ end) 
       } 
     }
@@ -219,23 +266,38 @@
   \__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:
-    \tl_set:Ne \l__jsonparse_remainder_tl {#1}
+    \tl_set:Nn \l__jsonparse_remainder_tl {#1}
     % array begin
-    \bool_if:NT \l_jsonparse_debug_mode_bool {
-      \msg_log:nne { jsonparse } { debug-info } {
+    \bool_if:NT \l__jsonparse_debug_mode_bool {
+      \msg_log:nnn { jsonparse } { debug-info } {
         (arr ~ begin) 
       }   
     }
     \int_zero:N \l__jsonparse_array_index_int
-    \bool_if:NT \l_jsonparse_array_index_zero_based_bool {
+    \bool_if:NT \l__jsonparse_array_index_zero_based_bool {
       \int_decr:N \l__jsonparse_array_index_int
     }
-    \tl_set:Ne \l__jsonparse_object_array_key_tl { \l__jsonparse_key_tl }
+    \tl_set_eq:NN \l__jsonparse_object_array_key_tl \l__jsonparse_key_tl
     \tl_set:Nn \l__jsonparse_object_array_val_tl { [ #1 }
-    \tl_set:Ne \l__jsonparse_prefix_tl { \l__jsonparse_key_tl \l_jsonparse_array_sep_left_str }
+    \tl_set:Ne \l__jsonparse_prefix_tl { \l__jsonparse_key_tl \l__jsonparse_array_sep_left_str }
     \__jsonparse_parse_remainder:
 }
 
@@ -243,13 +305,13 @@
     \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:e {#1} - 1
+          -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 {
+    \bool_if:NT \l__jsonparse_debug_mode_bool {
       \msg_log:nne { jsonparse } { debug-info } {
         (key) ~ \str_use:N \l__jsonparse_object_array_key_tl : \iow_newline:
         \iow_char:N \  \iow_char:N \  (arr) ~ \str_use:N \l__jsonparse_object_array_val_tl
@@ -256,10 +318,10 @@
       } 
     } 
   \group_end:
-  \tl_set:Ne \l__jsonparse_remainder_tl {#1}
+  \tl_set:Nn \l__jsonparse_remainder_tl {#1}
   % array end
-  \bool_if:NT \l_jsonparse_debug_mode_bool {
-    \msg_log:nne { jsonparse } { debug-info } {
+  \bool_if:NT \l__jsonparse_debug_mode_bool {
+    \msg_log:nnn { jsonparse } { debug-info } {
       (arr ~ end) 
     } 
   }
@@ -267,11 +329,16 @@
 }
 
 \cs_new:Npn \__jsonparse_array_key_set: {
-  \str_if_eq:eeT { \tl_item:Nn \l__jsonparse_prefix_tl { -1 } } 
-    { \l_jsonparse_array_sep_left_str } {
+  \str_if_eq:eVT { 
+    \tl_range:Nen \l__jsonparse_prefix_tl { 
+      \int_eval:n { 
+        -1 * \tl_count:N \l__jsonparse_array_sep_left_str 
+      } 
+    } { -1 }
+  } \l__jsonparse_array_sep_left_str {
     \int_incr:N \l__jsonparse_array_index_int
     \tl_set:Ne \l__jsonparse_key_tl { 
-      \l__jsonparse_prefix_tl \int_use:N \l__jsonparse_array_index_int \l_jsonparse_array_sep_right_str 
+      \l__jsonparse_prefix_tl \int_use:N \l__jsonparse_array_index_int \l__jsonparse_array_sep_right_str 
     }
   }
 }
@@ -278,21 +345,48 @@
 
 \cs_new:Npn \__jsonparse_parse_string_key:w " #1 " #2 \q_stop {
   \__jsonparse_array_key_set:
-  \tl_set:Ne \l__jsonparse_remainder_tl {#2}
+  \tl_set:Ne \l__jsonparse_remainder_tl { \tl_trim_spaces:n {#2} }
   % key or string?
-  \str_if_eq:enTF { \tl_head:n { #2 } } { : } {
-    \tl_remove_once:Ne \l__jsonparse_remainder_tl { \c_colon_str }
+  \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:
-      \tl_set:Nn \" { " }
-      \tl_set:Nn \/ { / }
-      \tl_set:Nn \\ { \c_backslash_str }
-      \tl_set:Ne \l__jsonparse_val_tl {#1}
+      \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 {
+      \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
@@ -305,8 +399,8 @@
 
 \cs_new:Npn \__jsonparse_parse_other:w #1 \q_stop {
   \__jsonparse_array_key_set:
-  \tl_set:Ne \l__jsonparse_remainder_tl {#1}
-  \tl_set:Ne \l__jsonparse_temp_tl { #1 , }
+  \tl_set:Nn \l__jsonparse_remainder_tl {#1}
+  \tl_set:Nn \l__jsonparse_temp_tl { #1 , }
   \tl_replace_once:Nnn \l__jsonparse_temp_tl { ] } { , }
   \exp_last_unbraced:Ne
     \__jsonparse_parse_other_aux:w \l__jsonparse_temp_tl \q_stop
@@ -313,63 +407,18 @@
 }
 
 \cs_new:Npn \__jsonparse_parse_other_aux:w #1 , #2 \q_stop {
-  \tl_set:Ne \l__jsonparse_temp_tl {#1}
-  \str_case:enF { 
-    \str_lowercase:f { \l__jsonparse_temp_tl } 
-  } {
-    { true } {
-      \tl_set:Ne \l__jsonparse_val_tl { 
-        \l_jsonparse_true_str 
-      }
-      \prop_gput:Nee \g_jsonparse_entries_prop 
-        { \l__jsonparse_key_tl } { \l__jsonparse_val_tl }
-      % true
-      \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 \  (tru) ~ \str_use:N \l__jsonparse_val_tl
-        } 
-      }  
-    }
-    { false } {
-      \tl_set:Ne \l__jsonparse_val_tl { 
-        \l_jsonparse_false_str 
-      }
-      \prop_gput:Nee \g_jsonparse_entries_prop 
-        { \l__jsonparse_key_tl } { \l__jsonparse_val_tl }
-      % false
-      \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 \  (fal) ~ \str_use:N \l__jsonparse_val_tl
-        } 
-      }
-    }
-    { null } {
-      \tl_set:Ne \l__jsonparse_val_tl { 
-        \l_jsonparse_null_str 
-      }
-      \prop_gput:Nee \g_jsonparse_entries_prop 
-        { \l__jsonparse_key_tl } { \l__jsonparse_val_tl }
-      % null
-      \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 \  (nul) ~ \str_use:N \l__jsonparse_val_tl
-        } 
-      }
-    }
-  } {
-    \fp_compare:nNnTF {#1} ? { 0 } { 
+  \tl_set:Nn \l__jsonparse_temp_tl {#1}
+  \cs_if_exist_use:cF { __jsonparse_parse_ \str_casefold:e { \l__jsonparse_temp_tl } : } {
+    \fp_if_nan:nTF {#1} { 
       % nan
       \msg_error:nnee { jsonparse } { parsing-error }
         { \l__jsonparse_key_tl } {#1}
     } { 
-      \tl_set:Ne \l__jsonparse_val_tl {#1}
+      \tl_set:Nn \l__jsonparse_val_tl {#1}
       \prop_gput:Nee \g_jsonparse_entries_prop 
         { \l__jsonparse_key_tl } { \l__jsonparse_val_tl }
       % number
-      \bool_if:NT \l_jsonparse_debug_mode_bool {
+      \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 \  (num) ~ \str_use:N \l__jsonparse_val_tl
@@ -377,11 +426,11 @@
       }
     }
   }
-  \tl_set:Ne \l__jsonparse_remainder_tl { \tl_trim_spaces:V \l__jsonparse_remainder_tl }
+  \tl_set:Ne \l__jsonparse_remainder_tl { \tl_trim_spaces:e { \l__jsonparse_remainder_tl } }
   \tl_set:Ne \l__jsonparse_remainder_tl { 
     \tl_range:Nen \l__jsonparse_remainder_tl { 
       \int_eval:n {
-        \tl_count:e { #1 } + 1 
+        \tl_count:n {#1} + 1 
       }
     } { -1 }
   }
@@ -388,9 +437,48 @@
   \__jsonparse_parse_remainder:
 }
 
+\cs_new:Npn \__jsonparse_parse_true: {
+  \tl_set_eq:NN \l__jsonparse_val_tl \l__jsonparse_true_str 
+  \prop_gput:Nee \g_jsonparse_entries_prop 
+    { \l__jsonparse_key_tl } { \l__jsonparse_val_tl }
+  % true
+  \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 \  (tru) ~ \str_use:N \l__jsonparse_val_tl
+    } 
+  }
+}
+
+\cs_new:Npn \__jsonparse_parse_false: {
+  \tl_set_eq:NN \l__jsonparse_val_tl \l__jsonparse_false_str 
+  \prop_gput:Nee \g_jsonparse_entries_prop 
+    { \l__jsonparse_key_tl } { \l__jsonparse_val_tl }
+  % false
+  \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 \  (fal) ~ \str_use:N \l__jsonparse_val_tl
+    } 
+  }
+}
+
+\cs_new:Npn \__jsonparse_parse_null: {
+  \tl_set_eq:NN \l__jsonparse_val_tl \l__jsonparse_null_str
+  \prop_gput:Nee \g_jsonparse_entries_prop 
+    { \l__jsonparse_key_tl } { \l__jsonparse_val_tl }
+  % null
+  \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 \  (nul) ~ \str_use:N \l__jsonparse_val_tl
+    } 
+  }
+}
+
 \cs_new:Npn \__jsonparse_parse_remainder: {
-  \tl_set:Ne \l__jsonparse_remainder_tl { \tl_trim_spaces:V \l__jsonparse_remainder_tl }
-  \str_if_eq:enT { \tl_head:N \l__jsonparse_remainder_tl } { , } {
+  \tl_set:Ne \l__jsonparse_remainder_tl { \tl_trim_spaces:e { \l__jsonparse_remainder_tl } }
+  \tl_if_head_eq_charcode:eNT { \l__jsonparse_remainder_tl } , {
     \tl_remove_once:Nn \l__jsonparse_remainder_tl { , }
   }
   \tl_if_empty:NF \l__jsonparse_remainder_tl {
@@ -398,18 +486,19 @@
   }
 }	
 
-\cs_new:Npn \jsonparse_filter:Nn #1#2 {
+\cs_new_protected:Npn \jsonparse_filter:Nn #1#2 {
+  \prop_clear:N \l__jsonparse_temp_prop
   \prop_map_inline:Nn #1 {
     \str_case_e:en { 
-      \tl_range:nne {##1} { 1 } { \int_eval:n { \tl_count:e {#2} + 1 } } 
+      \tl_range:nne {##1} { 1 } { \int_eval:n { \tl_count:n {#2} + 1 } } 
     } {
-      { #2 \l_jsonparse_child_sep_str } {
+      { #2 \l__jsonparse_child_sep_str } {
         \prop_put:Nee \l__jsonparse_temp_prop 
-          { \tl_range:nen {##1} { \int_eval:n { \tl_count:e {#2} + 2 } } { -1 } } {##2} 
+          { \tl_range:nen {##1} { \int_eval:n { \tl_count:n {#2} + 2 } } { -1 } } {##2} 
       }
-      { #2 \l_jsonparse_array_sep_left_str } {
+      { #2 \l__jsonparse_array_sep_left_str } {
         \prop_put:Nee \l__jsonparse_temp_prop 
-          { \tl_range:nen {##1} { \int_eval:n { \tl_count:e {#2} + 1 } } { -1 } } {##2} 
+          { \tl_range:nen {##1} { \int_eval:n { \tl_count:n {#2} + 1 } } { -1 } } {##2} 
       }
     }
   }
@@ -418,43 +507,71 @@
 
 % ===
 
-\NewExpandableDocumentCommand { \JSONParse } { m +m } {
+\NewDocumentCommand { \JSONParse } { m +m } {
+  \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} 
 }
 
-\NewExpandableDocumentCommand { \JSONParseFromFile } { m m } {
-  \ior_open:Nn \l__jsonparse_json_ior {#2}
-  
+\NewDocumentCommand { \JSONParseFromFile } { m m } {
+  \file_if_exist:nF {#2} {
+    \msg_error:nnn { jsonparse } { file-not-found }
+      {#2}
+  }
   \tl_clear:N \l__jsonparse_json_tl
-  \ior_map_inline:Nn \l__jsonparse_json_ior {
-    \tl_put_right:Nn \l__jsonparse_json_tl {##1}
+  \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
   }
-  
-  \ior_close:N \l__jsonparse_json_ior
-
   \prop_new:N #1
   \jsonparse_parse_to_prop:Nn #1 { \l__jsonparse_json_tl } 
 }
 
-\NewExpandableDocumentCommand { \JSONParseGetValue } { s m m } {
+\NewExpandableDocumentCommand { \JSONParseExpandableValue } { m m } {
+  \prop_item:Ne #1 {#2} 
+}
+
+\NewDocumentCommand { \JSONParseValue } { s m m } {
   \bool_if:NTF #1 {
-    \tl_rescan:ne { } { \prop_item:Nn #2 {#3} }
+    \tl_rescan:ne { } { \prop_item:Ne #2 {#3} }
   } {
-    \prop_item:Nn #2 {#3} 
+    \prop_item:Ne #2 {#3} 
   }
 }
 
-\NewExpandableDocumentCommand { \JSONParseGetArrayValues } { s m m O{} m } {
+\NewDocumentCommand { \JSONParseKeys } { m m } {
+  \tl_if_exist:NF #2 {
+    \tl_new:N #2
+  }
+  \bool_set_true:N \l__jsonparse_prop_map_first_bool
+  \tl_set:Nn \l__jsonparse_temp_tl { [ }
+    \prop_map_inline:Nn #1 {
+      \tl_if_in:nVF {##1} \l__jsonparse_child_sep_str { 
+        \bool_if:NTF \l__jsonparse_prop_map_first_bool { 
+          \bool_set_false:N \l__jsonparse_prop_map_first_bool
+        } {  
+          \tl_put_right:Nn \l__jsonparse_temp_tl { , } 
+        }
+        \tl_put_right:Nn \l__jsonparse_temp_tl { " ##1 " }
+      }
+    }
+  \tl_put_right:Nn \l__jsonparse_temp_tl { ] } 
+  \tl_set_eq:NN #2 \l__jsonparse_temp_tl
+}
+
+\NewDocumentCommand { \JSONParseArrayValues } { s m m O{} m } {
   \group_begin:
     \jsonparse_filter:Nn #2 {#3}
-    \bool_set_true:N \l__prop_map_first_bool
+    \bool_set_true:N \l__jsonparse_prop_map_first_bool
     \prop_map_inline:Nn #2 {
       \str_if_eq:enT { 
         \tl_range:nen {##1} { \int_eval:n { -1 * \tl_count:n {#4} } } { -1 } 
       } {#4} {
-        \bool_if:NTF \l__prop_map_first_bool { 
-          \bool_set_false:N \l__prop_map_first_bool
+        \bool_if:NTF \l__jsonparse_prop_map_first_bool { 
+          \bool_set_false:N \l__jsonparse_prop_map_first_bool
         } {        
           #5 
         }
@@ -472,7 +589,7 @@
   #1
 }
 
-\NewExpandableDocumentCommand { \JSONParseGetArrayCount } { m m } {
+\NewDocumentCommand { \JSONParseArrayCount } { m m } {
   \group_begin:
     \jsonparse_filter:Nn #1 {#2}
     \int_zero:N \l__jsonparse_array_count_int
@@ -491,7 +608,7 @@
   \group_end:
 }
 
-\NewExpandableDocumentCommand { \JSONParseUseArrayValues } { s m m O{} m } {
+\NewDocumentCommand { \JSONParseArrayValuesMap } { s m m O{} m } {
   \group_begin:
     \jsonparse_filter:Nn #2 {#3}
     \prop_map_inline:Nn #2 {
@@ -500,7 +617,7 @@
       } {#4} {
         \int_incr:N \l__jsonparse_array_index_int
         \tl_set:Ne \JSONParseArrayIndex { \__jsonparse_get_array_index:w ##1 \q_stop }
-        \tl_set:Ne \JSONParseArrayKey { ##1 }
+        \tl_set:Ne \JSONParseArrayKey {##1}
         \bool_if:NTF #1 {
           \tl_set_rescan:Nne \JSONParseArrayValue { } { \prop_item:Nn #2 {##1} }
         } {



More information about the tex-live-commits mailing list.