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.

  • Somewhat related: https://tex.stackexchange.com/questions/654216/is-there-a-way-to-automatically-generate-a-label-and-caption-for-a-series-of-fig/654274#654274 – John Kormylo Aug 21 '22 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? – David Carlisle Aug 21 '22 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? – Ulrich Diez Aug 22 '22 at 02: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?. – Ulrich Diez Aug 22 '22 at 02:18
  • Thank you for the suggestions, everyone! @DavidCarlisle, I have two main reasons for wanting to use JSON instead of tex files: (a) I find JSON easier to read and organize for this kind of hierarchical data, and (b) even if I don't intend to use the data somewhere else right now, if I decide to do so later, using JSON will make it a lot easier. – Ben Zelnick Aug 28 '22 at 17:44
  • @UlrichDiez, I believe that the typical method for parsing JSON with duplicate name strings is to use the value in the last name-value assignment (see this answer on StackExchange). – Ben Zelnick Aug 28 '22 at 17:44
  • @BenZ. That's what Lua-based json-parsers do which attempt to create a data-structure/a Lua table where each element can be referenced by some sort of unique unambiguous identifier. But the crucial point is: Not all json-files denote data-structures where each element is to be referable by a unique unambiguous identifier. E.g., in some contexts the name-string of a json-object might just denote how to categorize/treat the object in question when processing all objects one by one. – Ulrich Diez Aug 28 '22 at 18:13
  • @BenZ. I've been thinking around for a while how to transform json files and yaml-files into data structures suitable for access with LaTeX where unique identifiers are not mandatory, and what infrastructure is useful in LaTeX for evaluating such data structures. However, my knowledge of relational algebra and database management is very rusty. It will be a long time before I might come up with something that is not just a poor workaround. – Ulrich Diez Aug 28 '22 at 18:13

2 Answers2

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.

wipet
  • 74,238
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

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