0

I am writing my first XSLT code to convert XML file.

Below is the sample code just to explain the issue here.

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="urn.abc.xyz" xmlns:ns2="abc:cde">
    <xsl:template match="/catalog">
      <html>
      <body>
        <h2>My CD Collection</h2>
        <table border="1">
          <tr bgcolor="#9acd32">
          <th>Title</th>
          <th>Artist</th>
          <th>Price</th>      
        </tr>
        <xsl:for-each select="catalog/cd">
          <tr>
            <td><xsl:value-of select="title"/></td>
            <td><xsl:value-of select="artist"/></td>
            <td><xsl:value-of select="price"/></td>        
          </tr>
        </xsl:for-each>
        </table>
      </body>

  </html>
</xsl:template>
</xsl:stylesheet>

above code works well when all the namespace is having prefix in input XML. but fails to work when namespace prefix is missing in XML , i.e., execution just shows xml content without tag names.

Input XML

    <?xml version="1.0" encoding="UTF-8"?>
<catalog  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="urn.abc.xyz" xmlns:ns2="abc:cde">
  <cd>
    <title>Empire Burlesque</title>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
</catalog>

output for above XML: Title Artist Price

with prefix:

<catalog  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:abc="urn.abc.xyz" xmlns:ns2="abc:cde">

without prefix

<catalog  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="urn.abc.xyz" xmlns:ns2="abc:cde">

if i run the XSL without namespace prefix (abc) then output looks like below

Empire Burlesque Bob Dylan USA Columbia 10.90 1985

can anyone help why my xsl is not working when namespace prefix is empty? and what changes needed in my xsl so that it will work even though namespace prefix is missing.

1
  • you've included your stylesheet twice, instead of including the input XML document Commented Jun 10, 2022 at 4:39

2 Answers 2

2

A declaration of a namespace without a prefix defines a default namespace, and it places all unprefixed elements within the scope of such declaration in that namespace.

In order to address such elements in your stylesheet, you must declare the same namespace, assign it a prefix and use that prefix for all unprefixed elements in the source XML.

For example, if the input XML is:

<catalog xmlns="urn.abc.xyz">
  <cd>
    <title>Empire Burlesque</title>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
</catalog>

then your stylesheet needs to be:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:abc="urn.abc.xyz"
    exclude-result-prefixes="abc">
    <xsl:template match="/abc:catalog">
      <html>
      <body>
        <h2>My CD Collection</h2>
        <table border="1">
          <tr bgcolor="#9acd32">
          <th>Title</th>
          <th>Artist</th>
          <th>Price</th>      
        </tr>
        <xsl:for-each select="abc:cd">
          <tr>
            <td><xsl:value-of select="abc:title"/></td>
            <td><xsl:value-of select="abc:artist"/></td>
            <td><xsl:value-of select="abc:price"/></td>        
          </tr>
        </xsl:for-each>
        </table>
      </body>
  </html>
</xsl:template>
</xsl:stylesheet>

(you can use any prefix you want).

However, this stylesheet is NOT suitable for processing an input whose elements are in no-namespace:

<catalog>
  <cd>
    <title>Empire Burlesque</title>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
</catalog>

It is technically possible to construct a stylesheet that would ignore the namespace and process both XMLs by using only the local-names of the elements. However, this is not recommended unless absolutely necessary. The assumption is that namespaces are there for a reason, and ignoring them may lead to incorrect results.

Note also that namespace declarations that do use a prefix, such as xmlns:ns2="abc:cde" in your example, are applied only to nodes that have the same prefix. In the absence of such nodes, such declaration has no effect and is entirely redundant (and irrelevant to your question).

Sign up to request clarification or add additional context in comments.

Comments

1

In XSLT 1.0, if the elements in your source document are in a namespace, you must use namespace prefixes in the XPath expressions in your XSLT.

In your source document you declare "urn.abc.xyz" as the default namespace URI. (Incidentally, "urn.abc.xyz" is not really a proper namespace URI; it should be an absolute URI, including a URI scheme, e.g. "urn:abc.xyz" or something, but that's not your problem here).

<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns="urn.abc.xyz">
  <cd>
    <title>Empire Burlesque</title>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
</catalog>

(NB I removed the unused XSLT and "ns2" namespace declarations since they weren't used in the file anywhere).

In your XSLT you have a template with an XPath match expression = /catalog, but because catalog has no namespace prefix, it would only match a catalog element in no namespace. To match a catalog element in the urn.abc.xyz namespace you would need to first associate a namespace prefix (which could be anything, e.g. my-namespace) with the urn.abc.xyz namespace URI, and then use the XPath expression "/my-namespace:catalog" in your template's match attribute. To bind the urn.abc.xyz namespace URI to a prefix my-namespace, add a namespace declaration to the stylesheet element: xmlns:my-namespace="urn.abc.xyz"

Then within the template, your xsl:for-each statement would refer to the child elements using the expression my-namespace:cd, and similarly you'd need to use my-namespace:title, etc. to refer to the child elements of the cd element.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.