views:

42

answers:

2

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.

A: 

A couple questions:

  • Does your XSLT processor support XSLT 2.0?
  • Can you provide an example of the expected output?
Dave
yes my xslt processor supports xslt 2.0.the output i am expecting (from xslt function) is boolean based on the value present at xpath location
Laxmikanth Samudrala
+2  A: 

This XSLT 1.0 stylesheet:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <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
Alejandro
Not bad, @Alejandro! (+1).A
Dimitre Novatchev
@Dimitre: Thanks! I've fought a bit with `$document` type...
Alejandro