user17750365
user17750365

Reputation: 21

XPath of attributes in XSL for any random xml

I have a xml and want some desired output, where element xpath is calculated and added into an attribute called elementXpath. That is done.

Now what I want is to get attributes xpath as well and that xpath should come under attribute value.

<?xml version="1.0" encoding="UTF-8"?>
<test>
    <request function="createNewJob" style="11" responseType="labelURL"></request>
</test>

and my xslt

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:template match="*">
        <xsl:param name="path"/>
        <xsl:variable name="my-path">
            <xsl:for-each select="ancestor-or-self::*">
                <xsl:value-of select="concat('/',name())"/>
                <xsl:if test="(preceding-sibling::*|following-sibling::*)[name()=name(current())]">
                    <xsl:value-of select="concat('[',count(preceding-sibling::*[name()=name(current())])+1,']')"/>
                </xsl:if>
            </xsl:for-each>
        </xsl:variable>
        
        <xsl:copy>
            <xsl:variable name="elementXpath" select="replace($my-path, '/.*?:|/.*?','/*:')"/>
            <xsl:attribute name="xpath">
                <xsl:value-of select="concat($elementXpath,'/text()')"/>
            </xsl:attribute>
            
            <xsl:copy-of select="@*"/>
            
            <xsl:for-each select="@*">
                <xsl:variable name="attr" select="concat($elementXpath,'/@',name())" />
                <xsl:value-of select="$attr"/>
                <xsl:text>,&#xa;</xsl:text>    
            </xsl:for-each>
            
            <xsl:apply-templates>
                <xsl:with-param name="path" select="$my-path"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="text()"/>
    
</xsl:stylesheet>

Current Output

<?xml version="1.0" encoding="UTF-8"?>
<test xpath="/*:test/text()">
   <request xpath="/*:test/*:request/text()"
            function="createNewJob"
            style="11"
            responseType="labelURL">/*:test/*:request/@function,
/*:test/*:request/@style,
/*:test/*:request/@responseType,
</request>
</test>

Desired Output:

<?xml version="1.0" encoding="UTF-8"?>
<test xpath="/*:test/text()">
    <request xpath="/*:test/*:request/text()"
        function="/*:test/*:request/@function"
        style="/*:test/*:request/@style"
        responseType="/*:test/*:request/@responseType">
    </request>
</test>

Another Sample:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Body>
        <ns:processShipmentResponse xmlns:ns="http://ws.business.uss.transforce.ca">
            <ns:return xmlns:ax27="http://dto.uss.transforce.ca/xsd"                       
                xmlns:ax25="http://ws.business.uss.transforce.ca/xsd"                      
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <ax25:error xsi:nil="true"/>
                <ax25:processShipmentResult>
                    <ax27:shipment someDummyAttribute="dummyAttributeValue">
                        <ax27:transit_time color="blue">1</ax27:transit_time>
                        <ax27:request id="1" function="createNewJob" style="11" responseType="labelURL"></ax27:request>
                        <ax27:transit_time color="red">1</ax27:transit_time>
                        <ax27:transit_time_guaranteed>false</ax27:transit_time_guaranteed>
                        </ax27:shipment>
                </ax25:processShipmentResult>
            </ns:return>
        </ns:processShipmentResponse>
    </soapenv:Body>
</soapenv:Envelope>

Please help me to get this. Thanks

Upvotes: 2

Views: 59

Answers (1)

michael.hor257k
michael.hor257k

Reputation: 117073

Try perhaps:

XSLT 2.0

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:template match="*">
    <xsl:copy>
        <xsl:attribute name="xpath" select="ancestor-or-self::*/concat('/*:', local-name())" separator=""/>
        <xsl:apply-templates select="@*|*"/>
    </xsl:copy>
</xsl:template>
        
<xsl:template match="@*">
    <xsl:attribute name="{name()}" select="ancestor::*/concat('/*:', local-name()), concat('/@', local-name())" separator=""/>
</xsl:template>

</xsl:stylesheet>

The result here leaves out the text() location step. I am not sure why you would need it but if you do, you can easily add it.

Note that attributes too can be in a namespace. Here we ignore this possibility.

Upvotes: 1

Related Questions