使用 XSL 函数动态检索 XML 值

问题描述:

我输入的 xml 文件看起来像

my input xml file looks like

<root>
  <sub>
    <element1 value="abc"/>
        <element2 value="123"/>
  </sub>
  <sub1>
    <element1 value="ert"/>
    <element2 value="abc"/>
  </sub1>
</root>

我需要一个 XSLT 函数,它读取下面的 XML 文件并从上面的文件中提取在 map/domain/instance/@xpath 中指定的 xpath 表达式值

i need an XSLT function which reads below XML file and pulls the xpath expression value specified at map/domain/instance/@xpath from above file

<map>
  <domain>
    <instance xpath="root/sub/element1/@value" length="2"/>
  </domain>
  <domain>
        <instance xpath="root/sub1/element2/@value" length="3"/>
  </domain>
</map>

我需要一个 xslt 函数,它根据传入的 xml 文件检查为每个 xpath 表达式指定的长度.

I need a xslt function which checks the length specified for each xpath expression against the incoming xml file.

如果它在长度上失败,它应该返回 false.

if it fails on length it should retrun false.

这个 XSLT 1.0 样式表:

This XSLT 1.0 stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:variable name="document">
        <root>
            <sub>
                <element1 value="abc"/>
                <element2 value="123"/>
            </sub>
            <sub1>
                <element1 value="ert"/>
                <element2 value="abc"/>
            </sub1>
        </root>
    </xsl:variable>
    <xsl:template match="map/domain/instance" name="walker">
        <xsl:param name="path" select="@xpath"/>
        <xsl:param name="context"
               select="document('')/*/xsl:variable[@name='document']"/>
        <xsl:choose>
            <xsl:when test="contains($path,'/')">
                <xsl:call-template name="walker">
                    <xsl:with-param name="path"
                              select="substring-after($path,'/')"/>
                    <xsl:with-param name="context"
                      select="$context/*[name()=substring-before($path,'/')]"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:when test="starts-with($path,'@')">
                <xsl:value-of select="concat(
                                        string-length(
                                          $context/attribute::*
                                             [name()=substring($path,2)])
                                        =@length,
                                        '&#xA;')"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="concat(
                                        string-length(
                                          $context/*[name()=$path])
                                        =@length,
                                        '&#xA;')"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

输出:

false
true

编辑:XSLT 2.0 解决方案.此样式表:

Edit: XSLT 2.0 solution. This stylesheet:

<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="example.org">
    <xsl:output method="text"/>
    <xsl:strip-space elements="*"/>
    <xsl:variable name="document">
        <root>
            <sub>
                <element1 value="abc"/>
                <element2 value="123"/>
            </sub>
            <sub1>
                <element1 value="ert"/>
                <element2 value="abc"/>
            </sub1>
        </root>
    </xsl:variable>
    <xsl:template match="map/domain/instance">
        <xsl:sequence select="my:xpath(@xpath,$document)
                                   /string(string-length() =
                                           current()/@length)"/>
        <xsl:text>&#xA;</xsl:text>
    </xsl:template>
    <xsl:function name="my:xpath" as="item()*">
        <xsl:param name="path" as="xs:string"/>
        <xsl:param name="context" as="node()*"/>
        <xsl:variable name="steps" as="item()*"
                      select="tokenize($path,'/')"/>
        <xsl:variable name="this" as="item()*"
                      select="$context/(*[if ($steps[1]='*')
                                          then true()
                                          else name()=$steps[1]]|
                                        @*[if ($steps[1]='@*')
                                          then true()
                                          else name() =
                                               substring($steps[1],2)])"/>
        <xsl:choose>
            <xsl:when test="count($steps)>1">
                <xsl:sequence
                    select="my:xpath(string-join($steps[position()!=1],
                                                 '/'),
                                     $this)"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:sequence select="$this"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:function>
</xsl:stylesheet>

输出:

false
true

编辑 2:有所改进.所以现在,有了这个输入:

Edit 2: A bit improven. So now, with this input:

<map>
    <domain>
        <instance xpath="root/sub/element1/@*" length="2"/>
    </domain>
    <domain>
        <instance xpath="root/sub/*/@value" length="3"/>
    </domain>
</map>

输出:

false
true true