3

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.

Amanda
  • 387
  • 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 06 '19 at 12:09
  • @DavidCarlisle Updated. – Amanda May 06 '19 at 12:12
  • @DavidCarlisle So there is no latex way to loop over the tab? – Amanda May 06 '19 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 06 '19 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 06 '19 at 13:07
  • @DavidCarlisle Have updated the subsection part. The idea is that the JSON and latex here could be much more vast and complex. So would need a way, where I could see a generalisation, so adding more latex commands using the JSON does not appear to be a uphill task. – Amanda May 06 '19 at 13:10
  • so there is no text in the json? all sections just have te stub text "this is section ...` ? – David Carlisle May 06 '19 at 13:10
  • @DavidCarlisle Updated yet again – Amanda May 06 '19 at 13:15

1 Answers1

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.

David Carlisle
  • 757,742
  • I am trying to use \section command as \section{tex.sprint (k.name)} but looks like it does not work. – Amanda May 06 '19 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 06 '19 at 13:04
  • Have also updated my question – Amanda May 06 '19 at 13:04
  • @Amanda I updated answer (last update:-) – David Carlisle May 06 '19 at 13:46
  • So we are using lua to render latex. What purpose do the 2 dots .. before and after json object serve? – Amanda May 06 '19 at 15:22
  • 1
    @Amanda .. is Lua string concatenation – David Carlisle May 06 '19 at 15:34
  • Use ipairs instead of pairs. The latter does not guarantee ordering for numerical indices. – Henri Menke May 07 '19 at 10:22
  • Not related to this question but wanted to have your views. The real JSON is huge. It took around 32 seconds to generate a PDF of 2600 pages, when iterating over multiple points using lua. Do you think, there could be a better way to go from here in terms of time? – Amanda May 07 '19 at 15:17
  • 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 07 '19 at 15:31
  • @HenriMenke ipairs done thanks (I knew that really:-) – David Carlisle May 07 '19 at 15:33
  • @DavidCarlisle Okay. Would you have any idea on how to save all the latex to a file which is ready to be rendered? I tried, appending all the latex to variable and saved that variable into the file. But when the file is read, it throws some undefined controlled sequence error – Amanda May 07 '19 at 16:13
  • sorry already too many comments here, ask a new question with a new example if you need further help – David Carlisle May 07 '19 at 16:25
  • 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 08 '19 at 06:16
  • @DavidCarlisle Have asked a separate question : https://tex.stackexchange.com/questions/489722/saving-all-the-latex-to-a-file-and-then-render-reading-from-the-file?noredirect=1#comment1236607_489722 – Amanda May 08 '19 at 06:23
  • @HenriMenke For a 7MB JSON, I get 0.223 seconds. Time spent parsing is low and time spent parsing each section using lua is around 0.00043487548828125 s. Why does it then take 32 seconds? – Amanda May 08 '19 at 06:41
  • @Amanda typesetting 2600 pages may take a few seconds anyway just starting luatex takes longer than your lua parse times, so the time is unrelated to any code you have shown, it all depends on how complicated the tex is, how big your fonts are (if you load some massive CJK font set it takes luatex a while to sort it out) whether your file searches are hashed using ls-r ..... – David Carlisle May 08 '19 at 06:53
  • @DavidCarlisle Is there a way I could know the starting time of lualatex? I would be catering to multiple document generation requests, so wanted to understand this. – Amanda May 08 '19 at 08:09
  • just time lualatex small2e to see how long it takes to typeset a trivial 1 page document (pdflatex sample2e is likely significantly quicker) – David Carlisle May 08 '19 at 08:22