开发者

Dynamically generate XPath expression using XML data

I would like to dynamically create XPath expressions in an XSL determined by data in an XML file. (i.e. the XML data is "concatenated" to create an XPath expression).

Example of XML data:

<criteria>
    <criterion>AAA</criterion>
    <criterion>BBB<开发者_运维问答/criterion>
    <criterion>CCC</criterion>
</criteria>

Example of how I would like the XPath expression to look like:

//AAA | //BBB | //CCC

And this dynamic generation needs to be done in an XSL file.

I am fairly new to XSL (and family) and would appreciate some general direction on how to tackle this problem.

Thanks!

Edit: To provide a little more context.... What I need to do is generate an XPath to be used to create a second XSL which transforms an entirely different XML file. I know how to create an XSL from an XSL, I just need to dynamically create XPath expressions. If I could modify variables (which I read from somewhere else I can't) I would just keep concatenating nodes together to form an expression. Then from there I would use the variable name wherever I needed it. Unfortunately I can't do this though.. :(


The following will create the sample XPATH string as the value of the "generatedXPATH" variable when run against the sample XML file:

    <xsl:variable name="generatedXPATH">
        <xsl:for-each select="/criteria/criterion">
            <xsl:text>//</xsl:text>
            <xsl:value-of select="." />
            <xsl:if test="position()!=last()">
                <xsl:text> | </xsl:text>
            </xsl:if>
        </xsl:for-each>
    </xsl:variable>

You could use that variable in a stylesheet that produces a stylesheet to construct the template @match values like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xslt="http://www.w3.org/1999/XSL/TransformAlias"
    version="1.0">
    <xsl:namespace-alias stylesheet-prefix="xslt" result-prefix="xsl"/>
    <xsl:output indent="yes" />
    <xsl:template match="/">
        <xsl:variable name="generatedXPATH">
            <xsl:for-each select="/criteria/criterion">
                <xsl:text>//</xsl:text>
                <xsl:value-of select="." />
                <xsl:if test="position()!=last()">
                    <xsl:text> | </xsl:text>
                </xsl:if>
            </xsl:for-each>
        </xsl:variable>

        <xslt:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
            <xslt:template>
                <xsl:attribute name="match">
                    <xsl:value-of select="$generatedXPATH" />
                </xsl:attribute>
                <xsl:comment>Do something special, like select the value of the matched elements</xsl:comment>
                <xslt:value-of select="." />
            </xslt:template>
        </xslt:stylesheet>
    </xsl:template>

</xsl:stylesheet>

When run against the sample XML produces this stylesheet:

<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="//AAA | //BBB | //CCC">
<!--Do something special, like select the value of the matched elements-->
<xsl:value-of select="." />
</xsl:template>
</xsl:stylesheet>


It is possible to create XPath expressions dynamically in XSLT, but no dynamic evaluation of these expressions is possible (at least until XSLT 2.1).

What you want can be done in another way in XSLT.

Here is an example:

This transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my:my"
 >
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <my:criteria>
  <criterion>a</criterion>
  <criterion>b</criterion>
  <criterion>c</criterion>
 </my:criteria>

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

 <xsl:template match="*[name()=document('')/*/my:criteria/*]">
   <xsl:element name="{name()}-{name()}" namespace="{namespace-uri()}">
     <xsl:apply-templates select="node()|@*"/>
   </xsl:element>
 </xsl:template>
</xsl:stylesheet>

when applied on this XML document:

<t>
  <a>
    <x/>
    <y/>
    <b>
      <c/>
      <d/>
    </b>
  </a>
</t>

produces:

<t>
    <a-a>
        <x></x>
        <y></y>
        <b-b>
            <c-c></c-c>
            <d></d>
        </b-b>
    </a-a>
</t>

That is, we only processed in a special way any element (anywhere in the document), whose name was a or b or c.


You cannot dynamically do that with xsl or xpath. You'll have to use php or whatever (depends on where you're using this). If you have a limited set of nodes, you can use //AAA | //BBB | //CCC and it will work, though this is not really usefull if you want it to work on lots of different nodes.

See here for more info on XPath.

Edit: Jeez, you edited it.

Use /criteria/[criterion=AAA] for that.

Edit again:

the query should be: "/criteria/[criterion=AAA] | criteria/[criterion=BBB] | criteria/[criterion=CCC]". You can probably rephrase this to be more efficient though i forgot how. See this tutorial to see how it's done. Hope that helps.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜