3

ConTeXt provides a way of managing XML files. The reason I approached this functionality is due to the need of being able to publish xml data in PDF to obtain catalogs or other technical documentations.

The ConTeXt documentation refer to a document describing the available commands.

The examples below refer to a simple xml file tken from an example in the documentation above:

<?xml version="1.0" encoding="UTF-8"?>
<document>
  <section>
    <title>Some title</title>
    <content>
      <p>a paragraph of text</p>
      <p>another paragraph of text</p>
    </content>
  </section>
  <section>
    <title>Some title2</title>
    <content>
      <p>a paragraph of text2</p>
      <p>another paragraph of text2</p>
    </content>
  </section>
</document>

1

The easiest way is to rely on the available commands to explore the xml tree as shown in the code below.

\startxmlsetups xml:demo:base
  \xmlsetsetup{#1}{*}{-}
  \xmlsetsetup{#1}{document|section|content|p}{xml:demo:*}
\stopxmlsetups

\xmlregisterdocumentsetup{demo}{xml:demo:base}

\startxmlsetups xml:demo:document
  \xmlflush{#1}
\stopxmlsetups

\startxmlsetups xml:demo:section
  \xmlflush{#1}
\stopxmlsetups

\startxmlsetups xml:demo:content
  \xmlflush{#1} \\
\stopxmlsetups

\startxmlsetups xml:demo:p
  \xmlflush{#1} \\
\stopxmlsetups

\starttext
  \xmlprocessfile{demo}{demo.xml}{}
\stoptext

The \xmlsetup command provides instruction to the parser to call specific code when a node of type document, section, p is encountered. If nothing has to be done, the command \xmlflush{#1} returns the starting point to the parser to go on with the exploration. When the parser arrives at a leaf of the tree, then the command \xmlflush{#1} returns the content of that node. Running the code we obtain:

a paragraph of text
another paragraph of text
a paragraph of text2
another paragraph of text2

2

In some cases we could be interested in retrieving only a subset of the node <p> but, without any attribute, it is not possible to do it. A possible solution come from the mechanism used for parsing, i.e., associating an id to the visited nodes.

\startxmlsetups xml:demo:base
  \xmlsetsetup{#1}{*}{-}
  \xmlsetsetup{#1}{document|section|content|p}{xml:demo:*}
\stopxmlsetups

\xmlregisterdocumentsetup{demo}{xml:demo:base}

\startxmlsetups xml:demo:document
  \xmlflush{#1}
\stopxmlsetups

\startxmlsetups xml:demo:section
  SECTION\\
  \xmlflush{#1}
  \\
\stopxmlsetups

\startxmlsetups xml:demo:content
  level 2 -> \xmlflush{demo::2} \\
  level 5 -> \xmlflush{demo::5} \\
  level 6 -> \xmlflush{demo::6} \\
  level 7 -> \xmlflush{demo::7} \\
  level 8 -> \xmlflush{demo::8} \\
\stopxmlsetups

\starttext
  \xmlprocessfile{demo}{demo.xml}{}
\stoptext

What we obtain is:

SECTION
level 2 -> xml version="1.0" encoding="UTF-8” 
level 5 -> Some title
level 6 ->
level 7 -> a paragraph of text
level 8 -> another paragraph of text

SECTION
level 2 -> xml version="1.0" encoding="UTF-8” 
level 5 -> Some title
level 6 ->
level 7 -> a paragraph of text
level 8 -> another paragraph of text

Notice that, using demo::3 and demo::4 returns an error. Clearly these ids refer to the nodes <document> and <section>, not containing any text. Nevertheless also the node <content> does not contain any text but using demo::6 returns null string, but not an error. Why is this happening?

Another thing is that the ids are absolute in relation to the whole tree. If we want to get the content of the second <section> node, we must use the following ids. As an example demo::10 returns “Some title2”:

3

The documentation suggests a different approach to work with relative referencing, i.e., using the command \xmlfirst. The code is:

\startxmlsetups xml:demo:section
  SECTION\\
  \xmlfirst{#1}{/title}
  \xmlfirst{#1}{/content}
  \\
\stopxmlsetups

But the output the output is:

SECTION

SECTION

I am using OS X 10.12.5 and ConTeXt is provided through the TeX distribution. The same problem has been described here, but with no solution.

Do you have any suggestion?

Even modifying the approach as below, no result is obtained, even inserting the \xmlflush{#1} command in the xml:demo:section to go on with the search. Symptom that probably the parser got stuck.

\startxmlsetups xml:demo:section
  SECTION\\
  \xmlfirst{#1}{/title}
  \\
\stopxmlsetups

\startxmlsetups xml:demo:content
  \xmlflush{#1}
\stopxmlsetups

4

The solution I am using now takes advantage of the \xmltext command:

\startxmlsetups xml:demo:section
  SECTION\\
  \xmltext{#1}{./title}
  \\
\stopxmlsetups

Providing the output

SECTION 
Some title 
SECTION 
Some title2

5

A similar solution leverages on Lua.

\registerctxluafile{luafunction}{}

\startxmlsetups xml:demo:base
  \xmlsetsetup{#1}{*}{-}
  \xmlsetsetup{#1}{document|section|content|p}{xml:demo:*}
\stopxmlsetups

\xmlregisterdocumentsetup{demo}{xml:demo:base}

\startxmlsetups xml:demo:document
  \xmlflush{#1}
  \stopxmlsetups

\startxmlsetups xml:demo:section
  SECTION\\
  \xmlfunction{#1}{section}
  \\
\stopxmlsetups

\starttext
  \xmlprocessfile{demo}{demo.xml}{}
\stoptext

And defining the proper Lua function in the luafunction.lua file:

function xml.functions.section(t)
  context(xml.text(t,"./title"))
end

Any other suggestion or different approach?

  • What is the actual question here? If it is »Any other suggestion or different approach?« then I'm afraid this question has to be closed as opinion-based. Perhaps you could boil the question down to one specific problem. – Henri Menke Jul 16 '17 at 23:59
  • basically '\xmlfirst' is not working – Michele Jul 17 '17 at 0:03
  • 1
3

For \xmlfirst to “work” (it does, you're just using it wrong), remove the line \xmlsetsetup{#1}{*}{-}.

From the documentation (which you linked in your question):

The line:

\xmlsetsetup{demo}{*}{-}

sets the default for each element to ‘just ignore it’. A + would make the default to always flush the content.

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.