From within a template I'm trying to set a variable using a select= which references an absolute path. But when the "root" element specifies an xmlns= namespace, the select fails (returns an empty string). Here's the XSLT:

<?xml version="1.0" ?>
<xsl:transform version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns="http://foo.bar.com/something"
>
  <xsl:output method="xml" indent="yes" encoding="windows-1252" />

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

  <xsl:template match="*[@Mark]">
    <xsl:variable name="the_val" select="/Top/Wanted/@Val"/>
    <NewNode NewVal="{$the_val}" />
  </xsl:template>

</xsl:transform>

And here is the input XML:

<?xml version="1.0" encoding="windows-1252"?>
<Top xmlns="http://foo.bar.com/something">
  <Wanted Val='get this' />
  <FromHere Mark='X'/>
</Top>

And this is the result:

<?xml version="1.0" encoding="windows-1252"?>
<Top xmlns="http://foo.bar.com/something">
  <Wanted Val="get this"></Wanted>
  <NewNode NewVal="" />
</Top>

When I remove the xmlns= from both the XSLT and XML, it works. I can't do that in practice, because I have no control over the XML that is being sent to me -- I can only change the XSLT. Also, in reality the XML documents are more complex with deep, multi-level hierarchy, which is why I need to use an absolute path.

TIA for any help.

Recommended Answers

All 9 Replies

when there are some namespace used in a xml you MUST rewrite them with prefixe in your XSLT,without adding the namespaces, all your xpath and match attribute which use a name are false

Try that

<?xml version="1.0" ?><xsl:transform version="1.0"  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  xmlns:a="http://foo.bar.com/something">  <xsl:output method="xml" indent="yes" encoding="windows-1252" />   <xsl:template match="@*|*">    <xsl:copy>      <xsl:apply-templates select="@*" />      <xsl:apply-templates select="*" />    </xsl:copy>  </xsl:template>   <xsl:template match="*[@Mark]">    <xsl:variable name="the_val" select="/a:Top/a:Wanted/@Val"/>    <NewNode NewVal="{$the_val}" />  </xsl:template> </xsl:transform>

there is problem with namespace
the best make a new namespace
and then exculde the new namspace

<?xml version="1.0" ?>
<xsl:transform version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:a="http://foo.bar.com/something"
    exclude-result-prefixes="a"
    
    >
    <xsl:output method="xml" indent="yes" encoding="windows-1252" />
    
    <xsl:template match="@*|*">
        <xsl:copy>
            <xsl:apply-templates select="@*" />
            <xsl:apply-templates select="*" />
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="a:*[@Mark]">
        <xsl:variable name="the_val" select="/a:Top/a:Wanted/@Val"/>
        <NewNode  NewVal="{$the_val}" />
    </xsl:template>
    
</xsl:transform>

result

<?xml version="1.0" encoding="windows-1252"?>
<Top xmlns="http://foo.bar.com/something">
   <Wanted Val="get this"/>
   <NewNode xmlns="" NewVal="get this"/>
</Top>

This helps very much.

Thanks!

You can eliminate the spurious xmlns="" by adding the namespace prefix to the NewNode element, i.e.

<xsl:template match="a:*[@Mark]">
   <xsl:variable name="the_val" select="/a:Top/a:Wanted/@Val"/>
   <a:NewNode NewVal="{$the_val}" />
 </xsl:template>

This produces the following output

<Top xmlns="http://foo.bar.com/something">
  <Wanted Val="get this"/>
  <NewNode NewVal="get this"/>
</Top>

BTW, if you are using a XSLT 2.0 aware processor such as Saxon, you can achieve the same result using the xpath-default-namespace declaration.

Thanks again. However, when I try to add the namespace prefix to the NewNode element, the xmlns="" goes away, but the a: namespace reappears:

<Top xmlns="http://foo.bar.com/something">
  <Wanted Val="get this"></Wanted>
  <a:NewNode NewVal="get this" xmlns:a="http://foo.bar.com/something" />
</Top>

Maybe it's an artifact of the processor I'm using for these tests (msxsl.exe).

It might be. I used xsltproc.

I just tried xsltproc, and I got the same results as msxml.exe. Here's everything again, just to be sure we have the same code.

XSLT:

<?xml version="1.0" ?>
<xsl:transform version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:a="http://foo.bar.com/something"
  exclude-result-prefixes="a"
>
  <xsl:output method="xml" indent="yes" encoding="windows-1252" />

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

  <xsl:template match="a:*[@Mark]">
    <xsl:variable name="the_val" select="/a:Top/a:Wanted/@Val"/>
    <a:NewNode NewVal="{$the_val}" />
  </xsl:template>

</xsl:transform>

Input:

<?xml version="1.0" encoding="windows-1252"?>
<Top
  xmlns="http://foo.bar.com/something"
>
  <Wanted Val='get this' />
  <FromHere Mark='X'/>
</Top>

Result:

<?xml version="1.0" encoding="windows-1252"?>
<Top xmlns="http://foo.bar.com/something">
  <Wanted Val="get this"/>
  <a:NewNode xmlns:a="http://foo.bar.com/something" NewVal="get this"/>
</Top>

i make a mistake

this problem with copy
when you use copy then namespace will copy also


a better way ist use xsl as template

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:a="http://foo.bar.com/something" exclude-result-prefixes="a">
	<xsl:output method="xml" indent="yes" encoding="windows-1252"/>

	<xsl:template match="/">
		<xsl:apply-templates />
	</xsl:template>



	<xsl:template match="a:Top">
		<Top>
			<xsl:apply-templates/>			
		</Top>
	</xsl:template>



	<xsl:template match="a:Wanted">
		<Wanted>
			<xsl:attribute name="Val">
				<xsl:value-of select="@Val"/>
			</xsl:attribute>
		</Wanted>
	</xsl:template>

	<xsl:template match="a:FromHere">
		<FromHere>
			<xsl:attribute name="Mark">
				<xsl:value-of select="@Mark"/>
			</xsl:attribute>
		</FromHere>
	</xsl:template>
</xsl:stylesheet>

then no namespace will displayed


result

<?xml version="1.0" encoding="windows-1252"?>
<Top>
  <Wanted Val="get this" />
  <FromHere Mark="X" />
</Top>

Wonderful. Just two things:

1) The <Top> element now doesn't include xmlns="http://foo.bar.com/something". I got it to appear by adding xmlns="http://foo.bar.com/something" to the <xsl:transform> element.

2) I wanted the <FromHere> node to be transformed into a <NewNode> node, with an attribute whose value comes from another element via an absolute path. This was a simple bit of re-writing:

<xsl:template match="a:FromHere">
    <NewNode>
      <xsl:attribute name="NewVal">
        <xsl:value-of select="/a:Top/a:Wanted/@Val" />
      </xsl:attribute>
    </NewNode>
  </xsl:template>

This generated what I wanted!

However, the real document I'm processing contains much more complex structure than this simple test. I didn't want to replicate that entire structure in the XSLT code. So I went back to my original attempt, which uses xsl:copy for most things, and incorporated the various fixes mentioned in the various posts:

<xsl:transform version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:a="http://foo.bar.com/something"
  xmlns="http://foo.bar.com/something"
  exclude-result-prefixes="a"
>
  <xsl:output method="xml" indent="yes" encoding="windows-1252" />

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

  <xsl:template match="*[@Mark]">
    <xsl:variable name="the_val" select="/a:Top/a:Wanted/@Val"/>
    <NewNode NewVal="{$the_val}" />
  </xsl:template>

</xsl:transform>

And this now worked also.

Finally, I like the idea of matching element names instead of attribute names (I had tried this first, but it didn't work, probably because of namespace confusion). So here is the final version that does what I need in a nice way:

<xsl:transform version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:a="http://foo.bar.com/something"
  xmlns="http://foo.bar.com/something"
  exclude-result-prefixes="a"
>
  <xsl:output method="xml" indent="yes" encoding="windows-1252" />

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

  <xsl:template match="a:FromHere">
    <NewNode>
      <xsl:attribute name="NewVal">
        <xsl:value-of select="/a:Top/a:Wanted/@Val" />
      </xsl:attribute>
    </NewNode>
  </xsl:template>

</xsl:transform>

Which produces the following:

<?xml version="1.0" encoding="windows-1252"?>
<Top xmlns="http://foo.bar.com/something">
  <Wanted Val="get this"></Wanted>
  <NewNode NewVal="get this" />
</Top>

Thanks very much for all the help!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.