I am trying to create an xslt style sheet to transform xml. I have researched the muenchian method but am not familiar with how it all works. I am having difficulties because every ManifestNo can have any number of SequenceNo's. I am trying to group the flat order in the source xml to an order in the destination xml with nested Stop details.
<Orders>
<Order>
<ManifestNo>283749</ManifestNo>
<Warehouse>35</Warehouse>
<SequenceNo>1</SequenceNo>
<CustomerOrderNo>3524771-01</CustomerOrderNo>
<Weight>180</Weight>
</Order>
<Order>
<ManifestNo>283749</ManifestNo>
<Warehouse>35</Warehouse>
<SequenceNo>1</SequenceNo>
<CustomerOrderNo>3524771-02</CustomerOrderNo>
<Weight>250</Weight>
</Order>
<Order>
<ManifestNo>283749</ManifestNo>
<Warehouse>35</Warehouse>
<SequenceNo>2</SequenceNo>
<CustomerOrderNo>3524728-01</CustomerOrderNo>
<Weight>25</Weight>
</Order>
<Order>
<ManifestNo>283750</ManifestNo>
<Warehouse>50</Warehouse>
<SequenceNo>1</SequenceNo>
<CustomerOrderNo>5464565-01</CustomerOrderNo>
<Weight>150</Weight>
</Order>
<Order>
<ManifestNo>283750</ManifestNo>
<Warehouse>50</Warehouse>
<SequenceNo>2</SequenceNo>
<CustomerOrderNo>5874565-02</CustomerOrderNo>
<Weight>125</Weight>
</Order>
<Order>
<ManifestNo>283750</ManifestNo>
<Warehouse>50</Warehouse>
<SequenceNo>2</SequenceNo>
<CustomerOrderNo>3524888-01</CustomerOrderNo>
<Weight>95</Weight>
</Order>
</Orders>
to
<Orders>
<Order>
<ManifestNo>283749</ManifestNo>
<Warehouse>35</Warehouse>
<Stop>
<SequenceNo>1</SequenceNo>
<Freight>
<CustomerOrderNo>3524771-01</CustomerOrderNo>
<Weight>180</Weight>
</Freight>
<Freight>
<CustomerOrderNo>3524771-02</CustomerOrderNo>
<Weight>250</Weight>
</Freight>
</Stop>
<Stop>
<SequenceNo>2</SequenceNo>
<Freight>
<CustomerOrderNo>3524728-01</CustomerOrderNo>
<Weight>25</Weight>
</Freight>
<Freight>
<CustomerOrderNo>3524771-02</CustomerOrderNo>
<Weight>250</Weight>
</Freight>
</Stop>
</Order>
<Order>
<ManifestNo>283750</ManifestNo>
<Warehouse>50</Warehouse>
<Stop>
<SequenceNo>1</SequenceNo>
<Freight>
<CustomerOrderNo>5464565-01</CustomerOrderNo>
<Weight>150</Weight>
</Freight>
</Stop>
<Stop>
<SequenceNo>2</SequenceNo>
<Freight>
<CustomerOrderNo>5874565-02</CustomerOrderNo>
<Weight>125</Weight>
</Freight>
<Freight>
<CustomerOrderNo>3524888-0</CustomerOrderNo>
<Weight>95</Weight>
</Freight>
</Stop>
</Order>
</Orders>
I. This XSLT 1.0 transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kOrderByNum" match="Order"
use="ManifestNo"/>
<xsl:key name="kSeqInOrder" match="SequenceNo"
use="concat(../ManifestNo, '+', .)"/>
<xsl:key name="kSeqByOrderNo" match="SequenceNo"
use="../ManifestNo"/>
<xsl:template match="Order"/>
<xsl:template match="SequenceNo"/>
<xsl:template match="/*">
<Orders>
<xsl:apply-templates/>
</Orders>
</xsl:template>
<xsl:template match=
"Order
[generate-id()
=
generate-id(key('kOrderByNum', ManifestNo)[1])
]">
<Order>
<xsl:copy-of select="ManifestNo|Warehouse"/>
<xsl:apply-templates select=
"key('kSeqByOrderNo', ManifestNo)
[generate-id()
=
generate-id(key('kSeqInOrder',
concat(../ManifestNo, '+', .)
)[1]
)
]
"/>
</Order>
</xsl:template>
<xsl:template match="SequenceNo[true()]">
<Stop>
<xsl:copy-of select="."/>
<xsl:apply-templates mode="inGroup" select=
"key('kSeqInOrder',
concat(../ManifestNo, '+', .))"/>
</Stop>
</xsl:template>
<xsl:template match="SequenceNo" mode="inGroup">
<Freight>
<xsl:copy-of select="../CustomerOrderNo|../Weight"/>
</Freight>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<Orders>
<Order>
<ManifestNo>283749</ManifestNo>
<Warehouse>35</Warehouse>
<SequenceNo>1</SequenceNo>
<CustomerOrderNo>3524771-01</CustomerOrderNo>
<Weight>180</Weight>
</Order>
<Order>
<ManifestNo>283749</ManifestNo>
<Warehouse>35</Warehouse>
<SequenceNo>1</SequenceNo>
<CustomerOrderNo>3524771-02</CustomerOrderNo>
<Weight>250</Weight>
</Order>
<Order>
<ManifestNo>283749</ManifestNo>
<Warehouse>35</Warehouse>
<SequenceNo>2</SequenceNo>
<CustomerOrderNo>3524728-01</CustomerOrderNo>
<Weight>25</Weight>
</Order>
<Order>
<ManifestNo>283750</ManifestNo>
<Warehouse>50</Warehouse>
<SequenceNo>1</SequenceNo>
<CustomerOrderNo>5464565-01</CustomerOrderNo>
<Weight>150</Weight>
</Order>
<Order>
<ManifestNo>283750</ManifestNo>
<Warehouse>50</Warehouse>
<SequenceNo>2</SequenceNo>
<CustomerOrderNo>5874565-02</CustomerOrderNo>
<Weight>125</Weight>
</Order>
<Order>
<ManifestNo>283750</ManifestNo>
<Warehouse>50</Warehouse>
<SequenceNo>2</SequenceNo>
<CustomerOrderNo>3524888-01</CustomerOrderNo>
<Weight>95</Weight>
</Order>
</Orders>
produces the wanted, correct result:
<Orders>
<Order>
<ManifestNo>283749</ManifestNo>
<Warehouse>35</Warehouse>
<Stop>
<SequenceNo>1</SequenceNo>
<Freight>
<CustomerOrderNo>3524771-01</CustomerOrderNo>
<Weight>180</Weight>
</Freight>
<Freight>
<CustomerOrderNo>3524771-02</CustomerOrderNo>
<Weight>250</Weight>
</Freight>
</Stop>
<Stop>
<SequenceNo>2</SequenceNo>
<Freight>
<CustomerOrderNo>3524728-01</CustomerOrderNo>
<Weight>25</Weight>
</Freight>
</Stop>
</Order>
<Order>
<ManifestNo>283750</ManifestNo>
<Warehouse>50</Warehouse>
<Stop>
<SequenceNo>1</SequenceNo>
<Freight>
<CustomerOrderNo>5464565-01</CustomerOrderNo>
<Weight>150</Weight>
</Freight>
</Stop>
<Stop>
<SequenceNo>2</SequenceNo>
<Freight>
<CustomerOrderNo>5874565-02</CustomerOrderNo>
<Weight>125</Weight>
</Freight>
<Freight>
<CustomerOrderNo>3524888-01</CustomerOrderNo>
<Weight>95</Weight>
</Freight>
</Stop>
</Order>
</Orders>
II. XSLT 2.0 Solution:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kSeqByOrderNo" match="SequenceNo"
use="../ManifestNo"/>
<xsl:template match="/*">
<Orders>
<xsl:for-each-group select="Order" group-by="ManifestNo">
<Order>
<xsl:copy-of select="ManifestNo|Warehouse"/>
<xsl:for-each-group select="key('kSeqByOrderNo', ManifestNo)"
group-by=".">
<xsl:copy-of select="."/>
<Stop>
<xsl:apply-templates select="current-group()"/>
</Stop>
</xsl:for-each-group>
</Order>
</xsl:for-each-group>
</Orders>
</xsl:template>
<xsl:template match="SequenceNo">
<Freight>
<xsl:copy-of select="../CustomerOrderNo|../Weight"/>
</Freight>
</xsl:template>
</xsl:stylesheet>
Related
I am trying to create a counter to grouped elements.
Source:
<?xml version="1.0" encoding="UTF-8"?>
<root_com>
<root_por-out>
<is_globalprocessid>1370284</is_globalprocessid>
<is_processid>1370284</is_processid>
<partneridentcode>123456</partneridentcode>
<por-out>
<por_number>320060916</por_number>
<order_pos>10</order_pos>
<order_pos_partner>10</order_pos_partner>
</por-out>
<por-out>
<por_number>320060916</por_number>
<order_number_partner>875421</order_number_partner>
<order_pos>20</order_pos>
<order_pos_partner>20</order_pos_partner>
</por-out>
<por-out>
<por_number>320060916</por_number>
<order_pos>30</order_pos>
<order_pos_partner>10</order_pos_partner>
</por-out>
<por-out>
<por_number>320060916</por_number>
<order_pos>40</order_pos>
<order_pos_partner>30</order_pos_partner>
</por-out>
<por-out>
<por_number>320060916</por_number>
<order_pos>50</order_pos>
<order_pos_partner>10</order_pos_partner>
</por-out>
</root_por-out>
</root_com>
Desired output:
<Confirmation>
<Settings>
<DecimalSymbol>.</DecimalSymbol>
</Settings>
<Orders>
<Order>
<OrderIdSupplier>320060916</OrderIdSupplier>
<OrderItems>
<OrderItem>
<LineNumber>10</LineNumber>
<ItemSubNo>1</ItemSubNo>
</OrderItem>
<OrderItem>
<LineNumber>20</LineNumber>
<ItemSubNo>1</ItemSubNo>
</OrderItem>
<OrderItem>
<LineNumber>10</LineNumber>
<ItemSubNo>2</ItemSubNo>
</OrderItem>
<OrderItem>
<LineNumber>30</LineNumber>
<ItemSubNo>1</ItemSubNo>
</OrderItem>
<OrderItem>
<LineNumber>10</LineNumber>
<ItemSubNo>3</ItemSubNo>
</OrderItem>
</OrderItems>
</Order>
</Orders>
</Confirmation>
The Code i currently have:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:formatter="com.inubit.ibis.xsltext.Formatter" version="2.0" exclude-result-prefixes="formatter">
<xsl:output method="xml" encoding="UTF-8"/>
<xsl:template match="/"><xsl:for-each select="root_com/root_por-out"><Confirmation>
<Settings>
<DecimalSymbol>.</DecimalSymbol>
</Settings>
<Orders>
<Order>
<PurchaseNo><xsl:value-of select="por-out[1]/order_number_partner"/></PurchaseNo>
<SupplierId><xsl:value-of select="partneridentcode"/></SupplierId>
<OrderIdSupplier><xsl:value-of select="por-out[1]/por_number"/></OrderIdSupplier>
<OrderItems><xsl:for-each select="por-out/order_pos"><xsl:for-each-group select="../order_pos_partner" group-by="text()"><OrderItem>
<DeliveryDate><xsl:value-of select="delivery_date"/></DeliveryDate>
<LineNumber><xsl:value-of select="current-grouping-key()"/></LineNumber><ItemSubNo><xsl:value-of select="last()"/></ItemSubNo>
<ProductId><xsl:value-of select="article_partner"/></ProductId>
<Price><xsl:value-of select="price"/></Price>
<PriceFactor>1</PriceFactor>
<Quantity><xsl:value-of select="quantity"/></Quantity>
</OrderItem></xsl:for-each-group></xsl:for-each>
</OrderItems>
</Order>
</Orders>
</Confirmation></xsl:for-each></xsl:template>
</xsl:stylesheet>
Code reduced to the core issue:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:formatter="com.inubit.ibis.xsltext.Formatter" version="2.0" exclude-result-prefixes="formatter">
<xsl:output method="xml" encoding="UTF-8"/>
<xsl:template match="/"><xsl:for-each select="root_com/root_por-out"><Confirmation>
<Settings>
<DecimalSymbol>.</DecimalSymbol>
</Settings>
<Orders>
<Order>
<OrderItems><xsl:for-each select="por-out/order_pos"><xsl:for-each-group select="../order_pos_partner" group-by="text()"><OrderItem>
<LineNumber><xsl:value-of select="current-grouping-key()"/></LineNumber><ItemSubNo><xsl:value-of select="last()"/></ItemSubNo>
</OrderItem></xsl:for-each-group></xsl:for-each>
</OrderItems>
</Order>
</Orders>
</Confirmation></xsl:for-each></xsl:template>
</xsl:stylesheet>
What the point is:
The customer sends an order. We provide a response. Since high amounts are ordered, we need to delivere the ordered parts on different dates. We split original positions.
In our reply customer gets a reference to his original order position(order_pos_partner) and customer also needs a counter for amount of split positions(destination: "ItemSubNo")
how can I do that?
If output is sorted by positions, that is fine, but not needed.
Thank you
If you use
<xsl:template match="root_por-out">
<Confirmation>
<Orders>
<Order>
<OrderIdSupplier>{por-out[1]/por_number}</OrderIdSupplier>
<xsl:for-each-group select="por-out" group-by="order_pos_partner">
<xsl:apply-templates select="current-group()"/>
</xsl:for-each-group>
</Order>
</Orders>
</Confirmation>
</xsl:template>
<xsl:template match="por-out">
<OrderItem>
<LineNumber>{current-grouping-key()}</LineNumber>
<ItemSubNumber>{position()}</ItemSubNumber>
</OrderItem>
</xsl:template>
you will get
<OrderItem>
<LineNumber>10</LineNumber>
<ItemSubNumber>1</ItemSubNumber>
</OrderItem>
<OrderItem>
<LineNumber>10</LineNumber>
<ItemSubNumber>2</ItemSubNumber>
</OrderItem>
<OrderItem>
<LineNumber>10</LineNumber>
<ItemSubNumber>3</ItemSubNumber>
</OrderItem>
<OrderItem>
<LineNumber>20</LineNumber>
<ItemSubNumber>1</ItemSubNumber>
</OrderItem>
<OrderItem>
<LineNumber>30</LineNumber>
<ItemSubNumber>1</ItemSubNumber>
</OrderItem>
So I think that has the right numbers, although not the ordering you showed.
To preserve the original input order, you could use the grouping only to store the right sequences and them map then later on:
<xsl:template match="root_por-out">
<Confirmation>
<Orders>
<Order>
<OrderIdSupplier>{por-out[1]/por_number}</OrderIdSupplier>
<xsl:variable name="groups" as="map(*)">
<xsl:map>
<xsl:for-each-group select="por-out" group-by="order_pos_partner">
<xsl:map-entry key="current-grouping-key()" select="current-group() ! generate-id()"/>
</xsl:for-each-group>
</xsl:map>
</xsl:variable>
<xsl:apply-templates select="*">
<xsl:with-param name="groups" select="$groups"/>
</xsl:apply-templates>
</Order>
</Orders>
</Confirmation>
</xsl:template>
<xsl:template match="por-out">
<xsl:param name="groups"/>
<OrderItem>
<LineNumber>{order_pos_partner}</LineNumber>
<ItemSubNumber>{index-of($groups(order_pos_partner), generate-id())}</ItemSubNumber>
</OrderItem>
</xsl:template>
Both examples use XSLT 3 although with some more verbosity (<LineNumber>{order_pos_partner}</LineNumber> instead of <LineNumber><xsl:value-of select="current-grouping-key()"/></LineNumber>or XML data structures instead of light-weight maps
<xsl:key name="group" match="group" use="#key"/>
<xsl:template match="root_por-out">
<Confirmation>
<Orders>
<Order>
<OrderIdSupplier>{por-out[1]/por_number}</OrderIdSupplier>
<xsl:variable name="groups">
<xsl:for-each-group select="por-out" group-by="order_pos_partner">
<group key="{current-grouping-key()}">
<xsl:for-each select="current-group()">
<item>
<xsl:value-of select="generate-id()"/>
</item>
</xsl:for-each>
</group>
</xsl:for-each-group>
</xsl:variable>
<xsl:apply-templates select="*">
<xsl:with-param name="groups" select="$groups"/>
</xsl:apply-templates>
</Order>
</Orders>
</Confirmation>
</xsl:template>
<xsl:template match="por-out">
<xsl:param name="groups"/>
<OrderItem>
<LineNumber>
<xsl:value-of select="order_pos_partner"/>
</LineNumber>
<ItemSubNumber>
<xsl:value-of select="index-of(key('group', order_pos_partner, $groups)/item, generate-id())"/>
</ItemSubNumber>
</OrderItem>
</xsl:template>
it could be done in XSLT 2.
I need to sum the multiplication of 2 numbers based on this example
<test>
<stop>
<id>1</id>
<unit_id>1</unit_id>
<unit_id>2</unit_id>
</stop>
<stop>
<id>2</id>
<unit_id>1</unit_id>
<unit_id>3</unit_id>
</stop>
<unit>
<id>1</id>
<count>2</count>
<value>1</value>
</unit>
<unit>
<id>2</id>
<count>4</count>
<value>1</value>
</unit>
<unit>
<id>3</id>
<count>2</count>
<value>3</value>
</unit>
The result i want to get is the one below
<test>
<stop>
<id>1</id>
<sum>6</sum>
</stop>
<stop>
<id>2</id>
<sum>10</sum>
</stop>
Any tips how to get it?
I tried with this example but the sum of the moltiplication doesn't work, it is ok for only the sum or the multiplication but not both
<xsl:template match="stop">
<xsl:variable name="ship_unit" select="id"/>
<xsl:value-of select="sum(following-sibling::unit[id=$ship_unit]/count*following-sibling::unit[id=$ship_unit]/value)"/>
If I am guessing correctly, you want to do something like:
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="unit" match="unit" use="id" />
<xsl:template match="/test">
<xsl:copy>
<xsl:for-each select="stop">
<xsl:variable name="unit1" select="key('unit', unit_id[1])" />
<xsl:variable name="unit2" select="key('unit', unit_id[2])" />
<xsl:copy>
<xsl:copy-of select="id"/>
<sum>
<xsl:value-of select="$unit1/count * $unit1/value + $unit2/count * $unit2/value" />
</sum>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
However, the result of applying this to your input example will be:
<?xml version="1.0" encoding="utf-8"?>
<test>
<stop>
<id>1</id>
<sum>6</sum>
</stop>
<stop>
<id>2</id>
<sum>8</sum>
</stop>
</test>
and not what you posted.
We have a tool which is producing an unwanted XML element and we are using XSLT to translate it to the required format.
We are writing a different XSLT for every XML generated by the file. e.g. one for the Customers XML, one for the Orders XML and so on.
Below are a couple of XML data files produced by the tool and the actual output expected
Customers
Tool-generated XML
<Message>
<Data>
<CustomerArray>
<Customer>
<X>
<Name>John</Name>
<Id>100</Id>
<Roles>
<Role>
<X>Manager</X>
<X>Architect</X>
</Role>
</Roles>
</X>
<X>
<Name>Doe</Name>
<Id>102</Id>
<Roles>
<Role>
<X>Supervisor</X>
<X>Admin</X>
</Role>
</Roles>
</X>
</Customer>
</CustomerArray>
</Data>
</Message>
Required XML data
<Message>
<Data>
<CustomerArray>
<Customer>
<Name>John</Name>
<Id>100</Id>
<Roles>
<Role>Manager</Role>
<Role>Architect</Role>
</Roles>
</Customer>
<Customer>
<Name>Doe</Name>
<Id>102</Id>
<Roles>
<Role>Supervisor</Role>
<Role>Admin</Role>
</Roles>
</Customer>
</CustomerArray>
</Data>
</Message>
Orders
Tool-generated XML
<Message>
<Orders>
<Order>
<X>
<OrderNumber>O123</OrderNumber>
<CustomerID>C100</CustomerID>
<Quantity>100</Quantity>
<UnitPrice>10.0</UnitPrice>
</X>
<X>
<OrderNumber>O456</OrderNumber>
<CustomerID>C107</CustomerID>
<Quantity>100</Quantity>
<UnitPrice>5.0</UnitPrice>
</X>
</Order>
</Orders>
</Message>
Required XML data
<Message>
<Orders>
<Order>
<OrderNumber>O123</OrderNumber>
<CustomerID>C100</CustomerID>
<Quantity>100</Quantity>
<UnitPrice>10.0</UnitPrice>
</Order>
<Order>
<OrderNumber>O456</OrderNumber>
<CustomerID>C107</CustomerID>
<Quantity>100</Quantity>
<UnitPrice>5.0</UnitPrice>
</Order>
</Orders>
</Message>
The unwanted element X can come at any level.
Is it possible to write a generic XSLT transform to achieve this result across all XML input? For instance, where an X is found, replace it with the parent tag and then delete the parent tag.
You need to write an identity transform with an explicit template for all nodes that have any X children so that they can be replicated.
This transform does what you asked. It uses the variable name to save the name of the element that is parent to the X to avoid writing the more obscure name(current()) when it comes to generating each output element.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output method="xml" indent="yes" encoding="UTF-8" omit-xml-declaration="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[X]">
<xsl:variable name="name" select="name()"/>
<xsl:for-each select="X">
<xsl:element name="{$name}">
<xsl:apply-templates/>
</xsl:element>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
output
Customers
<Message>
<Data>
<CustomerArray>
<Customer>
<Name>John</Name>
<Id>100</Id>
<Roles>
<Role>Manager</Role>
<Role>Architect</Role>
</Roles>
</Customer>
<Customer>
<Name>Doe</Name>
<Id>102</Id>
<Roles>
<Role>Supervisor</Role>
<Role>Admin</Role>
</Roles>
</Customer>
</CustomerArray>
</Data>
</Message>
Orders
<Message>
<Orders>
<Order>
<OrderNumber>O123</OrderNumber>
<CustomerID>C100</CustomerID>
<Quantity>100</Quantity>
<UnitPrice>10.0</UnitPrice>
</Order>
<Order>
<OrderNumber>O456</OrderNumber>
<CustomerID>C107</CustomerID>
<Quantity>100</Quantity>
<UnitPrice>5.0</UnitPrice>
</Order>
</Orders>
</Message>
Here is a slightly simpler/shorter solution, which also handles correctly the case when a X can have non X sibling elements:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[X]"><xsl:apply-templates/></xsl:template>
<xsl:template match="X">
<xsl:element name="{name(..)}">
<xsl:apply-templates select="node()|#*"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied to the following XML document (the first of the provided ones, with one non-X sibling added to the X elements:
<Message>
<Data>
<CustomerArray>
<Customer>
<X>
<Name>John</Name>
<Id>100</Id>
<Roles>
<Role>
<X>Manager</X>
<X>Architect</X>
</Role>
</Roles>
</X>
<somethingElse/>
<X>
<Name>Doe</Name>
<Id>102</Id>
<Roles>
<Role>
<X>Supervisor</X>
<X>Admin</X>
</Role>
</Roles>
</X>
</Customer>
</CustomerArray>
</Data>
</Message>
the wanted, correct result is produced:
<Message>
<Data>
<CustomerArray>
<Customer>
<Name>John</Name>
<Id>100</Id>
<Roles>
<Role>Manager</Role>
<Role>Architect</Role>
</Roles>
</Customer>
<somethingElse/>
<Customer>
<Name>Doe</Name>
<Id>102</Id>
<Roles>
<Role>Supervisor</Role>
<Role>Admin</Role>
</Roles>
</Customer>
</CustomerArray>
</Data>
</Message>
Do note that the solution by Borodin loses the somethingElse element.
Input:
<Order>
<HeaderValue1>ORDER1</HeaderValue1>
<OrderLineItems>
<Item>`A`</Item>
</OrderLineitems>
<OrderLineItems>
<Item>A</Item>
</OrderLineItems>
<OrderLineItems>
<Item>`A`</Item>`
</OrderLineItems>
<OrderLineItems>`
<Item>B</Item>`
</OrderLineItems>
</Order>
<Order>
<HeaderValue1>`ORDER2`</HeaderValue1>
<OrderLineItems>
<Item>B`</Item>
</OrderLineItems>
</Order>
for each order there is a number of orderLineItems are present.For each order and items we need to assign a increment value.if the previous item value is equal to current item value.there is no need to increment just pass the value.otherwise increment value is assign to item element.
I need the output is like below.
<Order>
<HeaderValue1>1`</HeaderValue1>
<OrderLineItems>`
<Item>2`</Item>
</OrderLineItems>
<OrderLineItems>
<Item>A`</Item>
</OrderLineItems>
<OrderLineItems>
<Item>A`</Item>
</OrderLineItems>
<OrderLineItems>
<Item>3`</Item>
</OrderLineItems>
</Order>
<Order>
<HeaderValue1>4</HeaderValue1>
<OrderLineItems>
<Item>5</Item>
</OrderLineItems>
</Order>
This transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:param name="pNum" select="0"/>
<xsl:copy>
<xsl:apply-templates select="node()[1]|#*">
<xsl:with-param name="pNum" select="$pNum"/>
</xsl:apply-templates>
</xsl:copy>
<xsl:apply-templates select="following-sibling::node()[1]">
<xsl:with-param name="pNum" select="$pNum"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="HeaderValue1">
<xsl:param name="pNum" select="0"/>
<HeaderValue1><xsl:value-of select="$pNum+1"/></HeaderValue1>
<xsl:apply-templates select="following-sibling::node()[1]">
<xsl:with-param name="pNum" select="$pNum+1"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="OrderLineItems[not(. = preceding-sibling::*[1])]">
<xsl:param name="pNum" select="0"/>
<OrderLineItems>
<xsl:apply-templates select="node()[1]|#*">
<xsl:with-param name="pNum" select="$pNum+1"/>
</xsl:apply-templates>
</OrderLineItems>
<xsl:apply-templates select="following-sibling::node()[1]">
<xsl:with-param name="pNum" select="$pNum+1"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="Item[not(. = ../preceding-sibling::*[1])]">
<xsl:param name="pNum" select="0"/>
<Item><xsl:value-of select="$pNum"/></Item>
</xsl:template>
<xsl:template match="Order">
<xsl:param name="pNum" select="0"/>
<Order>
<xsl:apply-templates select="node()[1]|#*">
<xsl:with-param name="pNum" select="$pNum"/>
</xsl:apply-templates>
<xsl:apply-templates select="following-sibling::node()[1]">
<xsl:with-param name="pNum" select=
"$pNum +1 + count(OrderLineItems[not(. = preceding-sibling::*[1])])"/>
</xsl:apply-templates>
</Order>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document (corrected to be well-formed):
<t>
<Order>
<HeaderValue1>ORDER1</HeaderValue1>
<OrderLineItems>
<Item>A</Item>
</OrderLineItems>
<OrderLineItems>
<Item>A</Item>
</OrderLineItems>
<OrderLineItems>
<Item>A</Item>
</OrderLineItems>
<OrderLineItems>
<Item>B</Item>
</OrderLineItems>
</Order>
<Order>
<HeaderValue1>ORDER2</HeaderValue1>
<OrderLineItems>
<Item>B</Item>
</OrderLineItems>
</Order>
</t>
produces the wanted, correct result:
<t>
<Order>
<HeaderValue1>1</HeaderValue1>
<OrderLineItems>
<Item>2</Item>
</OrderLineItems>
<OrderLineItems>
<Item>A</Item>
</OrderLineItems>
<OrderLineItems>
<Item>A</Item>
</OrderLineItems>
<OrderLineItems>
<Item>3</Item>
</OrderLineItems>
<Order>
<HeaderValue1>4</HeaderValue1>
<OrderLineItems>
<Item>5</Item>
</OrderLineItems>
</Order>
</Order>
</t>
I am new to xslt and trying to learn how to learn grouping using keys and using templates.
Can somebody help me on how can do the following in xslt.
I have to call a template from another template to do the transformation.
Thanks
here is my xml.
<Doc>
<ExOrder>121</ExOrder>
<Line>
<Ordernumber>225</Ordernumber>
<OrderID>1</OrderID>
<Quantity>5</Quantity>
</Line>
<Line>
<Ordernumber>225</Ordernumber>
<OrderID>5</OrderID>
<Quantity>5</Quantity>
</Line>
<Line>
<Ordernumber>226</Ordernumber>
<OrderID>2</OrderID>
<Quantity>5</Quantity>
</Line>
And here is how it should look like after.
<Doc>
<Order>
<Ordernumber>225</Ordernumber>
<Line>
<ID>1</ID>
<ID>5</ID>
</Line>
</Order>
<Order>
<Ordernumber>225</Ordernumber>
<Line>
<ID>1</ID>
<ID>5</ID>
</Line>
</Order>
</Doc>
I'm going to assume the output you actually want is:
<Doc>
<Order>
<Ordernumber>225</Ordernumber>
<Line>
<ID>1</ID>
<ID>5</ID>
</Line>
</Order>
<Order>
<Ordernumber>226</Ordernumber>
<Line>
<ID>2</ID>
</Line>
</Order>
</Doc>
Since the sample output you provided makes no sense. This XSLT will produce the output above when run on your sample input:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="kOrder" match="Line" use="Ordernumber"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<Doc>
<xsl:apply-templates select="Line[generate-id() =
generate-id(key('kOrder', Ordernumber)[1])]" />
</Doc>
</xsl:template>
<xsl:template match="Line">
<Order>
<xsl:apply-templates select="Ordernumber" />
<Line>
<xsl:apply-templates select="key('kOrder', Ordernumber)/OrderID" />
</Line>
</Order>
</xsl:template>
<xsl:template match="OrderID">
<ID>
<xsl:value-of select="."/>
</ID>
</xsl:template>
</xsl:stylesheet>