0

my input xml file looks like

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

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>

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

if it fails on length it should retrun false.

1 Answer 1

3

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>

Output:

false
true

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>

Output:

false
true

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>

Output:

false
true true
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.