2

I want to loop over section in the following JSON and print name the number of times it appears under section. How could I do this?

{
  "ID": "23",
  "section": [
      {
          "name": "A",
          "text": "This is the start of section",
          "subsection": [
            {
             name: "Sub section AA",
             text: " This is some text"
            },
            {
             name: "Sub section BB",
             text: " This is some text"
            }
          ]
      },
      {
          "name": "B", 
          "text": "This is the start of the section",
          "subsection": [
             {
               name: "Sub section EE",
               text: " This is some text"
             },
             {
               name: "Sub section DD",
               text: " This is some text"
            }
           ]
      }
  ]
}

Here is how I am reading and parsing the JSON:

\documentclass{report}
\usepackage{luacode}
% load json file
\begin{luacode}
    local json = require("json")
    local file = io.open("sample.json")
    tab = json.parse(file:read("*all"))
    file:close()
\end{luacode}

\begin{document}

The ID of the document is \directlua{tex.print(tab['ID'])}

Here is the list of all the sections:

\end{document}

json.lua file that parses the JSON (Taken from answer by Henri Menke):

    local lpeg = assert(require("lpeg"))
local C, Cf, Cg, Ct, P, R, S, V =
    lpeg.C, lpeg.Cf, lpeg.Cg, lpeg.Ct, lpeg.P, lpeg.R, lpeg.S, lpeg.V

-- number parsing
local digit    = R"09"
local dot      = P"."
local eE       = S"eE"
local sign     = S"+-"^-1
local mantissa = digit^1 * dot * digit^0 + dot * digit^1 + digit^1
local exponent = (eE * sign * digit^1)^-1
local real     = sign * mantissa * exponent / tonumber

-- optional whitespace
local ws = S" \t\n\r"^0

-- match a literal string surrounded by whitespace
local lit = function(str)
    return ws * P(str) * ws
end

-- match a literal string and synthesize an attribute
local attr = function(str,attr)
    return ws * P(str) / function() return attr end * ws
end

-- JSON grammar
local json = P{
    "object",

    value =
        V"null_value" +
        V"bool_value" +
        V"string_value" +
        V"real_value" +
        V"array" +
        V"object",

    null_value =
        attr("null", nil),

    bool_value =
        attr("true", true) + attr("false", false),

    string_value =
        ws * P'"' * C((P'\\"' + 1 - P'"')^0) * P'"' * ws,

    real_value =
        ws * real * ws,

    array =
        lit"[" * Ct((V"value" * lit","^-1)^0) * lit"]",

    member_pair =
        Cg(V"string_value" * lit":" * V"value") * lit","^-1,

    object =
        lit"{" * Cf(Ct"" * V"member_pair"^0, rawset) * lit"}"
}

return { parse = function(str) return assert(json:match(str)) end }

Update:

I am trying to add dynamic sections / sub-sections using the for loop. For example,

\section{ A }
  This is the A section
   \addcontentsline{toc}{section}{ Some Sub Section }
   \addcontentsline{toc}{section}{Another Sub Section}

\section{ B }
  This is the B section
\section{ C }
  This is the C section

Like sections and sub-sections, I will also dynamically generate tables from the JSON read.

  • 1
    you presumably just get a Lua table from the parse so the question is a pure Lua question about how to iterate over a table in Lua, that is not clearly on topic for this site – David Carlisle May 6 at 12:09
  • @DavidCarlisle Updated. – Amanda May 6 at 12:12
  • @DavidCarlisle So there is no latex way to loop over the tab? – Amanda May 6 at 12:25
  • tex macros can not see Lua data at all, you need to copy the data from Lua to tex if you want to access it. – David Carlisle May 6 at 12:33
  • the question is far less clear after the edit, please make the input json match the required output. where do you expect the text and subsections to come from? You can only iterate over the lua table if you know its structure and that depends on the structure of the json that you have not shown. – David Carlisle May 6 at 13:07
6

You can just iterate over the returned Lua table:

enter image description here

\documentclass{report}
\usepackage{luacode}
% load json file
\begin{luacode}
    local json = require("json")
    local file = io.open("sample.json")
    tab = json.parse(file:read("*all"))
    file:close()
\end{luacode}

\begin{document}

The ID of the document is \directlua{tex.print(tab['ID'])}

Here is the list of all the sections:
\directlua{
for i,k in ipairs(tab["section"]) do
 if (i>1) then
     tex.sprint (", ")
 end
     tex.sprint (k.name)
end
}
\end{document}

enter image description here

The updated json was invalid, I assume this was intended:

{
    "ID": "23",
    "section": [
    {
            "name": "A",
            "text": "This is the start of section",
            "subsection": [
        {
            "name": "Sub section AA",
            "text": " This is some text"
        },
        {
            "name": "Sub section BB",
            "text": " This is some text"
        }
            ]
    },
      {
          "name": "B", 
          "text": "This is the start of the section",
          "subsection": [
              {
          "name": "Sub section EE",
          "text": " This is some text"
              },
              {
          "name": "Sub section DD",
                  "text": " This is some text"
              }
          ]
      }
    ]
}

So just change the Lua iterations to

\documentclass{report}
\usepackage{luacode}
% load json file
\begin{luacode}
    local json = require("json")
    local file = io.open("sample.json")
    tab = json.parse(file:read("*all"))
    file:close()
\end{luacode}

\begin{document}

The ID of the document is \directlua{tex.print(tab['ID'])}

\chapter{zzz}

\directlua{
for i,k in ipairs(tab["section"]) do
     tex.print ("\string\\section{" .. k.name .. "}")
     tex.print (k.text)
  for ii,kk in ipairs(k["subsection"]) do
     tex.print ("\string\\subsection{" .. kk.name .. "}")
     tex.print (kk.text)
end
end
}
\end{document}

A more complete version would need to escape any tex-special characters and guard against empty fields.

  • I am trying to use \section command as \section{tex.sprint (k.name)} but looks like it does not work. – Amanda May 6 at 12:51
  • 1
    you could use tex.print(\string\\section{k.name} instead of tex.sprint (k.name) in the above loop, but a list of \section commands with no actual text does not produce good results as no page breaks are allowed @Amanda – David Carlisle May 6 at 13:04
  • 1
    @Amanda .. is Lua string concatenation – David Carlisle May 6 at 15:34
  • 1
    @Amanda when I started to use tex, 15 minutes per page was quite common, so I still struggle to think of 32 seconds being a long time, impossible to say in general how to speed things up and whether the time is spent reading the json and building the lua table or in traversing the lua table or (most likely) in the tex rendering the generated markup. – David Carlisle May 7 at 15:31
  • 1
    @Amanda 32 seconds for 2600 pages sound pretty good. Anyway, you can profile whether parsing JSON is the bottleneck using the socket library which also comes bundled with LuaTeX (example code). For the small JSON from the question I get Time passed parsing JSON: 4.1961669921875e-05 s in the log file. – Henri Menke May 8 at 6:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.