我正在用 JSON 在一个小型文本文件中编写我的食谱(食物)。有没有办法在 LaTeX 中解析它?我知道有很多处理食谱的模板,但我想有自己的风格,可以快速改变,例如我妈妈对卡路里之类的东西不感兴趣 :)
以下是一个简单的例子:
{
"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"}
]
}
}
答案1
虽然问题是关于在 LaTeX 中解析 JSON,但由于 OP 想要“有自己的风格,而且变化很快”,我将提供一个 ConTeXt 解决方案,以简化操作。
ConTeXt 已经提供了 JSON 解析器。要使用它,只需加载
\usemodule[json]
然后,您可以使用 Lua 函数utilities.json.tolua
将 JSON 字符串转换为 Lua 表,并使用 Lua 函数utilities.json.tostring
将 Lua 表转换为 JSON 字符串。
使用以下方式排版 Lua 表非常简单:ConTeXt Lua 文档.以下是完整示例:
\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
现在您可以简单地定义一个 TeX 宏来将其参数传递给 Lua 函数。
% Note that I use the braces around #1 to make the input
% syntax slightly simpler
\define[1]\Recipe
{\ctxlua{userdata.show_recipe([==[{#1}]==])}}
让我们添加一些最小样式来格式化节头。与所有 ConTeXt 文档一样,您可以使用适当的命令更改格式\setup...
。
\setuphead[subject][style=\bfb]
\setuphead[subsubject][style=\bfa]
最后,主要文件
\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
这使
答案2
尝试这个:
\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}
它应该给你这个:
格式化是使用检查方法完成的,该方法使用一个\mbox{~~}
来隔开括号。
答案3
和往常一样,我错过了大卫的评论,所以我的想法并非 100% 原创。:)
正如大卫在聊天室中所说,他潜意识地给了我一个暗示,这证明他的力量超出了理解范围,而鸭子通常并不以理解能力而闻名。:)
注意力:
lualibs
虽然我的答案有效,但请青睐 Yiannis 的答案,因为它使用了LuaTeX 中可用的标准模块。
我将使用 LuaTeX 提供一个幼稚的答案,只是为了让事情尽快发挥作用。:)
首先要做的事情:得到这个用纯 Lua 编写的简单 JSON 编码器/解码器来自 Jeffrey Friedl 的网站。该文件是独立的,名为JSON.lua
。保存到您的工作目录。
Jeffrey 的代码是根据 Creative Commons Attribution 3.0 Unported License 发布的。
在同一级别,将 JSON 文件保存为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"}
]
}
}
最后,但并非最不重要的是我们的 TeX 文件(例如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}
使用以下方式运行文件
$ lualatex recipes.tex
结果:
享受!:)
答案4
以下是与 TeX Live 2020 (LuaHBTeX 1.12.0) 兼容的最新示例。第一个示例受到 Paulo cerreda 的回答的启发,但不依赖于外部工具。如果test.json
您的 tex 文档旁边有一个包含 OP 配方的文件,那么
% !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}
将排版为“First recipe”。请注意,这tab
是一个全局 Lua 变量,稍后可以访问。
第二个例子来自 Yiannis Lazarides 的回答,它已被修复和简化以帮助 Lua 初学者,但给出相同的输出。
% !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}
注意f.close()
: Yiannis 的回答有错误。