2

TL;DR

How do I print categorized and subcategorized values declared in a JSON file (or in some similar type of file) inside of a LaTeX document?


In my document, I am assigning a bunch of values to variables. For example:

Variable Value
sheep Baa
cow Moo
dog Woof
cat Meow

What I want to do is to reference these variables throughout my document and have them automatically expand to whatever is in the Value column. If I update any of the values and recompile the document, the change will propagate throughout. I know that I could do something like this:

\newcommand{\sheep}{Baa}

But this is undesirable because it will end up polluting my document with a bunch of unorganized macros that I'll have to keep track of. My preamble will end up looking like this:

\newcommand{\sheep}{Baa}
\newcommand{\cow}{Moo}
\newcommand{\dog}{Woof}
\newcommand{\cat}{Meow}
\newcommand{\chicken}{Bawk}
\newcommand{\snake}{Hiss}
\newcommand{\wolf}{Howl}

Now, say I want to put all of these animals into a category called "animals." I would put a dash, for instance, in the macros (i.e., \animals-sheep), but TeX doesn't support that.

What I am wondering is if there is some package or technique that will help me use a JSON file (YML or some other data interchange format is also acceptable) to do something like this:

file: variables.json

{
     "animals": {
          "sheep": "Baa",
          "cow": "Moo",
          "canines": {
               "dog": "Woof",
               "wolf": "Howl",
               "legs": 4
          },
          "cat": "Meow",
          "birds": {
               "chicken": "bawk",
               "robin": "chirp"
          }
     },
     "letters": {
          "a": "alpha",
          "b": "bravo",
          "c": "charlie"
     }
}

and then in my LaTeX file:

\documentclass{article}

\title{Animals and Letters}
\autor{Me}

\usepackage{jsonParser}

\jsonInclude{./variables.json}

\begin{document}
     \maketitle
     
     Hello there! Here's a demonstration of some animal sounds:
     
     A dog goes \jsonRef{animals.canines.dog} and has \jsonRef{animals.canines.legs} legs. \\
     % The line above should output "A dog goes Woof and has 4 legs."
     The word \jsonRef{letters.a} starts with the letter ``a"!
     % The line above should output "The word alpha starts with the letter “a”!"
\end{document}

If I recompiled the document after updating the value of animals.canines.dog in the ./variables.json to "Bark", then the output should be:

A dog goes Bark and has 4 legs.
The word alpha starts with the letter “a”!

Does anyone have an idea how I can do something like this? Right now, I'm using pdfLaTeX, but anything that supports a normal workflow with biber, gls2bib, and escaping to the shell (for the svg package, for instance) is fine. If LuaTeX is the best option here (which I anticipate may be the case), please provide the appropriate code with explanations and annotations.

9
  • Somewhat related: tex.stackexchange.com/questions/654216/… Aug 21, 2022 at 20:59
  • you could use a dash if you want, or expl3 uses _ so \benZ_animal_sheep{Baa}. You could parse json in TeX or Lua, but unless you actually need this data elsewhere what does it gain over using a tex syntax file? Aug 21, 2022 at 21:07
  • You wish a mechanism for referencing (component of) JSON-objects. Section "6 Objects" of The JSON Data Interchange Syntax says: "The JSON syntax does not impose any restrictions on the strings used as names, does not require that name strings be unique ..." So a JSON-file might contain several objects of same name. What should a parser providing TeX-infrastructure for referencing (components of) JSON-objects , be it written in TeX, be it written in Lua, do in such a situation? Aug 22, 2022 at 2:04
  • You say TeX doesn't support \animals-sheep. Let's be picky: While in the stage of reading from the .tex-input-file for making tokens TeX doesn't support creating control-sequence-tokens whose name consists of several characters, some of them not being of category 11(letter). But in the stage of expansion you can easily have TeX create such tokens via \csname..\endcsname, e.g., \csname animals-sheep\endcsname. You might be interested in \CsNameToCsToken as described in my answer to Why does TeX not allow numbers in command names?. Aug 22, 2022 at 2:18
  • Related: How to loop over the JSON object / list?
    – Alan Munn
    Aug 22, 2022 at 13:56

2 Answers 2

5

I show you the simpler part of your task. How to save data using control sequences constructed by \csname and \endcsname TeX primitives and how to use them. Your JSON example must be re-writen to the collection of \wdefs manually:

\def\wdef#1#2{\expandafter\def \csname word:#1\endcsname {#2}}
\def\jsonRef#1{\ifcsname word:#1\endcsname \csname word:#1\endcsname \else ???\fi}

\wdef {animals.sheep}         {baa}
\wdef {animals.cow}           {Moo}
\wdef {animals.canines.dog}   {Woof}
\wdef {animals.canines.wolf}  {Howl}
\wdef {animals.canines.legs}  {4}
\wdef {animals.cat}           {Meow}
\wdef {animals.birds.chicken} {bawk} 
\wdef {animals.birds.robin}   {chirp}
\wdef {letters.a}             {alpha}
\wdef {letters.b}             {bravo}
\wdef {letters.c}             {charlie}

A dog goes \jsonRef{animals.canines.dog} and has \jsonRef{animals.canines.legs} legs.

The word \jsonRef{letters.a} starts with the letter ``a"!

The \wdef macro defines the macros \word:animals.sheep, \word:animals.cow etc. And the \jsonRef macro uses them. It prints ??? if the macro isn't defined.

Note, that all macros have common prefix word:. Your original idea was not to use any internal prefix, but this is dangerous. If there isn't prefix, then (for example) \wdef {hbox}{foo} redefines the TeX's internal control sequence \hbox and it can cause total crash of the document processing.

The more complicated task is: how to convert your example of JSON file to the set of \wdefs. It is possible by TeX macros, but you can use an external convertor too.

6
  • This is helpful, but is there a way to make \jsonRef work if the variable is defined using \wdef later in the document? Aug 29, 2022 at 13:07
  • Yes, is there a way: using an external file and two runs of TeX.
    – wipet
    Aug 29, 2022 at 20:03
  • Could you elaborate on how I could do this? I had the idea of using a .aux file, but unfortunately, I couldn't find much guidance on the Internet. Aug 30, 2022 at 21:23
  • Does anyone know how to use an .aux file for this? Sep 15, 2022 at 10:51
  • IMHO, the question: how to implement forward references for this case using an external file is a subject of a new thread.
    – wipet
    Sep 15, 2022 at 16:32
2

The LuaLaTeX format by default loads the lualibs package which is ported over from ConTeXt and contains a JSON parser.

\documentclass{article}

\title{Animals and Letters}
\author{Me}

\newcommand\jsonInclude[1]{\directlua{
  userdata = userdata or {}
  userdata.json = utilities.json.load("\luaescapestring{#1}")
}}
\newcommand*\jsonRef[1]{\directlua{tex.sprint(userdata.json.#1)}} % pretty unsafe

\jsonInclude{./variables.json}

\begin{document}
     \maketitle
     
     Hello there! Here's a demonstration of some animal sounds:
     
     A dog goes \jsonRef{animals.canines.dog} and has \jsonRef{animals.canines.legs} legs. \\
     % The line above should output "A dog goes Woof and has 4 legs."
     The word \jsonRef{letters.a} starts with the letter ``a"!
     % The line above should output "The word alpha starts with the letter “a”!"
\end{document}

enter image description here

3
  • Can you clarify what you mean by "pretty unsafe"? What's an example of how someone could exploit this code? Aug 28, 2022 at 17:37
  • 1
    @BenZ. Try for example \jsonRef{animals.canines.dog and error("BOOM")} or a simple typo will also crash LuaTeX. Aug 28, 2022 at 20:14
  • @BenZ. Actually, there is lots of error checking missing. There is also no verification that the JSON actually parsed correctly. Aug 28, 2022 at 20:17

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .