Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XSL value-of doesn't seem to get value from the xml

I have an xml file which contains following:

<?xml version="1.0"?>
<mods xmlns="http://www.loc.gov/mods/v3" xmlns:mods="http://www.loc.gov/mods/v3"  
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xmlns:xlink="http://www.w3.org/1999/xlink">
   <titleInfo><title>A-Title-01</title></titleInfo>
</mods>

And an XSL file:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
    <h2>Description</h2>
    <p>Hello</p>
    <p><xsl:value-of select="titleInfo/title"/></p>
</xsl:template>
</xsl:stylesheet>

My problem is I don't get the title value in the xHTML. I could only see

Description

Hello

But If I remove default namespace from the xml like this:

<?xml version="1.0"?>
<mods xmlns:mods="http://www.loc.gov/mods/v3"  
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xmlns:xlink="http://www.w3.org/1999/xlink">
   <titleInfo><title>A-Title-01</title></titleInfo>
</mods>

and change the style sheet's match to <xsl:template match="/mods"> I can see the title value.

But I can not remove the default namespace from the xml because xml is generated by a form and it won't work if I remove the default namespace. I don't even have a clue how to get around this or if I am doing something wrong. Please help.

Thanks in Advance.

like image 774
lloydh Avatar asked Mar 23 '23 08:03

lloydh


2 Answers

A template match of / does not start at the document element, it is the "root node" - which is before any content. The first node() in the document doesn't have to be the document element, it could be a comment or a processing instruction and would be a child of the "root node".

So, if you want to match the document element mods, and you don't want to worry about namespaces your template match could be /*. Then your XPath selecting elements relative from the document element would work.

However, your titleInfo and title elements inherit the namespace of the document element. So, if you want to match them you have several options:

  1. Declare the namespace and give it a prefix so that you can use it in your XPath
  2. Match generically on element with a predicate filter to match the local-name() and namespace-uri() - but if you know the URI it would be easier to declare it and use the prefix in option #1.
  3. Match generically on element with a predicate filter to match just the local-name(). Not as clean/proper, but likely will work.
  4. Match generically on element for each step and rely on the structure of this simple document to find the element you are looking for

Option #1:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:m="http://www.loc.gov/mods/v3">
        <xsl:template match="/m:mods">
            <h2>Description</h2>
            <p>Hello</p>
            <p><xsl:value-of select="m:titleInfo/m:title"/></p>
        </xsl:template>
</xsl:stylesheet>

Option #2:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/*[local-name()='mods' and namespace-uri()='http://www.loc.gov/mods/v3']">
            <h2>Description</h2>
            <p>Hello</p>
        <p><xsl:value-of select="*[local-name()='titleInfo' 
                                   and namespace-uri()='http://www.loc.gov/mods/v3']/*[local-name()='title' and namespace-uri()='http://www.loc.gov/mods/v3']"/></p>
        </xsl:template>
</xsl:stylesheet>

Option #3:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/*[local-name()='mods']">
            <h2>Description</h2>
            <p>Hello</p>
        <p><xsl:value-of select="*[local-name()='titleInfo']/*[local-name()='title']"/></p>
        </xsl:template>
</xsl:stylesheet>

Option #4:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/*">
        <h2>Description</h2>
        <p>Hello</p>
        <p><xsl:value-of select="*/*"/></p>
    </xsl:template>
</xsl:stylesheet>
like image 179
Mads Hansen Avatar answered Apr 02 '23 18:04

Mads Hansen


Add a prefixed declaration for your namespace and then match on the prefixed names.

Below is untested:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:loc="http://www.loc.gov/mods/v3" exclude-result-prefixes="loc">
<xsl:template match="/loc:mods">
    <h2>Description</h2>
    <p>Hello</p>
    <p><xsl:value-of select="loc:titleInfo/loc:title"/></p>
</xsl:template>
</xsl:stylesheet>
like image 36
G. Ken Holman Avatar answered Apr 02 '23 19:04

G. Ken Holman