xslt 1.0 combinate two segments - xslt-1.0

how to combinate two or more segments to one segment at xslt 1.0?
I have two cases.
Case1:
If "QUALIFIER" at GRP/TXT is the same (for example: AAA) combinate this to one.
Correct:
QUALIFIER: AAA
TEXT: Test AAA rtetertertret
Case2:
Same should be at GRP/ITEM/TXT (for example: LIN)
Correct:
QUALIFIER: LIN
TEXT: Test LIN sdfsdfsfsf
<?xml version="1.0"?>
<SEEDELFOR>
<Test/>
<CNT>
<TRANSMISSION_DATE></TRANSMISSION_DATE>
<TRANSMISSION_TIME></TRANSMISSION_TIME>
<INTERCHANGE_CONTROL_NUMBER></INTERCHANGE_CONTROL_NUMBER>
<SENDER></SENDER>
<SENDER_QUALIFIER></SENDER_QUALIFIER>
<RECEIVER></RECEIVER>
<RECEIVER_QUALIFIER></RECEIVER_QUALIFIER>
<SYNTAX_IDENTIFIER></SYNTAX_IDENTIFIER>
<SYNTAX_VERSION></SYNTAX_VERSION>
<BGM></BGM>
<GRP>
<IDENTIFIER_BY></IDENTIFIER_BY>
<IDENTIFIER_SU></IDENTIFIER_SU>
<DATE_4></DATE_4>
<REF_ON></REF_ON>
<ADD>
<QUALIFIER></QUALIFIER>
<IDENTIFIER></IDENTIFIER>
<AGENCY_CODE></AGENCY_CODE>
<CONTACT>
<QUALIFIER></QUALIFIER>
<NUMBER></NUMBER>
</CONTACT>
<CONTACT>
<QUALIFIER></QUALIFIER>
<NUMBER></NUMBER>
</CONTACT>
<CONTACT>
<QUALIFIER></QUALIFIER>
<NUMBER></NUMBER>
</CONTACT>
</ADD>
<ADD>
<QUALIFIER></QUALIFIER>
<IDENTIFIER></IDENTIFIER>
<AGENCY_CODE></AGENCY_CODE>
</ADD>
<TXT>
<QUALIFIER>AAA</QUALIFIER>
<TEXT>Test AAA</TEXT>
</TXT>
<TXT>
<QUALIFIER>AAA</QUALIFIER>
<TEXT>rtetertertret</TEXT>
</TXT>
<TRANSPORT_DETAILS>
<ADDITIONAL_DETAILS>
<QUALIFIER></QUALIFIER>
<DETAILS></DETAILS>
</ADDITIONAL_DETAILS>
</TRANSPORT_DETAILS>
<TRANSPORT_DETAILS>
<ADDITIONAL_DETAILS>
<QUALIFIER></QUALIFIER>
<DETAILS></DETAILS>
</ADDITIONAL_DETAILS>
</TRANSPORT_DETAILS>
<TRANSPORT_DETAILS>
<ADDITIONAL_DETAILS>
<QUALIFIER></QUALIFIER>
<DETAILS></DETAILS>
</ADDITIONAL_DETAILS>
</TRANSPORT_DETAILS>
<ITEM>
<ITEM_NUMBER_SA></ITEM_NUMBER_SA>
<QUANTITY></QUANTITY>
<QUANTITY_UNIT></QUANTITY_UNIT>
<LINE_ITEM_NUMBER>2</LINE_ITEM_NUMBER>
<TXT>
<QUALIFIER>LIN</QUALIFIER>
<TEXT>Test LIN</TEXT>
</TXT>
</ITEM>
<ITEM>
<ITEM_NUMBER_SA></ITEM_NUMBER_SA>
<QUANTITY></QUANTITY>
<QUANTITY_UNIT></QUANTITY_UNIT>
<LINE_ITEM_NUMBER>1</LINE_ITEM_NUMBER>
<TXT>
<QUALIFIER>LIN</QUALIFIER>
<TEXT>Test LIN</TEXT>
</TXT>
<TXT>
<QUALIFIER>LIN</QUALIFIER>
<TEXT>sdfsdfsfsf</TEXT>
</TXT>
</ITEM>
</GRP>
</CNT>
</SEEDELFOR>
Correct output should be:
<?xml version="1.0"?>
<SEEDELFOR>
<Test/>
<CNT>
<TRANSMISSION_DATE></TRANSMISSION_DATE>
<TRANSMISSION_TIME></TRANSMISSION_TIME>
<INTERCHANGE_CONTROL_NUMBER></INTERCHANGE_CONTROL_NUMBER>
<SENDER></SENDER>
<SENDER_QUALIFIER></SENDER_QUALIFIER>
<RECEIVER></RECEIVER>
<RECEIVER_QUALIFIER></RECEIVER_QUALIFIER>
<SYNTAX_IDENTIFIER></SYNTAX_IDENTIFIER>
<SYNTAX_VERSION></SYNTAX_VERSION>
<BGM></BGM>
<GRP>
<IDENTIFIER_BY></IDENTIFIER_BY>
<IDENTIFIER_SU></IDENTIFIER_SU>
<DATE_4></DATE_4>
<REF_ON></REF_ON>
<ADD>
<QUALIFIER></QUALIFIER>
<IDENTIFIER></IDENTIFIER>
<AGENCY_CODE></AGENCY_CODE>
<CONTACT>
<QUALIFIER></QUALIFIER>
<NUMBER></NUMBER>
</CONTACT>
<CONTACT>
<QUALIFIER></QUALIFIER>
<NUMBER></NUMBER>
</CONTACT>
<CONTACT>
<QUALIFIER></QUALIFIER>
<NUMBER></NUMBER>
</CONTACT>
</ADD>
<ADD>
<QUALIFIER></QUALIFIER>
<IDENTIFIER></IDENTIFIER>
<AGENCY_CODE></AGENCY_CODE>
</ADD>
<TXT>
<QUALIFIER>AAA</QUALIFIER>
<TEXT>Test AAA rtetertertret</TEXT>
</TXT>
<TRANSPORT_DETAILS>
<ADDITIONAL_DETAILS>
<QUALIFIER></QUALIFIER>
<DETAILS></DETAILS>
</ADDITIONAL_DETAILS>
</TRANSPORT_DETAILS>
<TRANSPORT_DETAILS>
<ADDITIONAL_DETAILS>
<QUALIFIER></QUALIFIER>
<DETAILS></DETAILS>
</ADDITIONAL_DETAILS>
</TRANSPORT_DETAILS>
<TRANSPORT_DETAILS>
<ADDITIONAL_DETAILS>
<QUALIFIER></QUALIFIER>
<DETAILS></DETAILS>
</ADDITIONAL_DETAILS>
</TRANSPORT_DETAILS>
<ITEM>
<ITEM_NUMBER_SA></ITEM_NUMBER_SA>
<QUANTITY></QUANTITY>
<QUANTITY_UNIT></QUANTITY_UNIT>
<LINE_ITEM_NUMBER>2</LINE_ITEM_NUMBER>
<TXT>
<QUALIFIER>LIN</QUALIFIER>
<TEXT>Test LIN</TEXT>
</TXT>
</ITEM>
<ITEM>
<ITEM_NUMBER_SA></ITEM_NUMBER_SA>
<QUANTITY></QUANTITY>
<QUANTITY_UNIT></QUANTITY_UNIT>
<LINE_ITEM_NUMBER>1</LINE_ITEM_NUMBER>
<TXT>
<QUALIFIER>LIN</QUALIFIER>
<TEXT>Test LIN sdfsdfsfsf</TEXT>
</TXT>
</ITEM>
</GRP>
</CNT>
</SEEDELFOR>
Best regards
Julian

It can be achievable as following in XSLT 1.0:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:key name="qualifierKey" match="/SEEDELFOR/CNT/GRP//TXT" use="QUALIFIER" />
<xsl:key name="qualifierTextKey" match="/SEEDELFOR/CNT/GRP//TXT" use="concat(QUALIFIER, '|', TEXT)" />
<xsl:key name="itemTxtKey" match="/SEEDELFOR/CNT/GRP/ITEM/TXT" use="concat(generate-id(parent::*), QUALIFIER, '|', TEXT)" />
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*" />
</xsl:copy>
</xsl:template>
<xsl:template match="TXT[position() > 1]" />
<xsl:template match="/SEEDELFOR/CNT/GRP/ITEM/TXT[following-sibling::*[1]][generate-id(.) = generate-id(key('itemTxtKey', concat(generate-id(parent::*), QUALIFIER, '|', TEXT))[1])]
| /SEEDELFOR/CNT/GRP/TXT[following-sibling::*[1]][generate-id() = generate-id(key('qualifierKey',QUALIFIER)[1])]">
<TXT>
<QUALIFIER>
<xsl:value-of select="normalize-space(QUALIFIER)" />
</QUALIFIER>
<TEXT>
<xsl:variable name="count" select="count(key('qualifierKey',QUALIFIER)[generate-id() = generate-id(key('qualifierTextKey', concat(QUALIFIER, '|', TEXT))[1])])" />
<xsl:for-each select="key('qualifierKey',QUALIFIER)[generate-id() = generate-id(key('qualifierTextKey', concat(QUALIFIER, '|', TEXT))[1])]">
<xsl:value-of select="normalize-space(TEXT)" />
<xsl:if test="$count != position()"><xsl:value-of select="' '"></xsl:value-of></xsl:if>
</xsl:for-each>
</TEXT>
</TXT>
</xsl:template>
</xsl:stylesheet>
See output here: http://xsltfiddle.liberty-development.net/jyRYYib/2

Related

Find distinct values across each node in xslt1.0

I want to find distinct values across each node. but when i use muenchian method it will select distinct values only at top level.
Currently i am getting output as:
Bag 20
Tray 30
But i want to group type and quantity for each item
<!--for 1st item-->
Bag 20
Tray 30
<!--for 2nd item-->
Bag 20
Box 20
Tray 30
I am doing some research to make it work but did not succeed.
XML:
<?xml version="1.0" encoding="UTF-8"?>
<Test>
<Items>
<Item>
<name>A</name>
<type>Bag</type>
<input quantity="20"/>
</Item>
<Item>
<name>A</name>
<type>Bag</type>
<input quantity="20"/>
</Item>
<Item>
<name>B</name>
<type>Metal</type>
<input quantity="20"/>
</Item>
<Item>
<name>A</name>
<type>Tray</type>
<input quantity="30"/>
</Item>
</Items>
<Items>
<Item>
<name>A</name>
<type>Bag</type>
<input quantity="20"/>
</Item>
<Item>
<name>A</name>
<type>Box</type>
<input quantity="20"/>
</Item>
<Item>
<name>B</name>
<type>Metal</type>
<input quantity="20"/>
</Item>
<Item>
<name>A</name>
<type>Tray</type>
<input quantity="30"/>
</Item>
</Items>
</Test>
Code:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:date="http://exslt.org/dates-and-times"
xmlns:exsl="http://exslt.org/common" exclude-result-prefixes="exsl"
extension-element-prefixes="date"
>
<xsl:key name="item-key" match="Items/Item[name='A']" use="input/#quantity" />
<xsl:template match="/" name="Barcode">
<fo:root>
<fo:layout-master-set>
<fo:simple-page-master master-name="ManufacturLabelSize-first" page-height="297mm" page-width="210mm" margin-top="25.4mm" margin-right="25.4mm" margin-left="25.4mm" margin-bottom="25.4mm">
<fo:region-body margin-top="15mm" />
<fo:region-before />
<fo:region-after />
</fo:simple-page-master>
<fo:simple-page-master master-name="ManufacturLabelSize-rest" page-height="297mm" page-width="210mm" margin-top="25.4mm" margin-right="25.4mm" margin-left="25.4mm" margin-bottom="25.4mm">
<fo:region-body margin-top="15mm"/>
<fo:region-before />
<fo:region-after />
</fo:simple-page-master>
<fo:page-sequence-master master-name="ManufacturLabelSize-portrait">
<fo:repeatable-page-master-alternatives>
<fo:conditional-page-master-reference master-reference="ManufacturLabelSize-first"
page-position="first"/>
<fo:conditional-page-master-reference master-reference="ManufacturLabelSize-rest"
page-position="rest"/>
</fo:repeatable-page-master-alternatives>
</fo:page-sequence-master>
</fo:layout-master-set>
<fo:page-sequence master-reference="ManufacturLabelSize-portrait" id="pSeqID">
<fo:flow flow-name="xsl-region-body">
<fo:table >
<fo:table-body border="solid" border-width="0.5pt">
<fo:table-row>
<fo:table-cell>
<fo:block>
<xsl:for-each select="Test">
<xsl:for-each select="Items">
<xsl:for-each select="Item[name='A'][count(. | key('item-key',input/#quantity)[1])=1]">
<fo:block>
<xsl:value-of select="type"/> <fo:inline color="white">XXX</fo:inline><xsl:value-of select="input/#quantity"/>
</fo:block>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
Appreciate your help!
If - as it seems - you want to find distinct Items whose name is A by their type separately for each Items, then you need to include the parent Items' id in the key. Here's a simplified example:
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="k" match="Item[name='A']" use="concat(type, '|', generate-id(..))"/>
<xsl:template match="/Test">
<root>
<xsl:for-each select="Items">
<xsl:comment>
<xsl:value-of select="position()" />
</xsl:comment>
<xsl:for-each select="Item[name='A'][count(. | key('k', concat(type, '|', generate-id(..)))[1]) = 1]">
<item>
<type>
<xsl:value-of select="type"/>
</type>
<quantity>
<xsl:value-of select="input/#quantity"/>
</quantity>
</item>
</xsl:for-each>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
Applied to your input example, the result will be:
Result
<?xml version="1.0" encoding="UTF-8"?>
<root>
<!--1-->
<item>
<type>Bag</type>
<quantity>20</quantity>
</item>
<item>
<type>Tray</type>
<quantity>30</quantity>
</item>
<!--2-->
<item>
<type>Bag</type>
<quantity>20</quantity>
</item>
<item>
<type>Box</type>
<quantity>20</quantity>
</item>
<item>
<type>Tray</type>
<quantity>30</quantity>
</item>
</root>

Grouping XSLT elements by value (XSLT 1.0)

I am having a really hard time trying to group different elements by a common value using XSLT 1.0.
Using the following XML:
<root>
<segment>
<id>ABCD123</id>
</segment>
<segment>
<contact>
<field1>ABCD123</field1>
<field2>(111)345-7890</field2>
</contact>
</segment>
<segment>
<details>
<field1>ABCD123</field1>
<field5>More Details for ABCD123</field5>
</details>
</segment>
<segment>
<id>XZX098</id>
</segment>
<segment>
<contact>
<field1>XZX098</field1>
<field2>(111)443-9999</field2>
</contact>
</segment>
<segment>
<details>
<field1>XZX098</field1>
<field5>More Details for XZX098</field5>
</details>
</segment>
</root>
Transform into this:
<File>
<Record>
<id>ABCD123</id>
<phone>(111)345-7890</phone>
<details>More Details for ABCD123</details>
</Record>
<Record>
<id>XZX098</id>
<phone>(111)443-9999</phone>
<details>More Details for XZX098</details>
</Record>
</File>
I'm trying to group records by the 'id', and then get the contact, and details information that matches that 'id'.
Any help is greatly appreciated.
I don't see any grouping per se required here - just a simple lookup of cross-referenced data:
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="contact-details" match="contact|details" use="field1" />
<xsl:template match="root">
<File>
<xsl:for-each select="segment/id">
<Record>
<xsl:copy-of select="."/>
<phone>
<xsl:value-of select="key('contact-details', .)/field2"/>
</phone>
<details>
<xsl:value-of select="key('contact-details', .)/field5"/>
</details>
</Record>
</xsl:for-each>
</File>
</xsl:template>
</xsl:stylesheet>

Multiple groupings of XML nodes

I'm trying to group the input below by the destination and assortment values using muenchian-grouping which is new for me so I'm not sure how to do it properly. The input files will be much larger than this so performance is important.
<?xml version="1.0"?>
<ns0:Data xmlns:ns0="http://BizTalk_Projects.input">
<transports>
<destination>destination 1</destination>
<assortment>Volvo_GA961</assortment>
<quantity>10</quantity>
</transports>
<transports>
<destination>destination 1</destination>
<assortment>Volvo_GA961</assortment>
<quantity>15</quantity>
</transports>
<transports>
<destination>destination 1</destination>
<assortment>Volvo_GA969</assortment>
<quantity>15</quantity>
</transports>
<transports>
<destination>destination 1</destination>
<assortment>Volvo_GA972</assortment>
<quantity>5</quantity>
</transports>
<transports>
<destination>destination 1</destination>
<assortment>Volvo_SA980</assortment>
<quantity>20</quantity>
</transports>
<transports>
<destination>destination 2</destination>
<assortment>Volvo_GA960</assortment>
<quantity>10</quantity>
</transports>
<transports>
<destination>destination 1</destination>
<assortment>Nissan_GA963</assortment>
<quantity>5</quantity>
</transports>
<transports>
<destination>destination 1</destination>
<assortment>Nissan_GA963</assortment>
<quantity>5</quantity>
</transports>
</ns0:Data>
Expected output:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Destinations xmlns:ns0="http://BizTalk_Projects.output">
<Destination>
<name>destination 1</name>
<assortment>
<name>Volvo_GA</name>
<row>
<type>sumPerAssortment</type>
<id>961</id>
<totalQuantity>25</totalQuantity>
<region>1</region>
</row>
<row>
<type>sumPerAssortment</type>
<id>969</id>
<totalQuantity>15</totalQuantity>
<region>1</region>
</row>
<row>
<type>sumPerAssortment</type>
<id>972</id>
<totalQuantity>5</totalQuantity>
<region>2</region>
</row>
<row>
<type>sumPerRegion</type>
<id />
<totalQuantity>40</totalQuantity>
<region>1</region>
</row>
<row>
<type>sumPerRegion</type>
<id />
<totalQuantity>5</totalQuantity>
<region>2</region>
</row>
<row>
<type>totalSum</type>
<id />
<totalQuantity>45</totalQuantity>
<region />
</row>
</assortment>
<assortment>
<name>Volvo_SA</name>
<row>
<type>sumPerAssortment</type>
<id>980</id>
<totalQuantity>20</totalQuantity>
<region>3</region>
</row>
<row>
<type>sumPerRegion</type>
<id />
<totalQuantity>20</totalQuantity>
<region>3</region>
</row>
<row>
<type>totalSum</type>
<id />
<totalQuantity>20</totalQuantity>
<region />
</row>
</assortment>
<assortment>
<name>Nissan_GA</name>
<row>
<type>sumPerAssortment</type>
<id>963</id>
<totalQuantity>10</totalQuantity>
<region>1</region>
</row>
<row>
<type>sumPerRegion</type>
<id />
<totalQuantity>10</totalQuantity>
<region>1</region>
</row>
<row>
<type>totalSum</type>
<id />
<totalQuantity>10</totalQuantity>
<region />
</row>
</assortment>
</Destination>
<Destination>
<name>destination 2</name>
<assortment>
<name>Volvo_GA</name>
<row>
<type>sumPerAssortment</type>
<id>960</id>
<totalQuantity>10</totalQuantity>
<region>1</region>
</row>
<row>
<type>sumPerRegion</type>
<id />
<totalQuantity>10</totalQuantity>
<region>1</region>
</row>
<row>
<type>totalSum</type>
<id />
<totalQuantity>10</totalQuantity>
<region />
</row>
</assortment>
</Destination>
</ns0:Destinations>
Note:
assortment number starting with 96 = region 1
assortment number starting with 97 = region 2
assortment number starting with 98 = region 3
Start of my XSLT:
<?xml version="1.0" encoding="UTF-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:var="http://schemas.microsoft.com/BizTalk/2003/var"
exclude-result-prefixes="msxsl var s0"
version="1.0"
xmlns:s0="http://BizTalk_Projects.input"
xmlns:ns0="http://BizTalk_Projects.output">
<xsl:output omit-xml-declaration="yes" method="xml" version="1.0" />
<xsl:key name="destinationKey" match="transports" use="destination"/>
<xsl:template match="/">
<xsl:apply-templates select="/s0:Data" />
</xsl:template>
<xsl:template match="/s0:Data">
<ns0:Destinations>
<xsl:for-each select="transports[count(. | key('destinationKey',destination)[1]) = 1]">
<Destination>
<name>
<xsl:value-of select="destination/text()" />
</name>
<xsl:for-each select="key('destinationKey',destination)">
<assortment>
<name>
<xsl:value-of select="substring(assortment/text(),1,string-length(assortment)-3)" />
</name>
</assortment>
</xsl:for-each>
</Destination>
</xsl:for-each>
</ns0:Destinations>
</xsl:template>
</xsl:stylesheet>
With this code, I'm getting this output (duplicate rows, but correct assortments for each destination);
<ns0:Destinations xmlns:ns0="http://BizTalk_Projects.output">
<Destination>
<name>destination 1</name>
<assortment>
<name>Volvo_GA</name>
</assortment>
<assortment>
<name>Volvo_GA</name>
</assortment>
<assortment>
<name>Volvo_GA</name>
</assortment>
<assortment>
<name>Volvo_GA</name>
</assortment>
<assortment>
<name>Volvo_SA</name>
</assortment>
<assortment>
<name>Nissan_GA</name>
</assortment>
<assortment>
<name>Nissan_GA</name>
</assortment>
</Destination>
<Destination>
<name>destination 2</name>
<assortment>
<name>Volvo_GA</name>
</assortment>
</Destination>
</ns0:Destinations>
Any suggestions on how I can solve this? Help is very appreciated!
It's difficult to see how exactly the output relates to the input. Try this as your starting point:
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:key name="transports-by-destination" match="transports" use="destination" />
<xsl:key name="transports-by-assortment" match="transports" use="concat(destination, '|', assortment)" />
<xsl:template match="/*">
<xsl:copy>
<!-- for each unique destination -->
<xsl:for-each select="transports[count(. | key('transports-by-destination', destination)[1]) = 1]">
<Destination>
<name>
<xsl:value-of select="destination"/>
</name>
<xsl:variable name="group" select="key('transports-by-destination', destination)" />
<!-- for each unique assortment in this destination -->
<xsl:for-each select="$group[count(. | key('transports-by-assortment', concat(destination, '|', assortment))[1]) = 1]">
<assortment>
<name>
<xsl:value-of select="assortment"/>
</name>
<!-- process this subgroup -->
<xsl:for-each select="key('transports-by-assortment', concat(destination, '|', assortment))" >
<row>
<!-- not sure what goes in here -->
<totalQuantity>
<xsl:value-of select="quantity"/>
</totalQuantity>
</row>
</xsl:for-each>
</assortment>
</xsl:for-each>
</Destination>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

XSLT 1.0 grouping on one or multiple levels

Today's challenge was grouping in XSLT 1.0. Found out there are something called keys and the Muenchian grouping.
Input XML:
<Items>
<Item>
<ID>1</ID>
<Name>A</Name>
<Country>Sweden</Country>
<Region>Småland</Region>
</Item>
<Item>
<ID>2</ID>
<Name>B</Name>
<Country>Sweden</Country>
<Region>Norrland</Region>
</Item>
<Item>
<ID>3</ID>
<Name>C</Name>
<Country>USA</Country>
<Region>Alaska</Region>
</Item>
<Item>
<ID>4</ID>
<Name>D</Name>
<Country>USA</Country>
<Region>Texas</Region>
</Item>
<Item>
<ID>5</ID>
<Name>E</Name>
<Country>Sweden</Country>
<Region>Norrland</Region>
</Item>
</Items>
I need to make thins XML into a better structure, and from this sample XML I't like to get items structured by country and region. Below is wanted result where country and region gets sorted as well:
<Items>
<Country Name="Sweden">
<Region Name="Norrland">
<Item>
<ID>2</ID>
<Name>B</Name>
</Item>
<Item>
<ID>5</ID>
<Name>E</Name>
</Item>
</Region>
<Region Name="Småland">
<Item>
<ID>1</ID>
<Name>A</Name>
</Item>
</Region>
</Country>
<Country Name="USA">
<Region Name="Alaska">
<Item>
<ID>3</ID>
<Name>C</Name>
</Item>
</Region>
<Region Name="Texas">
<Item>
<ID>4</ID>
<Name>D</Name>
</Item>
</Region>
</Country>
</Items>
EDIT:
I also want to make sure regions end up in their own country, even if there are duplicates. I edited the answer accordingly.
Also, I'd like to hint about xsltfiddle.liberty-development.net as an easy way of doing trial-and-error XSLT development...
Inspired by this article, I found a neat solution to this problem:
I have included comments for using it for single or double grouping, see comments in the code. Notice how I use first key (index) as input to the secon for-each loop:
<?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"/>
<xsl:strip-space elements="*"/>
<xsl:key name="country" match="Item" use="Country" />
<xsl:key name="region" match="Item" use="concat(Region, '|', Country)" />
<xsl:template match="/Items">
<Items>
<xsl:for-each select="Item[generate-id(.) = generate-id(key('country', Country))]">
<xsl:sort select="Country" />
<xsl:variable name="_country" select="Country" />
<xsl:element name="Country">
<xsl:attribute name="Name"><xsl:value-of select="$_country" /></xsl:attribute>
<!-- single level grouping -->
<!--<xsl:apply-templates select="key('country', Country)" />-->
<!-- double grouping -->
<!-- START -->
<xsl:for-each select="key('country', Country)[generate-id(.) = generate-id(key('region', concat(Region, '|', Country)))]">
<xsl:sort select="Region" />
<xsl:variable name="_region" select="Region" />
<xsl:element name="Region">
<xsl:attribute name="Name"><xsl:value-of select="$_region" /></xsl:attribute>
<xsl:apply-templates select="key('region', concat(Region, '|', Country))" />
</xsl:element>
</xsl:for-each>
<!-- END -->
</xsl:element>
</xsl:for-each>
</Items>
</xsl:template>
<xsl:template match="Item">
<xsl:element name="Item">
<xsl:element name="ID"><xsl:value-of select="ID" /></xsl:element>
<xsl:element name="Name"><xsl:value-of select="Name" /></xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

How to return a list of objects from soap webservice with rails

I'm using Savon to access a soap webservice, but I can only ever return a single result, when what I want is an array of results.
Here is my call:
response = client.call(:get_events, message: { username: "xxxx", password: "xxxxxxxx", company_code: "12TCE" })
I want 'response' to return all records with the company_code of "12TCE" and for me to be able to output them all doing something like:
response.to_hash[:get_events_response].each do |a|
a[:return][:item][:name]
end
How can I return all records and output as desired?
UPDATE 1:
This is the link to the wsdl: http://www.brrmedia.co.uk/webservices/event/index.php?wsdl
This is the response I get:
HTTPI POST request to www.brrmedia.co.uk (httpclient)
SOAP response (status 200)
<?xml version="1.0" encoding="utf-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:tns="http://www.brrmedia.co.uk/webservices/event" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:getEventsResponse xmlns:ns1="http://www.brrmedia.co.uk/webservices/event">
<return xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="tns:objEvent[1]">
<item xsi:type="tns:objEvent">
<id xsi:type="xsd:int">119466</id>
<name xsi:type="xsd:string">blur Group - 2000 projects milestone</name>
<summary xsi:type="xsd:string"/>
<location xsi:type="xsd:string"/>
<date xsi:type="xsd:string">2013-12-17 11:30</date>
<link xsi:type="xsd:string">http://www.brrmedia.co.uk/event/119466/partner/brrsoap</link>
<company xsi:type="tns:objCompany">
<name xsi:type="xsd:string">blur Group</name>
<codes xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="tns:objCompanyCode[1]">
<item xsi:type="tns:objCompanyCode">
<code xsi:type="xsd:string">BLUR</code>
<collection xsi:type="xsd:string">London Stock Exchange (AIM)</collection>
</item>
</codes>
<website xsi:type="xsd:string">http://www.blurgroup.com/</website>
<category xsi:type="xsd:string">Technology</category>
</company>
<presenter xsi:type="tns:objPresenter">
<name xsi:type="xsd:string"> Philip Letts</name>
<image xsi:type="xsd:string">http://www.brrmedia.co.uk/getimage/id/31215</image>
</presenter>
<media xsi:type="SOAP-ENC:Array" SOAP-ENC:arrayType="tns:objMediaItem[3]">
<item xsi:type="tns:objMediaItem">
<src xsi:type="xsd:string">http://s3-us-west-2.amazonaws.com/brr-streamguys/files/BLUR/blur20131217.pdf</src>
<duration xsi:type="xsd:string">00:00:00</duration>
<filesize xsi:type="xsd:string">380</filesize>
<media_type xsi:type="xsd:string">pdf</media_type>
</item>
<item xsi:type="tns:objMediaItem">
<src xsi:type="xsd:string">http://s3-us-west-2.amazonaws.com/brr-streamguys/files/BLUR/BLUR20131217</src>
<duration xsi:nil="true" xsi:type="xsd:string"/>
<filesize xsi:nil="true" xsi:type="xsd:string"/>
<media_type xsi:type="xsd:string">presimages</media_type>
</item>
<item xsi:type="tns:objMediaItem">
<src xsi:type="xsd:string">http://s3-us-west-2.amazonaws.com/brr-streamguys/files/BLUR/BLUR20131217editv1.mp3</src>
<duration xsi:type="xsd:string">00:07:54</duration>
<filesize xsi:type="xsd:string">5557</filesize>
<media_type xsi:type="xsd:string">audio</media_type>
</item>
</media>
<type xsi:type="xsd:string">audio</type>
<height xsi:type="xsd:int">900</height>
<width xsi:type="xsd:int">680</width>
</item>
</return>
</ns1:getEventsResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>