I have the following XML source file:
<?xml version = "1.0" encoding = "UTF-8"?>
<Master revision="B" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Data>
<Block name="PRIMARY" >
<BlockGroup name="GROUP_PRIMARY" >
<BlockLayer Name="COVER" sequence="1">
<BlockRef id="BLOCK_COVER"/>
</BlockLayer>
<BlockLayer Name="TOP" sequence="2">
<BlockRef id="BLOCK_TOP"/>
</BlockLayer>
<BlockLayer Name="INDEX_3" sequence="3">
<BlockRef id="BLOCK_INDEX_3"/>
</BlockLayer>
<BlockLayer Name="INT2" sequence="4">
<BlockRef id="BLOCK_INT2"/>
</BlockLayer>
</BlockGroup>
</Block>
<Block name="SECONDARY" >
<BlockGroup name="GROUP_SECONDARY" >
<BlockLayer Name="BLOCK_TOP" sequence="5">
<BlockRef id="BLOCK_TOP"/>
</BlockLayer>
<BlockLayer Name="INT6" sequence="6">
<BlockRef id="BLOCK_INT6"/>
</BlockLayer>
<BlockLayer Name="INDEX_7" sequence="7">
<BlockRef id="BLOCK_INDEX_7"/>
</BlockLayer>
</BlockGroup>
</Block>
<Block name="TERTIARY">
<BlockGroup name="GROUP_TERTIARY">
<BlockLayer Name="COVER" sequence="1">
<BlockRef id="BLOCK_COVER"/>
</BlockLayer>
<BlockLayer Name="TOP" sequence="2">
<BlockRef id="BLOCK_TOP"/>
</BlockLayer>
<BlockLayer Name="INDEX_3" sequence="3">
<BlockRef id="BLOCK_INDEX_3"/>
</BlockLayer>
<BlockLayer Name="INT2" sequence="4">
<BlockRef id="BLOCK_INT2"/>
</BlockLayer>
</BlockGroup>
</Block>
</Data>
</Master>
I need to create an output file that consists of three groups each containing a unique set of all elements across the three input groups, like so:
<?xml version="1.0" encoding="UTF-8"?>
<Blocks>
<Block Name="PRIMARY">
<BlockNumber>1</BlockNumber>
<BlockCollection>
<BlockLayer Name="COVER" Order="1">
<Display>True</Display>
</BlockLayer>
<BlockLayer Name="TOP" Order="2">
<Display>True</Display>
</BlockLayer>
<BlockLayer Name="INDEX_3" Order="3">
<Display>True</Display>
</BlockLayer>
<BlockLayer Name="INT2" Order="4">
<Display>True</Display>
</BlockLayer>
<BlockLayer Name="BLOCK_TOP" Order="5">
<Display>False</Display>
</BlockLayer>
<BlockLayer Name="INT6" Order="6">
<Display>False</Display>
</BlockLayer>
<BlockLayer Name="INDEX_7" Order="7">
<Display>False</Display>
</BlockLayer>
</BlockCollection>
</Block>
<Block Name="SECONDARY">
<BlockNumber>2</BlockNumber>
<BlockCollection>
<BlockLayer Name="COVER" Order="1">
<Display>False</Display>
</BlockLayer>
<BlockLayer Name="TOP" Order="2">
<Display>False</Display>
</BlockLayer>
<BlockLayer Name="INDEX_3" Order="3">
<Display>False</Display>
</BlockLayer>
<BlockLayer Name="INT2" Order="4">
<Display>False</Display>
</BlockLayer>
<BlockLayer Name="BLOCK_TOP" Order="5">
<Display>True</Display>
</BlockLayer>
<BlockLayer Name="INT6" Order="6">
<Display>True</Display>
</BlockLayer>
<BlockLayer Name="INDEX_7" Order="7">
<Display>True</Display>
</BlockLayer>
</BlockCollection>
</Block>
<Block Name="TERTIARY">
<BlockNumber>3</BlockNumber>
<BlockCollection>
<BlockLayer Name="COVER" Order="1">
<Display>True</Display>
</BlockLayer>
<BlockLayer Name="TOP" Order="2">
<Display>True</Display>
</BlockLayer>
<BlockLayer Name="INDEX_3" Order="3">
<Display>True</Display>
</BlockLayer>
<BlockLayer Name="INT2" Order="4">
<Display>True</Display>
</BlockLayer>
<BlockLayer Name="BLOCK_TOP" Order="5">
<Display>False</Display>
</BlockLayer>
<BlockLayer Name="INT6" Order="6">
<Display>False</Display>
</BlockLayer>
<BlockLayer Name="INDEX_7" Order="7">
<Display>False</Display>
</BlockLayer>
</BlockCollection>
</Block>
</Blocks>
I am using the following stylesheet file:
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<Blocks>
<xsl:apply-templates select="/Master/Data/Block" mode="block"/>
</Blocks>
</xsl:template>
<xsl:template match="/Master/Data/Block" mode="block">
<Block Name="{@name}" >
<BlockNumber><xsl:value-of select="position()" /></BlockNumber>
<BlockCollection>
<xsl:apply-templates select="/Master/Data/Block/BlockGroup/BlockLayer[not(@sequence = preceding::*/@sequence)]">
<xsl:sort select="@sequence" data-type="number"/>
</xsl:apply-templates>
</BlockCollection>
</Block>
</xsl:template>
<xsl:template match="/Master/Data/Block/BlockGroup/BlockLayer">
<BlockLayer Name="{@Name}" Order="{@sequence}">
<Display>
<xsl:value-of select="if(count(../../preceding-sibling::Block) = 0) then 'True' else 'False'"/>
</Display>
</BlockLayer>
</xsl:template>
</xsl:stylesheet>
I need the Display element in each output BlockLayer element to display True or False to indicate which of the three input groups it belongs to. So BlockLayers 1, 2, 3, 4 will display True and BlockLayers 5, 6, 7 will display False for the first and third groups.
For the second group, BlockLayers 1, 2, 3, 4 must display False and 5, 6, 7 must display True.
My first attempt uses the expression:
<xsl:value-of select="if(count(../../preceding-sibling::Block) = 0) then 'True' else 'False'"/>
The context is at the BlockLayer level and I need the grandparent of each BlockLayer. This expression clearly can only display the BlockLayers in the first group. How can I check for the presence of a BlockLayer in each input group?
(Hope that's not too much detail...)
Best regards,
Ralph Bryce
I think it is better to use for-each-group
on @sequence
instead of the comparison on preceding
elements, then you also have the group and can compare whether it intersects with the elements in the current Block
:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<Blocks>
<xsl:apply-templates select="/Master/Data/Block"/>
</Blocks>
</xsl:template>
<xsl:template match="/Master/Data/Block">
<Block Name="{@name}">
<xsl:variable name="Block" select="."/>
<BlockNumber><xsl:value-of select="position()" /></BlockNumber>
<BlockCollection>
<xsl:for-each-group select="/Master/Data/Block/BlockGroup/BlockLayer" group-by="xs:integer(@sequence)">
<xsl:sort select="current-grouping-key()"/>
<xsl:apply-templates select=".">
<xsl:with-param name="Display" select="boolean(current-group() intersect $Block/BlockGroup/BlockLayer)"/>
</xsl:apply-templates>
</xsl:for-each-group>
</BlockCollection>
</Block>
</xsl:template>
<xsl:template match="/Master/Data/Block/BlockGroup/BlockLayer">
<xsl:param name="Display"/>
<BlockLayer Name="{@Name}" Order="{@sequence}">
<Display>
<xsl:value-of select="$Display"/>
</Display>
</BlockLayer>
</xsl:template>
</xsl:stylesheet>
Online at http://xsltransform.net/93dEHFC, task left is to use True
/False
instead of true
/false
.