KKGanguly
KKGanguly

Reputation: 343

Exracting child node attributes of following sibling node in XPath

My XML code is,

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<unit
   xmlns="http://www.srcML.org/srcML/src" revision="0.9.5" language="Java" filename="ListSample.java">
<class>
    <specifier>public</specifier> class 
    <name>ListSample</name>
    <block>{

        <decl_stmt>
            <decl>
                <specifier>private</specifier>
                <type>
                    <name>List</name>
                </type>
                <name>nameList</name>
                <init>=
                    <expr>
                        <operator>new</operator>
                        <call>
                            <name>List</name>
                            <argument_list>()</argument_list>
                        </call>
                    </expr>
                </init>
            </decl>;
        </decl_stmt>
        <function>
            <specifier>public</specifier>
            <type>
                <name>void</name>
            </type>
            <name>method</name>
            <parameter_list>()</parameter_list>
            <block>{

                <expr_stmt>
                    <expr>
                        <call>
                            <name>
                                <name>nameList</name>
                                <operator>.</operator>
                                <name>add</name>
                            </name>
                            <argument_list>(
                                <argument>
                                    <expr>
                                        <literal type="string">"name"</literal>
                                    </expr>
                                </argument>)
                            </argument_list>
                        </call>
                    </expr>;
                </expr_stmt>
                }
            </block>
        </function>
        }
    </block>
</class>

I am trying to extract the data type of any List. For example, nameList is a list given in the XML file. I am trying to achieve this by searching the name of the list (preceded by the List keyword) and then by determining the data type from literal type="string". Here, is my code using XPath and XSLT.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

<xsl:template
    match="//*[local-name()='name'][preceding-sibling::node()='List']">
    <xsl:variable name="listName" select="text()"></xsl:variable>
    <xsl:call-template name="getType">
        <xsl:with-param name="listName" select="$listName" />
    </xsl:call-template>

</xsl:template>
<xsl:template name="getType"
    match="//*[local-name()='name'][following-sibling::node()='.' and following-sibling::node()[2]='add']">
    <xsl:param name="listName" />
    <xsl:if test="node()=$listName">
        <xsl:message>
            <xsl:value-of select="parent::node()/following-sibling::literal/@type" />
        </xsl:message>
    </xsl:if>

</xsl:template>

However, the part enclosed in the message

<xsl:message>
    <xsl:value-of select="parent::node()/following-sibling::literal/@type" />
</xsl:message>

is unable to get the literal type. How can I correct the code so that I can get the literal node and the data type in the attribute? Thanks in advance.

Upvotes: 2

Views: 2148

Answers (2)

JLRishe
JLRishe

Reputation: 101682

I'm still not sure what the purpose of this is, but this produces the expected result:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:src="http://www.srcML.org/srcML/src"
>

  <!-- key to locate the usage of a list in an .add() operation based on the list 
       variable's name -->      
  <xsl:key name="kListUsage"
           match="src:call[src:name[src:operator[1] = '.' and src:name[2] = 'add']]"
           use="src:name/src:name[1]" />

  <!-- Override default handling of text nodes -->
  <xsl:template match="text()" />

  <xsl:template match="src:decl[src:type/src:name = 'List']/src:name">
    <xsl:apply-templates select="key('kListUsage', .)[1]" mode="listUsage" />
  </xsl:template>

  <xsl:template match="src:call" mode="listUsage">
    <xsl:message>
      <xsl:value-of select="src:argument_list/src:argument[1]/src:expr/src:literal/@type"/>
    </xsl:message>
  </xsl:template>
</xsl:stylesheet>

When run on your sample input, this outputs the following message:

string

Among other things, it seems you're focusing too much nodes' positions relative to each other when the logic will be cleaner if you think about where they are respective to their shared ancestors and work down from those shared ancestors.

Upvotes: 4

Mads Hansen
Mads Hansen

Reputation: 66724

Given your current XPath, there are several adjustments that would need to be made:

  1. From the /unit/class/block/decl_stmt/decl/type/name element, you need to jump up two levels. The parent is type and you want to go up to the decl element
  2. From the decl element, the following-sibling is the function element, which will have the literal descendant
  3. Your XML is bound to a namespace and you were selecting the literal element as if was bound to the "no namespace", so adjust to select by local-name() as you have done in other areas

An updated XPath that produces "string" in the message:

parent::*/parent::node()/following-sibling::*//*[local-name()='literal']/@type"

You could also write it like this:

../../following-sibling::*//*[local-name()='literal']/@type

You might be able to just use the following:: axis and write more succinctly:

../../following::*[local-name()='literal']/@type

Upvotes: 1

Related Questions