简体   繁体   中英

Using xsl:copy-of when defining a variable in XSLT 2.0

I have (in an imaginary simplified world) a basic source XML structure:

<?xml version="1.0"?>
<library>
  <book>
    <language>french</language>
    <title>Les Misérables</title>
  </book>
  <book>
    <language>english</language>
    <title>Great Expectations</title>
  </book>
</library>

I define a variable myVar1 using select attribute of the xsl:variable

<xsl:variable name="myVar1" select="library/book[language='french']"/>

And I define a variable myVar2 with a xsl:copy-of inside the xsl:variable

    <xsl:variable name="myVar2">
        <xsl:copy-of select="library/book[language='french']"/>
    </xsl:variable>

I expect myVar1 and myVar2 to be the same, but it isn't

    <xsl:value-of select="$myVar1/title"/> <!--result: Les Misérables-->
    <xsl:value-of select="$myVar2/title"/> <!--empty result-->

What is wrong when I define myVar2? How to indicate that I want it as a structured queryable variable (like myVar1)? The use of select is not convenient in case I have several conditional statements to define the variable.

Here is the complete XSLT file

<?xml version='1.0'?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match="/">

    <xsl:variable name="myVar1" select="library/book[language='french']"/>

    <xsl:variable name="myVar2">
      <xsl:copy-of select="library/book[language='french']"/>
    </xsl:variable>

    <xsl:value-of select="$myVar1/title"/>
    <xsl:value-of select="$myVar2/title"/>

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

The rules are given at https://www.w3.org/TR/xslt-30/#variable-values

A quick summary: if there is no select attribute and no as attribute, then the value of the variable is a new document node, which contains the copied element as a child. It's this extra level that stops your path expression working. With select="library/book" the value of the variable is a book element, with the sequence constructor the value is a document node that has a book element as its child.

I test it out.

if you gat a file with your input you can include it with <?xe.source [FILE_PATH]>

Here is my XML File named Test_XML_1.xml:

<?xml version="1.0"?>
<library>
  <book>
    <language>french</language>
    <title>Les Misérables</title>
  </book>
  <book>
    <language>english</language>
    <title>Great Expectations</title>
  </book>
</library>

Same as yours.

Here is my XSLT Code:

<?xml version="1.0" encoding="UTF-8"?><?xe.source ../TemporaryFiles/Test_XML_1.xml#library?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output media-type="text/xml" method="xml"></xsl:output>
    <xsl:variable name="myVar1" select="/library/book[language='french']"></xsl:variable>
    <xsl:variable name="myVar2">
        <xsl:copy-of select="/library/book[language='french']"></xsl:copy-of>
    </xsl:variable>
    <xsl:template match="/">
        <root>
            <xsl:copy-of select="$myVar1"></xsl:copy-of>
            <xsl:copy-of select="$myVar2"></xsl:copy-of>
        </root>
    </xsl:template>
</xsl:stylesheet>

With the File and the XSLT I gat the Following Output:

<?xml version="1.0" encoding="UTF-8"?>
<root>
<book>
<language>french</language>
<title>Les Misérables</title>
</book>
<book>
<language>french</language>
<title>Les Misérables</title>
</book>
</root>

To Use Variable right, you need to declare them between the <output> and <template> element. Otherwise you need to work with Template Calls. It's jus as in every Programming/Script language. Set Global or Local variables.

Btw. you neet to be clear where you are in the XPath.

If you say once library/book[language='french'] you are so lang in this path as you switched or exit the template. You to handly the problem is to use Dynamic XPath or Static XPath.

So you can yous also this solution:

<?xml version="1.0" encoding="UTF-8"?><?xe.source ../TemporaryFiles/Test_XML_1.xml#library?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output media-type="text/xml" method="xml"></xsl:output>
    <xsl:template match="/">
        <xsl:variable name="myVar1" select="library/book[language='french']"></xsl:variable>
        <xsl:variable name="myVar2">
            <xsl:copy-of select="library/book[language='french']"></xsl:copy-of>
        </xsl:variable>
        <root>
            <xsl:copy-of select="$myVar1"></xsl:copy-of>
            <xsl:copy-of select="$myVar2"></xsl:copy-of>
        </root>
    </xsl:template>
</xsl:stylesheet>

To add to a selected Path a part of the path use this:

<xsl:value-of select="//*[$myVar2]/title" /> Should be work then.

I hope that small task helped you;)

I expect myVar1 and myVar2 to be the same

But they are not the same:

  • The first variable contains a reference to the book node in the source document.

  • The second variable contains a copy of the book node in its own document .

Therefore, the instruction:

<xsl:value-of select="$myVar2/title"/>

returns empty, because title is not a child of $myVar2 . The content of $myVar2 is the copied book node - and in order to get the value of its child title node you need to use:

<xsl:value-of select="$myVar2/book/title"/>

You can use

<xsl:variable name="myVar2" as="element()*">
  <xsl:copy-of select="library/book[language='french']"/>
</xsl:variable>

to make your variable hold a sequence of element nodes instead of a document (fragment) node containing the element nodes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM