TeX - LaTeX Asked by user3417078 on March 21, 2021
I’m writing my recipes (food) in a small textfile in JSON. Are there any ways to parse this in LaTeX?
I know, that are lots of templates dealing with recipes, but I want to have my own style, which can be change very fast e.g. my mother is not interested in stuff like calories 🙂
Here is one simple example:
{
"recipe": {
"title":"First recipe",
"source":"My first cookbook",
"carbs":"1 oz",
"fat":"1 oz",
"protein":"1 oz",
"cal":"100 kcal",
"ingredients": [
{"item":"Eggs"},
{"item":"Oil"},
{"item":"Nuts"}
],
"cooking": [
{"step":"Mix eggs and oil"},
{"step":"Add nuts"}
]
}
}
As always, I missed David's comment, so my idea is not 100% original. :)
As David said in the chatroom, he subliminally gave me a hint, which proves his powers are beyond comprehension, and that ducks aren't noted for their powers of comprehension normally. :)
Attention: Although my answer works, please favour Yiannis' answer since it uses a module from the standard
lualibs
available in LuaTeX.
I'll provide a naive answer using LuaTeX just to get things working as quickly as possible. :)
First things first: get this simple JSON encoder/decoder written in pure Lua from Jeffrey Friedl's site. The file is self-contained and it's named JSON.lua
. Save to your working directory.
Jeffrey's code is released under a Creative Commons Attribution 3.0 Unported License.
At the very same level, save your JSON file as, say, recipes.json
:
{
"recipe": {
"title":"First recipe",
"source":"My first cookbook",
"carbs":"1 oz",
"fat":"1 oz",
"protein":"1 oz",
"cal":"100 kcal",
"ingredients": [
{"item":"Eggs"},
{"item":"Oil"},
{"item":"Nuts"}
],
"cooking": [
{"step":"Mix eggs and oil"},
{"step":"Add nuts"}
]
}
}
At last, but not least, our TeX file (e.g, recipes.tex
):
documentclass{article}
usepackage{luacode}
begin{document}
begin{luacode}
function read(file)
local handler = io.open(file, "rb")
local content = handler:read("*all")
handler:close()
return content
end
JSON = (loadfile "JSON.lua")()
local table = JSON:decode(read("recipes.json"))
tex.print(table['recipe']['title'])
end{luacode}
end{document}
Run your file with
$ lualatex recipes.tex
The result:
Enjoy! :)
Answered by Paulo Cereda on March 21, 2021
Try this:
documentclass{scrbook}
usepackage{luacode}
begin{filecontents*}{test.json}
{
"recipe": {
"title":"First recipe",
"source":"My first cookbook",
"carbs":"1 oz",
"fat":"1 oz",
"protein":"1 oz",
"cal":"100 kcal",
"ingredients": [
{"item":"Eggs"},
{"item":"Oil"},
{"item":"Nuts"}
],
"cooking": [
{"step":"Mix eggs and oil"},
{"step":"Add nuts"}
]
}
}
end{filecontents*}
begin{document}
begin{luacode}
-- We use the lualibs built-in modules
-- this loads all the modules including a json converter
--
local M = M or {}
require("lualibs.lua")
-- @json file
function getjsonfile (file)
local f, s
f = io.open(file, 'r')
s = f:read('*a')
f.close()
return s
end
local s = utilities.json.tolua(getjsonfile('test.json'))
local rep, write = string.rep, tex.print
function M.inspect (tab, offset)
local openbracket, closebracket, par = "{", "mbox{..}}", "par"
offset = offset or ""
for k, v in pairs (tab) do
local newoffset = offset .. "mbox{~~}"
if type(v) == "table" then
write(offset .. k .. " = " .. openbracket .. par)
M.inspect(v, newoffset)
write(offset .. closebracket .. par)
else
if k~="data" then write(offset..k.." = ".. tostring(v), "par")
else
write(offset.."k = char data ")
end
end
end
end
tex.print(M.inspect(s))
end{luacode}
end{document}
It should give you this:
The formatting is done using an inspect method that uses an mbox{~~}
to space the brackets out.
Answered by Yiannis Lazarides on March 21, 2021
Although the question is about parsing JSON in LaTeX, since the OP wants to "have my own style, which can be change very fast", I'll give a ConTeXt solution for its simplicity.
ConTeXt already comes up with a parser for JSON. To use it, simply load
usemodule[json]
Then, you can use the Lua function utilities.json.tolua
to convert JSON string to Lua table and the Lua function utilities.json.tostring
to convert a Lua table to a JSON string.
It is very simple to typeset Lua tables using ConTeXt Lua Document. Here is a complete example:
usemodule[json]
startluacode
userdata = userdata or {}
local json = utilities.json
userdata.show_recipe = function(recipe)
local lua_recipe = json.tolua(recipe).recipe
local ingredients = lua_recipe.ingredients
local cooking = lua_recipe.cooking
context.subject(lua_recipe.title)
local show_value = function(value)
context.NC() context(value)
context.EQ() context(lua_recipe[value])
context.NC() context.NR()
end
context.starttabulate()
show_value("source")
show_value("carbs")
show_value("protein")
show_value("cal")
context.stoptabulate()
context.subsubject("Ingredients")
context.startitemize{"packed, intro"}
for i = 1,#ingredients do
context.startitem()
context(ingredients[i].item)
context.stopitem()
end
context.stopitemize()
context.subsubject("Cooking")
context.startitemize{"packed, intro"}
for i = 1,#cooking do
context.startitem()
context(cooking[i].step)
context.stopitem()
end
context.stopitemize()
end
stopluacode
Now you can simply define a TeX macro to pass its argument to the Lua function.
% Note that I use the braces around #1 to make the input
% syntax slightly simpler
define[1]Recipe
{ctxlua{userdata.show_recipe([==[{#1}]==])}}
Let's add some minimal styling to format the section heads. As with all ConTeXt documents, you can change the format by using appropriate setup...
command.
setuphead[subject][style=bfb]
setuphead[subsubject][style=bfa]
Finally, the main document
starttext
Recipe
{
"recipe": {
"title":"First recipe",
"source":"My first cookbook",
"carbs":"1 oz",
"fat":"1 oz",
"protein":"1 oz",
"cal":"100 kcal",
"ingredients": [
{"item":"Eggs"},
{"item":"Oil"},
{"item":"Nuts"}
],
"cooking": [
{"step":"Mix eggs and oil"},
{"step":"Add nuts"}
]
}
}
stoptext
which gives
Answered by Aditya on March 21, 2021
Here are up to date examples that do work with TeX Live 2020 (LuaHBTeX 1.12.0).
The first one is inspired by Paulo cerreda's answer but do not rely on an external tool.
If you have a test.json
file containing the OP recipe next to your tex document, then
% !TEX encoding = UTF-8
% !TEX program = Lualatex
documentclass{scrbook}
usepackage{luacode}
begin{document}
begin{luacode}
require("lualibs.lua")
local f = io.open('test.json', 'r')
local s = f:read('*a')
f:close()
tab = utilities.json.tolua(s)
end{luacode}
luadirect{tex.sprint(tab['recipe']['title'])}
end{document}
will typeset into "First recipe". Notice that tab
is a global Lua variable that can be accessed later on.
The second example comes from Yiannis Lazarides answer, it has been fixed and simplified to help Lua beginners but gives the same output.
% !TEX encoding = UTF-8
% !TEX program = Lualatex
documentclass{scrbook}
usepackage{luacode}
begin{filecontents*}{test.json}
{
"recipe": {
"title":"First recipe",
"source":"My first cookbook",
"carbs":"1 oz",
"fat":"1 oz",
"protein":"1 oz",
"cal":"100 kcal",
"ingredients": [
{"item":"Eggs"},
{"item":"Oil"},
{"item":"Nuts"}
],
"cooking": [
{"step":"Mix eggs and oil"},
{"step":"Add nuts"}
]
}
}
end{filecontents*}
begin{document}
begin{luacode}
local f = io.open('test.json', 'r')
local s = f:read('*a')
f:close()
require("lualibs.lua")
local tab = utilities.json.tolua(s)
local openbracket = "{"
local closebracket = "mbox{..}}"
local par = "par"
local prettyprint
prettyprint = function (tab, prefix)
for k, v in pairs (tab) do
local newprefix = prefix .. "mbox{~~}"
if type(v) == "table" then
tex.print(prefix .. k .. " = " .. openbracket .. par)
prettyprint(v, newprefix)
tex.print(prefix .. closebracket .. par)
elseif k == "data" then
tex.print(prefix.."k = char data ")
else
tex.print(prefix..k.." = ".. tostring(v), "par")
end
end
end
prettyprint(tab, '')
end{luacode}
end{document}
Nota Bene: there was a faulty f.close()
in Yiannis' answer.
Answered by Jérôme LAURENS on March 21, 2021
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP