18

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"}
    ]
}
}
  • 1
    Can you make a small example? – egreg Jan 11 '15 at 11:26
  • 2
    Are you aware of tex.blogoverflow.com/2012/10/… ? – DG' Jan 11 '15 at 11:35
  • I'd added one example. – user3417078 Jan 11 '15 at 21:09
  • 1
    In order to help the readers, it would be useful to know if all recipes must have entries for all these keywords, or if some may be missing from some recipes. – Steven B. Segletes Jan 11 '15 at 21:24
  • 1
    @StevenB.Segletes JSON has an exceptionally simple grammar, see the railroad diagrams at json.org – David Carlisle Jan 11 '15 at 21:31
19

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

enter image description here

18

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:

enter image description here

The formatting is done using an inspect method that uses an \mbox{~~} to space the brackets out.

  • Does the lualibs module load the same JSON parser that is included as util-jsn.lua in the ConTeXt distribution? – Aditya Jan 12 '15 at 1:53
  • 1
    @Aditya It is very simialr github.com/phi-gamma/lualibs. They are based on the ConTeXt ones. – Yiannis Lazarides Jan 12 '15 at 4:39
  • @Aditya It’s the same code, just not updated as frequently. – Philipp Gesang Jan 15 '15 at 6:21
16

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. :)

Warning 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:

Recipe

Enjoy! :)

  • 2
    There is a json parser avaialable with "lualibs-util-jsn.lua", just require it:) – Yiannis Lazarides Jan 11 '15 at 21:57
  • @YiannisLazarides: it is? Oh my, I'm a silly duck! :) Let me try here and I will update the answer. Thanks, Yiannis! :) – Paulo Cereda Jan 11 '15 at 21:59
  • @Yiannis: You were absolutely right, there is a JSON parser in LuaLibs! :) Sadly, I cannot load it (according to the documentation, it's in the "extended set"), even after trying a couple of settings. Do you know how we could use it? – Paulo Cereda Jan 11 '15 at 22:10
  • I will post a minimal – Yiannis Lazarides Jan 11 '15 at 22:42
  • @PauloCereda This works on my home machine (windows), with the latest Lua distribution. I am curious to see if it works on Linux. – Yiannis Lazarides Jan 11 '15 at 23:01

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.