7

Given this code that barely parse XML and print it as SXML (s-exp XML, aka lisp-style XML, aka XML-with-parenthesis):

\catcode`@=11
\let\@xp\expandafter
\def\@makeletter#1#2\@nil{\catcode`#1 \ifx\relax#2\relax\else\@makeletter#2\@nil\fi}
\def\@gobble#1 {}
\def\@nil{}
\catcode`<13
\catcode`>12
\def\@split #1#2 #3\@nil{#1{#2} \ifx\relax#3\relax\else\@split#1#3\@nil\fi}
\def\defelt#1{\def<#1##1>##2</#1>{ (#1 {\ifx\relax##1\relax\else \@xp\@split\@xp\@key\@gobble ##1 \@nil\fi} ##2) }}
\defelt{div}
\defelt{span}

\catcode`?6
\catcode`#11
\def\@key?1=?2{ {\tt #:?1} ?2}
\def\@key?1{ {\tt #:?1}}
\catcode`^^J=10 \catcode`?=11 \catcode`^=11 \catcode`_=11 \catcode`~=11 \catcode`{=11 \catcode`}=11 %\catcode`\=11
%\@makeletter?^_~{}%\@nil

<span>first<span foo bar=yes>second</span>three</span>

\eject
\end

The first problem is redefining \ catcode doesn’t work since after that \<string> still gives the “undefined control sequence” error, the second (less important) is I can’t write a loop to redefine repeatedly catcodes, and the third, most important:

Why doesn’t TeX support macro overloading? I mean like you can see I did with \xattr, but, even worst, the \defelt which redefines “<” with several stuff in between its arguments. The problem is either I find a way of making the “<” control sequence emulate all xml tags, either I make “<” catcode 0 and then that means I have to give access to aaaall TeX control sequences from within the XML I want to parse (which is ugly): how can I do this then?

Is there another solution than defining alone the < command then recursively read the text that come after without benefiting the TeX parsing capabilities?

  • 2
    Have you looked at xmltex? – jon Jun 11 '16 at 5:01
  • Oh, didn’t knew about it… I’m looking… yet source code isn’t enough readable to me to quickly find relevant parts/an answer there ^^" – galex-713 Jun 11 '16 at 5:08
  • 1
    ConTeXt has extensive features for parsing XML. See the ConTeXt XML manual. Also see the XML section of ConTeXt wiki. – Aditya Jun 11 '16 at 5:12
  • 1
    @JosephWright The older MkII version of the XML parser is a pure TeX solution. – Wolfgang Schuster Jun 11 '16 at 12:32
  • 1
    @JosephWright: I was not sure if the OP's objective is to write a parser (as an academic exercise), or process XML with TeX. In the latter case, using luatex is a viable option. From the comment, I see that it was the former. In that case, as Metafox said, the old MkIi code is still relevant. – Aditya Jun 11 '16 at 15:17
7

Suppose following input example:

<?xml version="1.0" encoding="utf8"?>
<cenik>
  <nazev>Počítačové komponenty</nazev>
  <platnost od="1.1.2000" do="31.3.2000"/>
  <dodavatel>
    <nazev>První hardwarová, s.r.o.</nazev>
    <adresa>
      <ulice>Průmyslová 12</ulice>
      <mesto>Praha 10</mesto>
      <psc>100 000</psc>
      <email>info@prhv.cz</email>
   </adresa>
  </dodavatel> 
  <nabidka>
    <produkt kategorie="polohovací zařízení" kod="pxbd-21">
      <nazev>Hyperoptická <em>digitální</em> myš</nazev>
      <cena mena="CZK">368.30</cena>
    </produkt>
    <produkt kategorie="pevné disky" kod="sbhd-99">
      <nazev>Soft-slow disc &lt; 19,3 GB</nazev>
      <cena mena="CZK">8500</cena>
    </produkt>
    <produkt kategorie="polohovací zařízení" kod="pxbd-13">
      <nazev>Tlakový tablet</nazev>
      <cena mena="CZK">5635.20</cena>
    </produkt>
  </nabidka>
</cenik>

Then try to process it by my macro \xmlprep {input} {output} using pdftex thisfile command for example.

\newwrite\xmloutfile
\def\xmlprep#1#2{% #1=input file, #2=output file
   \ifx\relax#2\relax \chardef\xmloutfile=16 \else
   \immediate\openout\xmloutfile=#2 \fi
   \begingroup \everypar={\setbox0=\lastbox\par \xscan}\input#1 \endgroup
   \immediate\closeout\xmloutfile
}
\long\def\xscan#1<{\ifx\xscan#1\xscan \else
   \toks0={#1}\xprint{\the\toks0\npercent}\fi\xtag}

\def\nob#1{}\edef\nob{\expandafter\nob\string\{}
\def\ncb#1{}\edef\ncb{\expandafter\ncb\string\}}
\def\npercent#1{}\edef\npercent{\expandafter\npercent\string\%}

\def\xprint#1{\immediate\write\xmloutfile{\xindent#1}}
\def\xindent{}

\def\xtag#1#2>{\ifx#1?\xtagD#2>\else\ifx#1/\xtagC#2>\else\xtagE#1#2>/>\end\fi\fi}
\def\xtagE#1/>#2\end{\ifx>#2>\let\tmp=n\xtagA#1 \end\else \let\tmp=/\xtagA#1> \end\fi}
\def\xtagA#1 #2\end{\def\currargs{}\ifx>#2>\xtagB#1\else \xargsB#2\xtagB#1>\fi}
\def\xtagB#1>{\bgroup\def\currtag{#1}%
   \ifx\tmp/\xprint{\string\XML#1{\currargs}{}}\egroup\else
   \xprint{\string\XML#1{\currargs}\nob\npercent}%
   \edef\xindent{\xindent\space\space}\fi}
\def\xtagD#1?>{\xprint{\string\META{#1}}}
\def\xargsB#1>{\def\currargs{#1}}
\def\xtagC#1>{\def\tmp{#1}\ifx\tmp\currtag\else
   \message{WARNING: <\currtag>...</#1> doesn't match}\fi
   \egroup\xprint{\ncb}%
}

\xmlprep {test.xml} {test.out}

\end

You get the following output test.out

\META{xml version="1.0" encoding="utf8"}
\XMLcenik{}{%
  \XMLnazev{}{%
    Počítačové komponenty%
  }
  \XMLplatnost{od="1.1.2000" do="31.3.2000"}{}
  \XMLdodavatel{}{%
    \XMLnazev{}{%
      První hardwarová, s.r.o.%
    }
    \XMLadresa{}{%
      \XMLulice{}{%
        Průmyslová 12%
      }
      \XMLmesto{}{%
        Praha 10%
      }
      \XMLpsc{}{%
        100 000%
      }
      \XMLemail{}{%
        info@prhv.cz%
      }
    }
  }
  \XMLnabidka{}{%
    \XMLprodukt{kategorie="polohovací zařízení" kod="pxbd-21"}{%
      \XMLnazev{}{%
        Hyperoptická %
        \XMLem{}{%
          digitální%
        }
        myš%
      }
      \XMLcena{mena="CZK"}{%
        368.30%
      }
    }
    \XMLprodukt{kategorie="pevné disky" kod="sbhd-99"}{%
      \XMLnazev{}{%
        Soft-slow disc &lt; 19,3 GB%
      }
      \XMLcena{mena="CZK"}{%
        8500%
      }
    }
    \XMLprodukt{kategorie="polohovací zařízení" kod="pxbd-13"}{%
      \XMLnazev{}{%
        Tlakový tablet%
      }
      \XMLcena{mena="CZK"}{%
        5635.20%
      }
    }
  }
}

This is TeX-friendly format. You can simply define used tags \XMLcenik, \XMLnazev etc. macros with two parameters (arguments and body-text). Then you can process it by TeX. If these macros are prepared then you can process XML file in one TeX run:

\xmlprep {test.xml} {text.out}
\input test.out

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.