Reputation: 3
Let's say we have 2 XML files a.xml:
<Reports>
<Fields>
<PositionID>000101</PositionID>
<Date>2021-04-19</Date>
<Name>XXX</Name>
</Fields>
<Fields>
<PositionID>000100</PositionID>
<Date>2021-04-19</Date>
<Name></Name>
</Fields>
</Reports>
and b.xml:
<AOID>
<Employee>
<Name>YYY</Name>
<PositionID>000100</PositionID>
</Employee>
<Employee>
<Name>XXX</Name>
<PositionID>000101</PositionID>
</Employee>
</AOID>
I'm looking for a v1 xslt that would join the two extracts by PositionID and add the Name field from xml b if it's empty or not existing in XML a.
The problem I have is that none of these are on the input instead I have them in a variable like below:
<xsl:variable name="ExtractA" select="document('a.xml')" /> <xsl:variable name="ExtractB" select="document('b.xml')" />
Unfortunately, i cannot make it work in xslt v1 but I would need it because of some limitations of the platform I use.
I have a working example in v3 xslt for the test, so I would need the same functionality in v1:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" omit-xml-declaration="yes" />
<xsl:variable name="ExtractA">
<Reports>
<Fields>
<PositionID>000101</PositionID>
<Date>2021-04-19</Date>
<Name></Name>
</Fields>
<Fields>
<PositionID>000100</PositionID>
<Date>2021-04-19</Date>
<Name></Name>
</Fields>
</Reports>
</xsl:variable>
<xsl:variable name="ExtractB">
<AOID>
<Employee>
<Name>YYY</Name>
<PositionID>000100</PositionID>
</Employee>
<Employee>
<Name>XXX</Name>
<PositionID>000101</PositionID>
</Employee>
</AOID>
</xsl:variable>
<xsl:template match="/">
<xsl:if test="$ExtractA/Reports[1]/Fields[1]/Name != ''">
<xsl:copy-of select="$ExtractA" />
</xsl:if>
<xsl:if test="$ExtractA/Reports[1]/Fields[1]/Name = ''">
<Reports>
<xsl:for-each-group select="$ExtractA/Reports/Fields, $ExtractB/AOID/Employee" group-by="PositionID">
<xsl:copy>
<xsl:copy-of select="current-group()[1]/Date, PositionID, current-group()[2]/Name"/>
</xsl:copy>
</xsl:for-each-group>
</Reports>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Upvotes: 0
Views: 52
Reputation: 29022
You can do this better without the xsl:key
instruction:
<xsl:variable name="ExtractA" select="document('a.xml')" />
<xsl:variable name="ExtractB" select="document('b.xml')" />
<xsl:template match="/">
<Reports>
<xsl:for-each select="$ExtractA/Reports/Fields">
<Fields>
<xsl:variable name="curItemID" select="PositionID" />
<xsl:variable name="secItem" select="$ExtractB/AOID/Employee[PositionID=$curItemID]" />
<xsl:choose>
<xsl:when test="not(normalize-space(Name))">
<xsl:copy-of select="$secItem/Name" />
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="Name" />
</xsl:otherwise>
</xsl:choose>
<xsl:copy-of select="Date" />
<xsl:copy-of select="PositionID" />
</Fields>
</xsl:for-each>
</Reports>
</xsl:template>
The expression not(normalize-space(Name))
matches either empty strings or not-present elements.
The output for your sample is
<Reports>
<Fields>
<Name>XXX</Name>
<Date>2021-04-19</Date>
<PositionID>000101</PositionID>
</Fields>
<Fields>
<Name>YYY</Name>
<Date>2021-04-19</Date>
<PositionID>000100</PositionID>
</Fields>
</Reports>
The downside of this approach is, that the output only contains items from file A. If file B contains more items, they'd be ignored.
Upvotes: 0
Reputation: 116957
Try something along the lines of:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<Reports>
<xsl:for-each select="document('a.xml')/Reports/Fields">
<xsl:copy>
<xsl:copy-of select="PositionID | Date"/>
<xsl:choose>
<xsl:when test="Name/text()">
<xsl:copy-of select="Name"/>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="document('b.xml')/AOID/Employee[PositionID=current()/PositionID]/Name"/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:for-each>
</Reports>
</xsl:template>
</xsl:stylesheet>
When this is applied to any XML input, the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<Reports>
<Fields>
<PositionID>000101</PositionID>
<Date>2021-04-19</Date>
<Name>XXX</Name>
</Fields>
<Fields>
<PositionID>000100</PositionID>
<Date>2021-04-19</Date>
<Name>YYY</Name>
</Fields>
</Reports>
provided that the two documents, a.xml
and b.xml
, are in the same location as the XSL stylesheet.
A more efficient solution would use a key to get the corresponding Name
, but this is more difficult to do in XSLT 1.0 - see, for example, https://stackoverflow.com/a/34663060/3016153.
Upvotes: 0