Abhishek Nayak
Abhishek Nayak

Reputation: 3748

xslt merge two xml file with pre specified select value in attribute

I have two XML files named as data1.xml and data2.xml, using XSLT how can i generate result.xml, in data2.xml i have used selectValFrm attribute to specify the XPATH to pick value form. and same for other attributes.

data1.xml

<inputRoot>
    <childOne>
        I am first child
    </childOne>
    <childTwo>
        <nestedChild id="1">
            I am second child
        </nestedChild>
    </childTwo> 
</inputRoot>

data2.xml

<result>
    <ChildOneDesc selectValFrm="root/childOne"></ChildOneDesc>
    <ChildTwoDesc>
        <value num="root/childTwo/nestedChild/@id" selectValFrm="root/childTwo/nestedChild"></value>
    </ChildTwoDesc> 
</result>

result.xml

<result>
    <ChildOneDesc>
        I am first child
    </ChildOneDesc>
    <ChildTwoDesc>
        <value num="1">
            I am second child
        </value>
    </ChildTwoDesc> 
</result>

I wrote an XSLT like:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:java="http://xml.apache.org/xslt/java" 
    xmlns:LocaTime="xalan://org.joda.time.LocalTime"
    xmlns:LocaDate="xalan://org.joda.time.LocalDate">

    <xsl:output method="xml" encoding="utf-8" indent="yes" />

    <xsl:param name="xml-input" as="xs:string" />
    <xsl:param name="xml-input-data" select="document($xml-input)" />

    <xsl:template match="node()|@*">        
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*" name="init-element-value">
        <xsl:element name="{name()}">
            <xsl:value-of select="$xml-input-data/{@selectValFrm}"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="@*" name="init-attribute-value">
        <xsl:attribute name="{name()}">
            <xsl:value-of select="$xml-input-data/{how to resolve attribute value}"/>
        </xsl:attribute>
    </xsl:template>

</xsl:stylesheet>

not working, any idea how to achieve this result?

Upvotes: 1

Views: 187

Answers (2)

michael.hor257k
michael.hor257k

Reputation: 117073

If - as it seems - you are using Xalan as your XSLT processor, you can use the EXSLT dyn:evaluate() extension function to evaluate the given strings as XPath expressions. Try for example:

XSLT 1.0 + EXSLT

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

<xsl:param name="data-url" select="'data1.xml'"/>

<xsl:template match="node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:template match="@selectValFrm">
    <xsl:variable name="path" select="." />
    <xsl:for-each select="document($data-url)">
        <xsl:value-of select="dyn:evaluate($path)"/>
    </xsl:for-each>
</xsl:template>

<xsl:template match="@*">
    <xsl:attribute name="{name()}">
        <xsl:variable name="path" select="." />
        <xsl:for-each select="document($data-url)">
            <xsl:value-of select="dyn:evaluate($path)"/>
        </xsl:for-each>
    </xsl:attribute>
</xsl:template>

</xsl:stylesheet>

For this to actually produce the expected result, your input document (data2.xml) needs to be modified as explained by Martin Honnen in his answer.

Upvotes: 2

Martin Honnen
Martin Honnen

Reputation: 167706

If you can use Saxon 9.7 (probably 9.6 will do, too) PE or EE then you can use XSLT 3.0 and xsl:evaluate, here is an example:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  version="3.0">

    <xsl:param name="xml-input" as="xs:string" select="'test201512150101.xml'"/>
    <xsl:param name="xml-input-data" select="document($xml-input)" />

    <xsl:template match="node()|@*">        
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*[@selectValFrm]">
      <xsl:copy>
        <xsl:apply-templates select="@* except @selectValFrm"/>
        <xsl:value-of>
          <xsl:evaluate xpath="@selectValFrm" context-item="$xml-input-data"/>
        </xsl:value-of>
      </xsl:copy>
    </xsl:template>

    <xsl:template match="@num">
      <xsl:attribute name="{name()}" namespace="{namespace-uri()}">
        <xsl:evaluate xpath="." context-item="$xml-input-data"/>
      </xsl:attribute>
    </xsl:template>

</xsl:stylesheet>

If the input document is

<result>
    <ChildOneDesc selectValFrm="inputRoot/childOne"></ChildOneDesc>
    <ChildTwoDesc>
        <value num="inputRoot/childTwo/nestedChild/@id" selectValFrm="inputRoot/childTwo/nestedChild"></value>
    </ChildTwoDesc> 
</result>

(note I had to change the XPath values to start with inputRoot instead of root) and the parameter document is

<inputRoot>
    <childOne>
        I am first child
    </childOne>
    <childTwo>
        <nestedChild id="1">
            I am second child
        </nestedChild>
    </childTwo> 
</inputRoot>

then the result is

<result>
    <ChildOneDesc>
        I am first child
    </ChildOneDesc>
    <ChildTwoDesc>
        <value num="1">
            I am second child
        </value>
    </ChildTwoDesc>
</result>

Other XSLT processors might provide an extension function or instruction to use XPath evaluation, we need to know which processor you use, the use of as="xs:string" suggests you are using an XSLT 2.0 processor.

Upvotes: 1

Related Questions