我如何才能自动从 XML 文件中解析数据?

我如何才能自动从 XML 文件中解析数据?

我有一个 XML 文档,其中包含一些必须用来创建 LaTeX 文档的元素。有没有办法自动获取每个元素并针对每个元素执行一些 LaTeX 命令?

为了更清楚一点,假设我有如下存储的文章:

<article title="Article title" author='The Author'>
    <section title="Section title">
        <body>
            Lorem ipsum dolor sit amet,
            consectetur adipiscing elit.
        </body>
    </section>
    <image source="image/path/foo.png" />
    <section title=”Another section title">
        <body>
            Nam dui ligula, fringilia a,
            euismod sodales, sollicitudin
            vel, wisi.
        </body>
    </section>
</article>

我想要创建一个 LaTeX 文档,该文档在编译时会自动创建与我为每篇文章手动执行以下操作时相同的输出:

\chapter{Article title}
by \emph{The Author}

\section{Section title}
Lorem ipsum dolor sit amet,
consectetur adipiscing elit.

\includegraphics[width=\textwidth]{image/path/foo.png}

\section{Another section title}
Nam dui ligula, fringilia a,
euismod sodales, sollicitudin
vel, wisi.

我认为像csvsimplepackage 这样的工具可以完成这项工作,但我找不到它。不过,目前我只有 XML 文档,所以我可以尝试所有可能的解决方案,而不会丢失任何已完成的工作。

答案1

您可以使用我的 LuaXML 包,顾名思义,它可以使用 LuaTeX 处理 XML 文档。我将使用我关于Mathml 到 LaTeX处理。这是图书馆,transform-xml.lua

-- adapted code from https://github.com/michal-h21/luaxml-mathml
--
local domobject = require "luaxml-domobject"

-- we need to define different actions for XML elements. The default action is
-- to just process child elements and return the result
local function default_action(element)
  return process_children(element)
end

-- use template string to place the processed children
local function simple_content(s)
  return function(element)
    local content = process_children(element)
    -- process attrubutes
    -- attribute should be marked as @{name}
    local expanded = s:gsub("@{(.-)}", function(name)
      return element:get_attribute(name) or ""
    end)
    return string.format(expanded, content)
  end
end

local function get_child_element(element, count)
  -- return specified child element 
  local i = 0
  for _, el in ipairs(element:get_children()) do
    -- count elements 
    if el:is_element() then
      -- return the desired numbered element
      i = i + 1
      if i == count then return el end
    end
  end
end

-- actions for particular elements
local actions = {
  
}

-- add more complicated action
local function add_custom_action(name, fn)
  actions[name] = fn
end

-- normal actions
local function add_action(name, template)
  actions[name] = simple_content(template)
end

-- convert Unicode characters to TeX sequences
local unicodes = {
  [35] = "\\#",
  [38] = "\\&",
  [60] = "\\textless{}",
  [62] = "\\textgreater{}",
  [92] = "\\textbackslash{}",
  [123] = "\\{",
  [125] = "\\}"
}

local function process_text(text)
  local t = {}
  -- process all Unicode characters and find if they should be replaced
  for _, char in utf8.codes(text) do
    -- construct new string with replacements or original char
    t[#t+1] = unicodes[char] or utf8.char(char)
  end
  return table.concat(t)
end

function process_children(element)
  -- accumulate text from children elements
  local t = {}
  -- sometimes we may get text node
  if type(element) ~= "table" then return element end
  for i, elem in ipairs(element:get_children()) do
    if elem:is_text() then
      -- concat text
      t[#t+1] = process_text(elem:get_text())
    elseif elem:is_element() then
      -- recursivelly process child elements
      t[#t+1] = process_tree(elem)
    end
  end
  return table.concat(t)
end


function process_tree(element)
  -- find specific action for the element, or use the default action
  local element_name = element:get_element_name()
  local action = actions[element_name] or default_action
  return action(element)
end

function parse_xml(content)
  -- parse XML string and process it
  local dom = domobject.parse(content)
  -- start processing of DOM from the root element
  -- return string with TeX content
  return process_tree(dom:root_node())
end

local function load_file(filename)
  local f = io.open(filename, "r")
  local content = f:read("*all")
  f:close()
  return parse_xml(content)
end


function print_tex(content)
  -- we need to replace "\n" characters with calls to tex.sprint
  for s in content:gmatch("([^\n]*)") do
    tex.sprint(s)
  end
end


local M = {
  parse_xml = parse_xml,
  process_children = process_children,
  print_tex = print_tex,
  add_action = add_action,
  add_custom_action = add_custom_action,
  simple_content = simple_content,
  load_file = load_file
}

return M

我不会详细描述它,因为它基本上与来自我的另一个答案,只是更加通用。

它提供的主要功能是transform_xml.add_action。它需要两个参数,第一个是元素的名称,第二个是将插入到文档中的 TeX 模板。

该模板可以使用文本从所有子元素插入%s文本。您应该在所有可以包含任何文本的元素中使用它。可以使用@{attribute name}占位符访问属性。

示例模板可能如下所示:

xmltransform.add_action("section", [[\section{@{title}}
%s
\par]])

注意我们使用[[ ... ]]来指定模板,是为了方便输入换行符和反斜杠。\par插入段落时也需要使用命令。

我们可以在自定义 TeX 包中指定必要的操作myarticle.sty

\ProvidesPackage{myarticle}
\RequirePackage{luacode}
\RequirePackage{graphicx}
\RequirePackage{xparse}


\begin{luacode*}
xmltransform = require "transform_xml.lua" 


xmltransform.add_action("article", [[
\chapter{@{title}}
by \emph{@{author}}\par

%s
]])

xmltransform.add_action("section", [[\section{@{title}}
%s
\par]])

xmltransform.add_action("image", '\\includegraphics{@{source}}')

function article_load(filename)
  local transformed = xmltransform.load_file(filename)
  if transformed then
    xmltransform.print_tex(transformed)
  else
    tex.sprint("Error in parsing of the XML file " .. filename)
  end
end

\end{luacode*}


\NewDocumentCommand\processarticle{m}{%
  \directlua{article_load("#1")}
}
 

\endinput

为所有需要的元素(文章、章节、图像)指定操作。当没有为元素指定操作时,例如<body>,它的内容将被处理并插入到 TeX 文档中。

包还声明了一个新命令\processarticle。它获取 XML 文件名并使用 对其进行处理transform_xml

我们现在可以尝试一个示例文档:

\documentclass{book}
\usepackage{myarticle}
\begin{document}
\processarticle{article.xml}
\end{document}

最终的 PDF 结果如下:

在此处输入图片描述

相关内容