views:

197

answers:

3

Greetings!

I want to extract some properties from different Maven POMs in a XSLT via the document function. The script itself works fine but the document function returns an empty result for the POM as long as I have the xmlns="http://maven.apache.org/POM/4.0.0" in the project tag. If I remove it, everything works fine.

Any idea how the make this work while leaving the xmlns attribute where it belongs or why this doesn't work with the attribute in place?

Here comes the relevant portion of my XSLT:

<xsl:template match="abcs">
 <xsl:variable name="artifactCoordinate" select="abc"/>
   <xsl:choose>
        <xsl:when test="document(concat($artifactCoordinate,'-pom.xml'))">
         <abc>
          <ID><xsl:value-of select="$artifactCoordinate"/></ID>
    <xsl:copy-of select="document(concat($artifactCoordinate,'-pom.xml'))/project/properties"/>
   </abc>
         </xsl:when>
            <xsl:otherwise>
       <xsl:message terminate="yes">
           Transformation failed: POM "<xsl:value-of select="concat($artifactCoordinate,'-pom.xml')"/>" doesn't exist. 
       </xsl:message>
      </xsl:otherwise> 

</xsl:choose> 

And, for completeness, a POM extract with the "bad" attribute:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"&gt;
<modelVersion>4.0.0</modelVersion>
<!-- ... -->
<properties>
    <proalpha.version>[5.2a]</proalpha.version>
    <proalpha.openedge.version>[10.1B]</proalpha.openedge.version>
    <proalpha.optimierer.version>[1.1]</proalpha.optimierer.version>
    <proalpha.sonic.version>[7.6.1]</proalpha.sonic.version>
</properties>
 </project>
+2  A: 

This is a namespace problem. The xmlns="http://maven.apache.org/POM/4.0.0" in the source document means that all the elements are by default put into the "http://maven.apache.org/POM/4.0.0" namespace in the XML document.

If you want to get ahold of them in your xslt, you need to declare that namespace in your xslt (with or without a prefix to use) and then use that namespace when selecting your elements.

For example, I'm guessing that the template in your example is meant to match an "abcs" element in your POM, yes? Try adding a namespace declaration in your xsl:stylesheet, e.g.:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:pom="http://maven.apache.org/POM/4.0.0" version="1.0">

That says to the XSL "I want to add 'pom' as a prefix that identifies the 'http://maven.apache.org/POM/4.0.0' namespace in this document."

Then when selecting elements or matching templates, use that prefix, e.g.:

<xsl:template match="pom:abcs">

Or try it without the prefixes by declaring your stylesheet with the POM namespace as default, something like:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns="http://maven.apache.org/POM/4.0.0" version="1.0">
Matt Gibson
Thank you! Sadly the "global approach" doesn't work for me - apparently because I've got tags from other namespaces too. Maybe my question was not precise enough - the abcs are in another namespace. The per prefix approach works for me though, like Dimitre suggested below.
Jan
@Matt: If you want a default namespace when selecting elements with XPath, you need to use `xpath-default-namespace` (supported in XSLT 2.0 or later), as described in http://www.w3.org/TR/xslt20/#unprefixed-qnames. Setting the default namespace with `xmlns="..."` only configures the default namespace of XML literals in the stylesheet, XPath expressions are not affected.
markusk
@markusk Thanks for the clarification; I've not used xpath-default-namespace yet, sure that'll come in handy at some point in my future!
Matt Gibson
+4  A: 

Your problem is that the POM extract uses default namespace. This means that the elements, although unprefixed, are in the "http://maven.apache.org/POM/4.0.0" -- not in the "no namespace".

However, in this XPath expression, in the XSLT code:

document(concat($artifactCoordinate,'-pom.xml'))/project/properties

the names project and properties are unprefixed. XPath always treats unprefixed names as belonging to "no namespace". Hence, no such elements are found and no node is selected.

Solution: Add a namespace definition to your <xsl:stylesheet>, lets say:

  xmlns:p="http://maven.apache.org/POM/4.0.0"

Then rewrite element names in any expressions referencing POM nodes from someElement to p:someElement. For example:

document(concat($artifactCoordinate,'-pom.xml'))/p:project/p:properties
Dimitre Novatchev
Thank you Dimitre - this works! Right now this solution givs me xmlns:pom="..." in every element I get from the document function call but I hope to find some way to get rid of that, perhaps by providing the declaration in the target document before.
Jan
@Jan: Just add to the `<xsl:stylesheet>` the following attribute: `exclude-result-prefixes="pom"`
Dimitre Novatchev
Ah! That's a nice addition - thank you. I also found a general solution which comes in handy for my special need (see my answer).
Jan
A: 

Node can (if using XSLT 2.0+) also be adressed via * because they lie in another namespace .

    <xsl:copy-of select="document(concat($artifactCoordinate,'-pom.xml'))/*:project/*:properties)"/> 

This can be just convienent or especially useful if the namespace is unknown. In this case the nice side effect is that if the namespace is marked this way the nodes from the other namespace don't annoted - which is not wanted in our case.

Jan
@Jan: The `*:name` syntax is supported only in XPath 2.0.
Dimitre Novatchev
@Jan: Also note that you can use `xpath-default-namespace` if you're using XSLT 2.0 or later, as described in http://www.w3.org/TR/xslt20/#unprefixed-qnames.
markusk
@Dimitre: Sure - sorry, I forgot to add that detail (using that).
Jan