Reputation: 3748
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
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
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