How to group a specific number of blocks in an xml based on number of tags - xslt-1.0

I have a big xml like below and I would like to group a specific number of tags under one block; The expected input and output below will make my question clearer. Any help is greatly appreciated
The input file is
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<Root>
<ListABC>
<ABC name="name1" class="class1" age="age1" />
<ABC name="name2" class="class2" age="age2" />
<ABC name="name3" class="class3" age="age3" />
<ABC name="name4" class="class4" age="age4" />
<ABC name="name5" class="class5" age="age5" />
</ListABC>
<ListABC>
<EOF tag1="1" tag2="2" tag3="3"/>
</ListABC>
</Root>
I need to create a tag ListABC after every 2 ABC elements and at the same time, the last ListABC which contains EOF element should not be impacted at all. This is how I need the output
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<Root>
<ListABC>
<ABC name="name1" class="class1" age="age1" />
<ABC name="name2" class="class2" age="age2" />
</ListABC>
<ListABC>
<ABC name="name3" class="class3" age="age3" />
<ABC name="name4" class="class4" age="age4" />
</ListABC>
<ListABC>
<ABC name="name5" class="class5" age="age5" />
</ListABC>
<ListABC>
<EOF tag1="1" tag2="2" tag3="3"/>
</ListABC>
</Root>
Thanks much!

How about:
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:strip-space elements="*"/>
<xsl:template match="/Root">
<xsl:copy>
<xsl:for-each select="ListABC[not (EOF)]/ABC[position() mod 2 = 1]">
<ListABC>
<xsl:copy-of select=". | following-sibling::ABC[1]"/>
</ListABC>
</xsl:for-each>
<xsl:copy-of select="ListABC[EOF]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

Remove both duplicate records using XSLT

I am trying to remove both the duplicate records in an XML
I already can remove the second occurrence but I need to remove both records in this case.
This is the XSLT mapping that I have
<?xml version="1.0" encoding="UTF-8"?>
<xsl:transform version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="item">
<xsl:copy-of select="." />
</xsl:template>
<xsl:template match="/ZTABLE/Record">
<ZTABLE>
<Record>
<xsl:apply-templates select="item[not(ID=preceding-sibling::item/ID)]" />
</Record>
</ZTABLE>
</xsl:template>
</xsl:transform>
The input XML is:
<ZTABLE>
<Record>
<item>
<ID>400400</ID>
</item>
<item>
<ID>100100</ID>
</item>
<item>
<ID>200200</ID>
</item>
<item>
<ID>300300</ID>
</item>
<item>
<ID>400400</ID>
</item>
</Record>
</ZTABLE>
The expected output is
<ZTABLE>
<Record>
<item>
<ID>100100</ID>
</item>
<item>
<ID>200200</ID>
</item>
<item>
<ID>300300</ID>
</Record>
</ZTABLE>
Try it this way:
<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:strip-space elements="*"/>
<xsl:key name="item" match="item" use="ID" />
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="item[count(key('item', ID)) > 1]"/>
</xsl:stylesheet>
For explanation, read about Muenchian grouping.

Based on the object corresponding values should be taken using xslt

INPUT XML
<root>
<file1>
<commodity>
<units>1</units>
<obj>mango</obj>
</commodity>
<commodity>
<units>5</units>
<obj>guava</obj>
</commodity>
</file1>
<file2>
<category>
<object>guava</object>
<type>CAT1</type>
<colour>green</colour>
</category>
<category>
<object>mango</object>
<type>CAT2</type>
<colour>yellow</colour>
</category>
</file2>
</root>
I need to compare the values of obj in file1 and object in file2 under root, if same I need to take their corresponding units, type and colour and produce the following output using xslt.
OUTPUT XML
<output>
<com>
<name>guava</name>
<num>5</num>
<category>CAT1</category>
<col>green</col>
</com>
<com>
<name>mango</name>
<num>1</num>
<category>CAT2</category>
<col>yellow</col>
</com>
</output>
I tried the below XSLT but the response is not as expected. Its not looping properly. Could you please tell me where I am going wrong.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8"
indent="yes" />
<xsl:key name="object-search" match="root/file1/commodity" use="obj" />
<xsl:template match="/">
<output>
<xsl:for-each select="key('object-search', //category/object)">
<com>
<name>
<xsl:value-of select="obj" />
</name>
<num>
<xsl:value-of select="units" />
</num>
<category>
<xsl:value-of
select="//root/file2/category/type" />
</category>
<col>
<xsl:value-of
select="//root/file2/category/colour" />
</col>
</com>
</xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet>
Try it this way:
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:key name="cat" match="category" use="object" />
<xsl:template match="/root">
<output>
<xsl:for-each select="file1/commodity">
<com>
<name>
<xsl:value-of select="obj" />
</name>
<num>
<xsl:value-of select="units" />
</num>
<xsl:variable name="cat" select="key('cat', obj)" />
<category>
<xsl:value-of select="$cat/type" />
</category>
<col>
<xsl:value-of select="$cat/colour" />
</col>
</com>
</xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet>
Note that the result is slightly different from what you posted:
<?xml version="1.0" encoding="UTF-8"?>
<output>
<com>
<name>mango</name>
<num>1</num>
<category>CAT2</category>
<col>yellow</col>
</com>
<com>
<name>guava</name>
<num>5</num>
<category>CAT1</category>
<col>green</col>
</com>
</output>
Alternatively, you could do:
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:key name="com" match="commodity" use="obj" />
<xsl:template match="/root">
<output>
<xsl:for-each select="file2/category">
<xsl:variable name="com" select="key('com', object)" />
<com>
<name>
<xsl:value-of select="$com/obj" />
</name>
<num>
<xsl:value-of select="$com/units" />
</num>
<category>
<xsl:value-of select="type" />
</category>
<col>
<xsl:value-of select="colour" />
</col>
</com>
</xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet>
and get:
<?xml version="1.0" encoding="UTF-8"?>
<output>
<com>
<name>guava</name>
<num>5</num>
<category>CAT1</category>
<col>green</col>
</com>
<com>
<name>mango</name>
<num>1</num>
<category>CAT2</category>
<col>yellow</col>
</com>
</output>

Filter keys out of response

I have an xslt sheet in which I have 2 response objects. $response1 contains a list of ids something like:
<response>
<idlist>
<id>1</id>
<id>2</id>
</idlist>
</response>
And $response2 contains a number of objects:
<response2>
<obj id="1" name="obj1"/>
<obj id="2" name="obj2"/>
<obj id="3" name="obj3"/>
<obj id="4" name="obj4"/>
</response2>
I want to make a copy of response2 but filtering out any objects where the id matches thos contained in response 1
<xsl:variable name="copy">
<xsl:copy-of select="$response2/*[not contains($response1, id)]"/>
</xsl:variable>
any ideas greatly appreciated
C
Given a well-formed input such as:
<root>
<response>
<idlist>
<id>1</id>
<id>2</id>
</idlist>
</response>
<response2>
<obj id="1" name="obj1"/>
<obj id="2" name="obj2"/>
<obj id="3" name="obj3"/>
<obj id="4" name="obj4"/>
</response2>
</root>
the following stylesheet:
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="/root">
<xsl:variable name="ids" select="response/idlist/id" />
<output>
<xsl:copy-of select="response2/obj[not(#id=$ids)]"/>
</output>
</xsl:template>
</xsl:stylesheet>
will return:
<?xml version="1.0" encoding="UTF-8"?>
<output>
<obj id="3" name="obj3"/>
<obj id="4" name="obj4"/>
</output>
A better solution is to use a key to link the nodes by matching id:
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:key name="id" match="id" use="." />
<xsl:template match="/root">
<output>
<xsl:copy-of select="response2/obj[not(key('id', #id))]"/>
</output>
</xsl:template>
</xsl:stylesheet>

Counting child notes in an document with multiple namespaces (XBRL)

I am trying (but not succeeding) to count the children in an XBRL document.
I want to know how many schemas, unit, contexts and facts grouped by namespace prefix are used.
Input:
<?xml version="1.0" encoding="utf-8"?>
<xbrl xml:lang="en" xmlns="http://www.xbrl.org/2003/instance"
xmlns:link="http://www.xbrl.org/2003/linkbase"
xmlns:find="http://www.eurofiling.info/xbrl/ext/filing-indicators"
xmlns:xlink="http://www.w3.org/1999/xlink" >
<link:schemaRef xlink:type="simple" xlink:href="http://www.eba.europa.eu/eu/fr/xbrl/crr/fws/corep/its-2013-02/2014-03-31/mod/corep_le_con.xsd" />
<context id="I-2014-9-E">
<entity>
<identifier scheme="http://www.dnb.nl/id">123</identifier>
</entity>
<period>
<instant>2014-09-30</instant>
</period>
</context>
<unit id="u-EUR">
<measure>iso4217:EUR</measure>
</unit>
<unit id="u-pure">
<measure>pure</measure>
</unit>
<find:fIndicators>
<find:filingIndicator contextRef="I-2014-9-E">C_00.01</find:filingIndicator>
</find:fIndicators>
<find:fIndicators>
<find:filingIndicator contextRef="I-2014-9-E">C_26.00</find:filingIndicator>
</find:fIndicators>
<find:fIndicators>
<find:filingIndicator contextRef="I-2014-9-E">C_27.00</find:filingIndicator>
</find:fIndicators>
</xbrl>
Wanted output:
<?xml version="1.0" encoding="utf-8"?>
<XBRLfacts xmlns="http://www.xbrl.org/2003/instance" xmlns:link="http://www.xbrl.org/2003/linkbase">
<linkCount>1</linkCount>
<unitCount>2</unitCount>
<contextCount></contextCount>
<factCount>
<find>3</find>
</factCount>
</XBRLfacts>
XSLT tried:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.xbrl.org/2003/instance" xmlns:link="http://www.xbrl.org/2003/linkbase" >
<xsl:output method="xml" encoding="UTF-8" indent="yes" media-type="text/xml" />
<xsl:template match="/">
<XBRLfacts >
<linkCount>
<xsl:value-of select="//xbrl/link:schemaRef" />
</linkCount>
<unitCount>
<xsl:value-of select="//xbrl/unit" />
</unitCount>
<contextCount>
<xsl:value-of select="//xbrl/context" />
</contextCount>
<!-- something for the facts -->
</XBRLfacts>
</xsl:template>
</xsl:stylesheet>
Output gotten:
<?xml version="1.0" encoding="utf-8"?>
<XBRLfacts xmlns="http://www.xbrl.org/2003/instance" xmlns:link="http://www.xbrl.org/2003/linkbase">
<linkCount></linkCount>
<unitCount></unitCount>
<contextCount></contextCount>
</XBRLfacts>
Any help telling me what I am doing wrong is greatly appreciated.
Thanks.
Paul.
Your source elements are in namespaces. You must assign a prefix to each namespace and use it when addressing the elements in that namespace.
The other thing is that you're not actually counting anything.
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.xbrl.org/2003/instance"
xmlns:xbrl="http://www.xbrl.org/2003/instance"
xmlns:link="http://www.xbrl.org/2003/linkbase"
xmlns:find="http://www.eurofiling.info/xbrl/ext/filing-indicators"
exclude-result-prefixes="xbrl find">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/xbrl:xbrl">
<XBRLfacts>
<linkCount>
<xsl:value-of select="count(link:schemaRef)" />
</linkCount>
<unitCount>
<xsl:value-of select="count(xbrl:unit)" />
</unitCount>
<contextCount>
<xsl:value-of select="count(xbrl:context)" />
</contextCount>
<fIndicatorCount>
<xsl:value-of select="count(find:fIndicators)" />
</fIndicatorCount>
</XBRLfacts>
</xsl:template>
</xsl:stylesheet>
Result:
<?xml version="1.0" encoding="UTF-8"?>
<XBRLfacts xmlns="http://www.xbrl.org/2003/instance" xmlns:link="http://www.xbrl.org/2003/linkbase">
<linkCount>1</linkCount>
<unitCount>2</unitCount>
<contextCount>1</contextCount>
<fIndicatorCount>3</fIndicatorCount>
</XBRLfacts>

Filter xml in .Net with param

I am trying to filter an xml file in a .Net application I am developing. Some sample xml below, obviously not the proper xml, but near enough :-)
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Row A1="1" A2="AMS">
<Name>Ashley</Name>
<Team>Team B</Team>
<Date>3/25/2012</Date>
<Value>511681.15</Value>
</Row>
<Row A1="2" A2="AMS">
<Name>Kylie</Name>
<Team>Team A</Team>
<Date>9/28/2010</Date>
<Value>408438.47</Value>
</Row>
<Row A1="3" A2="AMS">
<Name>Gianna</Name>
<Team>Team B</Team>
<Date>40004</Date>
<Value>109709.22</Value>
</Row>
<Row A1="4" A2="AMS">
<Name>Chase</Name>
<Team>Team F</Team>
<Date>40152</Date>
<Value>279018.79</Value>
</Row>
The stylesheet has a param that is set by XsltArgumentList in the application. The param is passed into the stylesheet, but does not filter the xml. I have tried using the ms node-set and exsl node-set but only get the top level root returned. Stylesheet below:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="xml" indent="yes" encoding="UTF-8" />
<xsl:decimal-format name="NN" NaN="0" />
<xsl:param name="Filter" />
<xsl:template match="/">
<Root>
<xsl:for-each select="exsl:node-set($Filter)">
<Row>
<xsl:attribute name="A1">
<xsl:value-of select="#A1" />
</xsl:attribute>
<xsl:attribute name="A2">
<xsl:value-of select="#A2" />
</xsl:attribute>
<Team>
<xsl:value-of select="Team"/>
</Team>
</Row>
</xsl:for-each>
</Root>
</xsl:template>
The filter i am trying to pass could be using any of the combination of the xml elements. The filter is am currently attempting is below
<xsl:param name="Filter" select="//Row[Team='Team A']" />
But this is just returns
<?xml version="1.0" encoding="utf-8"?>
<Root>
<Row A1="" A2="">
<Team></Team>
</Row>
</Root>
Any help or pointers would be appreciated!
Thanks
This will not woke like you tried it, because you can't have a xpath expression in an XLST parameter or variable. You can use variable or parameter in please of const values.
Therefore you can use something like
<xsl:param name="teamFilter" select="'Team A'" />
...
<xsl:for-each select="//Row[Team='$teamFilter']">
Or if you have to filter for different nodes you may try:
<xsl:param name="filterValue" select="'Team A'" />
<xsl:param name="filterNode" select="'Team'" />
...
<xsl:for-each select="//Row[*[name()= $filterNode and . =$filterValue]]">
Other alternatives would be:
Patch the XSLT before loading it
Use xpath iterator with XPathExpression from .NET.