Using XSLT to preprocess #ifdefs in XML

2019-07-20 05:13发布

问题:

This is presumably an almost carbon copy of Using XSLT as an XML pre-processor But as the OP of that question did not post a full example, despite being asked to, the replies are no use to anyone not familiar with XSLT. Neither have extensive web searches turned up anything helpful - XSLT seems remarkably poorly documented and little discussed on the Web.

Anyway ...

I have an XML file, say foo.xml, as follows (greatly simplified, obviously):

 <?xml version="1.0" encoding="UTF-8"?>

 <main>

  <fee>blah</fee>

  <ifdef select="OLD_VERSION">
   <fi>blah blah</fi>
  </ifdef>

  </main>

(C-style #ifdef changed to "ifdef" block in light of Ian Roberts's answer)

I want to run an xsltproc command on linux, as follows:

xsltproc --stringparam xmlver NEW_VERSION --nonet foo.xslt foo.xml

and have this use the following XSLT file, foo.xslt, to exclude the #ifdef'ed section:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 <xsl:output method="xml" />
 <xsl:param name="xmlver" required="yes"/>

 <xsl:template match="node() | @*">
  <xsl:copy><xsl:apply-templates select="node() | @*"/></xsl:copy>
 </xsl:template>

 <xsl:variable name="defines" select="document($xmlver)/defines"/>

 <xsl:template match="ifdef">

  <xsl:variable name="this" select="."/>

  <xsl:for-each select="$defines[def = $this/@select]">
   <xsl:apply-templates select="$this/node()" />
  </xsl:for-each>

 </xsl:template>

</xsl:stylesheet>

(I did use the replies to the question referenced above to construct this XSLT; but the missing ingredient is where/how to incorporate the "xmlver" value. Of course there is no guarantee it is correct in the above; but this essentially what I am asking - How is all this put together in a way that works?)

Any constructive replies will be greatly appreciated, and will doubtless be useful to many people with a similar requirement in the future; but please no tiresome, dogmatic "Why would you want to do that?" replies!

回答1:

The question you refer to looks like it's based on an XML structure that uses XML elements for its ifdefs. In your case your #ifdef lines are not XML elements, so you can't match them with an XSLT template. You'd be better off using a non-XML tool (possibly even the normal C pre-processor) to handle the ifdefs and feed the resulting XML to an XSLT if you need to do other XML-aware processing on it.

If you're happy to use real XML elements for your ifdefs, as in the previous question:

<?xml version="1.0" encoding="UTF-8"?>
<main>
  <fee>blah</fee>

  <ifdef select="OLD_VERSION">
   <fi>blah blah</fi>
  </ifdef>

</main>

then the question becomes how to treat the xmlver command line parameter as the single ifdef that you want to include, rather than as the location of a file from which to load a whole set of defines (which is how the previous question worked). That is much simpler:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

 <xsl:output method="xml" />
 <xsl:param name="xmlver" />

 <xsl:template match="node() | @*">
  <xsl:copy><xsl:apply-templates select="node() | @*"/></xsl:copy>
 </xsl:template>

 <xsl:template match="ifdef">
 <!-- for the ifdef we want, include all its child elements, text nodes etc
      but not its attributes, for other ifdefs do nothing -->    
  <xsl:if test="@select = $xmlver">
   <xsl:apply-templates select="node()" />
  </xsl:if>
 </xsl:template>

</xsl:stylesheet>

My previous attempt at this used a <xsl:template match="ifdef[@select=$xmlver]">, which works in some processors but not in xsltproc (it is technically not allowed by the XSLT spec, and xsltproc is stricter than my usual test harness, Xalan).



回答2:

suggestion: instead of xsltproc, can you just use m4 ?

<?xml version="1.0" encoding="UTF-8"?>

 <main>

  <fee>blah</fee>

ifdef(`X',`
   <fi>blah blah</fi>
')

</main>

.

$ m4 jeter.xml
<?xml version="1.0" encoding="UTF-8"?>

 <main>

  <fee>blah</fee>



</main>

.

$ m4 -DX jeter.xml
<?xml version="1.0" encoding="UTF-8"?>

 <main>

  <fee>blah</fee>


   <fi>blah blah</fi>


</main>


回答3:

I'd like to start with your general approach:

the replies are no use to anyone not familiar with XSLT. Neither have extensive web searches turned up anything helpful - XSLT seems remarkably poorly documented and little discussed on the Web.

This basically tells us that you are trying to write a program in a language you are not familiar with, and that you are trying to find information about that language by googling for it. Is that a good learning strategy?

I would have thought your problem was too many hits rather than too few, for example "XSLT tutorial" gives 2,300,000 hits. The top ones are all well worth a read. It seems odd to describe that as "poorly documented and little discussed".

Personally though if I'm going to start programming in a new language I always start by reading a book on the subject. Online material is rarely as carefully designed, written and reviewed as a text book. Of course I would say that since I'm the author of one of the most popular XSLT reference books, but I think it's true nonetheless.

Now, can I help you with your problem. Not really, I regret. Some people respond to questions on this forum with working tested code that you can use without understanding it. I don't: I simply point people in the right direction; and I'm not going to give you fragments of code in a language you don't understand. Pointing you in the right direction in this case means telling you to learn the language before you try using it.