summaryrefslogtreecommitdiff
path: root/macros/generic/olsak-misc/xmlparser.tex
blob: 0d540f9d057430494b84f3aef176d42bd0267bb4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
% xmlparser.tex
%%%%%%%%%%%%%%%%%%
% Petr Olsak  2016

% After \input xmlparser you can do:

% \xmlprep {domument.xml} {document.out}
% You can define all tags used in the document.xml 
% in the form \def\XMLtag#1#2{...}.  Then you can process:
% \input document.out

% The macro \xmlprep {input.xml} {output-file} converts XML document to a
% TeX-friendly format. You can define used macros and do \input output-file. 
% More information is at the end of this document

\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\npercent#1{}\edef\npercent{\expandafter\npercent\string\%} % normal %
\def\xprint#1{\immediate\write\xmloutfile{\xindent#1}}
\def\xindent{}

\def\xtag#1{\ifx#1!\expandafter\xtagH \else\fihere\xtagA#1\fi}
\def\xtagA#1#2>{\ifx#1?\xtagE#2>\else\ifx#1/\xtagG#2>\else\xtagB#1#2>/>\end\fi\fi}
\def\xtagB#1/>#2\end{\ifx>#2>\let\tmp=n\xtagC#1 \end\else \let\tmp=/\xtagC#1> \end\fi}
\def\xtagC#1 #2\end{\def\currargs{}\ifx>#2>\xtagD#1\else \xtagF#2\xtagD#1>\fi}
\def\xtagD#1>{\bgroup\def\currtag{#1}%
   \ifx\tmp/\xprint{\string\XML#1\space{\currargs}{}}\egroup\else
   \xprint{\string\XML#1\space{\currargs}\iftrue\string{\else}\fi\npercent}%
   \edef\xindent{\xindent\space\space}\fi
}
\def\xtagE#1?>{\xprint{\string\META\space{#1}}}
\def\xtagF#1>{\def\currargs{#1}}
\def\xtagG#1>{\def\tmp{#1}\ifx\tmp\currtag\else
   \message{WARNING: <\currtag>...</#1> doesn't match}\fi
   \egroup\xprint{\iffalse{\else\string}\fi\npercent}%
}
\def\xtagH#1{\ifx#1-\expandafter\xtagI \else \fihere\xtagJ#1\fi}
\def\xtagI#1-->{} % comment in the format <!-- ... -->
\def\xtagJ#1 #2>{\xprint{\string\SPEC#1\space{#2}}}
\def\fihere#1\fi{\fi#1}

\def\xarg#1{\xargA#1 ==}
\def\xargA#1#2={\def\xargN{#1#2}\ifx#1=\else\expandafter\xargB\fi}
\def\xargB#1{\ifx#1"\expandafter\xargC\else\fihere\xargE#1\fi}
\def\xargC#1"{\xargD{#1}}
\def\xargD#1{\expandafter\def\csname ARG\xargN\endcsname{#1}\xargA}
\def\xargE#1{\ifx#1'\expandafter\xargF\else\fihere\xargG#1\fi}
\def\xargF#1'{\xargD{#1}}
\def\xargG#1 {\xargD{#1}}

\def\META#1{}  \def\SPECDOCTYPE#1{}

\def\entity#1;{\csname ent:#1\endcsname}
\def\declentity#1#2{\expandafter\def\csname ent:#1\endcsname{#2}}

\endinput

--------------------------------------------------------------------------

Documentation
=============

Introduction example. Suppose the test.xml file:

--------------------

<?xml version="1.0" encoding="utf8"?>
<pricelist>
  <!-- This is price list of a virtual firm -->
  <name>Computer components</name>
  <validity from="1.1.2000" to="31.3.2000"/>
  <firm>
    <name>První hardwarová, s.r.o.</name>
    <address>
      <street>Průmyslová 12</street>
      <city>Praha 10</city>
      <postalcode>100 000</postalcode>
      <email>info@prhv.cz</email>
    </address>
  </firm> 
  <offer>
    <product category="polohovací zařízení" code="pxbd-21">
      <name>Hyperoptická <em>digitální</em> myš</name>
      <price currency="CZK">368.30</price>
    </product>
    <product category="pevné disky" code="sbhd-99">
      <name>Soft-slow disc &lt; 19,3 GB</name>
      <price currency="CZK">8500</price>
    </product>
    <product category="polohovací zařízení" code="pxbd-13">
      <name>Special touchpad</name>
      <price currency="CZK">5635.20</price>
    </product>
  </offer>
</pricelist>

--------------------

When you process it by \xmlprep {test.xml} {test.out} you get:

--------------------

\META {xml version="1.0" encoding="utf8"}
\XMLpricelist {}{%
  \XMLname {}{%
    Computer components%
  }%
  \XMLvalidity {from="1.1.2000" to="31.3.2000"}{}
  \XMLfirm {}{%
    \XMLname {}{%
      První hardwarová, s.r.o.%
    }%
    \XMLadddress {}{%
      \XMLstreet {}{%
        Průmyslová 12%
      }%
      \XMLcity {}{%
        Praha 10%
      }%
      \XMLpostalcode {}{%
        100 000%
      }%
      \XMLemail {}{%
        info@prhv.cz%
      }%
    }%
  }%
  \XMLoffer {}{%
    \XMLproduct{category="polohovací zařízení" code="pxbd-21"}{%
      \XMLname {}{%
        Hyperoptická %
        \XMLem {}{%
          digitální%
        }%
        myš%
      }%
      \XMLprice {currency="CZK"}{%
        368.30%
      }%
    }%
    \XMLproduct {category="pevné disky" code="sbhd-99"}{%
      \XMLname {}{%
        Soft-slow disc &lt; 19,3 GB%
      }%
      \XMLprice {currency="CZK"}{%
        8500%
      }%
    }%
    \XMLproduct {category="polohovací zařízení" code="pxbd-13"}{%
      \XMLname {}{%
        Special touchpad%
      }%
      \XMLprice {currency="CZK"}{%
        5635.20%
      }%
    }%
  }%
}%

--------------------------

This format is more comfortable for further TeX processing.
You can define appropriate macros, for example:

--------------------------

\newtoks\street \newtoks\city \newtoks\postalcode \newtoks\email

\def\XMLpricelist#1{} % only process second argument ...
\def\XMLname#1#2{{\bf#2}\medskip}
\def\XMLvalidity#1#2{}
\def\XMLfirm#1#2{\bgroup
   \def\XMLname##1##2{{\it##2}\par}%
   \def\XMLaddress##1##2{##2\printaddress}%
   \def\XMLstreet##1##2{\street{##2}}%
   \def\XMLcity##1##2{\city{##2}}%
   \def\XMLpostalcode##1##2{\postalcode{##2}}%
   \def\XMLemail##1##2{\email{##2}}%
   \def\printaddress{ulice: \the\street, 
      mesto: \the\postalcode\space\the\city, email: {\tt\the\email}} 
   Dodavatel: #2\par
   \egroup
}
\def\XMLoffer#1{}
\def\XMLproduct#1#2{\bgroup
   \xarg{#1}%
   \def\XMLname##1##2{\def\name{##2}}%
   \def\XMLprice##1##2{\xarg{currency=?}\xarg{##1}\def\price{##2}}
   #2%
   \centerline{\name\space(\ARGcode)\dotfill\price\space\ARGcurrency}
   \egroup
}
\def\XMLem#1#2{{\it#2} \ignorespaces}

\catcode`&=13 \let&=\entity
\declentity{lt}{$<$}

\input test.out

----------------------------

After \input test.out, you get the desired document.


Features of the \xmlprep conversion
===================================

The <tag arguments>text</tag> is converted to:

   \XMLtag {arguments}{%
      text%
   }%

The <tag arguments/> or <tag/> are converted to

   \XMLtag {arguments}{}  or  \XMLtag {}{}

The <!-- text --> is ignored.

The <?text?> is converted to:

   \META {text}

The <!TEXT text> is converted to:

   \SPECTEXT {text}

The closings </tags> are checked to the opening <tag>. 
The nested tags are indented in the output.


Scanning of the arguments
=========================

The <tag arguments> are converted to \XMLtag{arguments}{%, so the arguments
are saved as first parameter of the \XMLtag macro. Arguments are typically
in the form

   argA="valueA" argB="valueB" argC="valueC"

You can define

   \def\XMLtag#1#2{\bgroup \xarg{#1}...process #2\egroup}

The \xarg{arguments} scans the arguments given in the parameter. The result of
\xarg{"valueA" argB="valueB" argC="valueC"} is equivalent to
\def\ARGargA{valueA}\def\ARGargB{valueB}\def\ARGargC{valueC}, so you can use
these macros in further processing.

There are alternatives of the format of arguments:

   argA='valueA' or argA=valueA (separated by space or end of arguments).

The \xarg macro is able to treat these alternatives properly.

Recommendation: If you assume default values of arguments then do something
similar to this:

   \xarg{argA="defaultA" argB="defaultB"}\xarg{#1}.


XML entities
============

The XML text includes sometimes the entity in the form &name; If it is true
in your XML document, then you can set & as active with the meaning \entity 
and declare the used entities by \declentity{name}{what to do}. If the document 
includes the entities &l; &gt; &amp; (for example) then you can do:

   \catcode`&=13 \let&=\entity
   \declentity{lt}{$<$}
   \declentity{gt}{$>$}
   \declentity{amp}{\&}


Various approaches for \XMLtag definitions
==========================================

Classical approach is

   \def\XMLtag#1#2{\bgroup process arguments #1, process body #2\egroup}

You can define \XMLtag with only one parameter. Then the second parameter is
normally processed in the group. This is usable if you need to keep the
possibility of catcode changing in the document. If you need to process
arguments in the same group as body then do something like this:

   \def\XMLtag#1{\bgroup \xarg{#1}\let\next=}

If you read body in #2 then you can decide what to do before and what to do
after:

   \def\XMLtag#1#2{what to do before #2 what to do after}

If the body of <tag> includes data declared in <tagA>, <tabB>, then you can
save the data first and then print the result in the part "what to do after". 
Example:

   <tag> <tagA>somethingA</tagA> <tagB>somethingB</tagB> </tag>

   \newtoks\dataA \newtoks\dataB
   \def\XMLtag#1#2{\bgroup
      \def\XMLtagA##1{\dataA=}\def\XMLtagB##1{\dataB=}%
      #2% process the body, dataA and dataB are set.
      print \the\dataB and \the\dataA.
      \egroup}

The meaning of the tag can depends on the outer tag used. See the "name" tag
in the introduction example. Then you must to define various meaning of such
tag inside another \XMLtag macro. 

   <tagA> <name>My name</name> </tagA> <tagB> <name>City name</name> </tagB>

   \def\XMLtagA#1#2{\bgroup
       \def\XMLname##1##2{...}%
       ...
       \egroup
   }
   \def\XMLtagB#1#2{\bgroup
       \def\XMLname##1##2{...something different}%
       ...
       \egroup
   }

-------------------------------------------------------