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.
Related
I need to replace the child node with another node without affecting its sub nodes, I tried matching the child node but was unable to
This is the xml format
<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Header>
<MessageId>{A124-B421-C325-D467}</MessageId>
<Action>find</Action>
</Header>
<Body>
<MessageParts xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Run xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/Run">
<RunObject class="entity">
<A1>NA</A1>
<A2>False</A2>
<Object class="entity">
<A3>02</A3>
</Object>
<A4>ER</A4>
</RunObject>
</Run>
</MessageParts>
</Body>
</Envelope>
This is the xml format that i require
<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Header>
<MessageId>{A124-B421-C325-D467}</MessageId>
<Action>find</Action>
</Header>
<Body>
<Document>
<Item>
<A1>NA</A1>
<A2>False</A2>
<Base>
<A3>02</A3>
</Base>
<A4>ER</A4>
</Item>
</Document>
</Body>
</Envelope>
This is the code that i through which i tried to change the format
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:m="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"
xmlns:r="http://schemas.microsoft.com/dynamics/2008/01/documents/Run"
exclude-result-prefixes="m r">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- move all elements to no namespace -->
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<!-- rename MessageParts to Document + skip the Run wrapper -->
<xsl:template match="m:MessageParts">
<Document>
<xsl:apply-templates select="r:Run/*"/>
</Document>
</xsl:template>
<!-- rename RunObject to Item + reorder child nodes -->
<xsl:template match="r:RunObject[#class='entity']">
<Item>
<xsl:apply-templates select="r:A1" />
<xsl:apply-templates select="r:A2" />
<xsl:template match="r:Object[#class='entity']>
<Base>
<xsl:apply-templates select="r:A3" />
</Base>
</xsl:Template>
<xsl:apply-templates select="r:A4" />
</Item>
</xsl:template>
</xsl:stylesheet>
I tried matching the Object element but was unable to since i am already matching its parent element that is RunObject
Your mistake is that you cannot define a template inside of a template. So move the <xsl:template match="r:Object[#class='entity']> to the root level and add an <xsl:apply-templates select="r:Object" /> at its place.
The stylesheet could look like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:m="http://schemas.microsoft.com/dynamics/2011/01/documents/Message" xmlns:r="http://schemas.microsoft.com/dynamics/2008/01/documents/Run" exclude-result-prefixes="m r">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- move all elements to no namespace -->
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<!-- rename MessageParts to Document + skip the Run wrapper -->
<xsl:template match="m:MessageParts">
<Document>
<xsl:apply-templates select="r:Run/*"/>
</Document>
</xsl:template>
<!-- rename RunObject to Item + reorder child nodes -->
<xsl:template match="r:RunObject[#class='entity']">
<Item>
<xsl:apply-templates select="r:A1" />
<xsl:apply-templates select="r:A2" />
<xsl:apply-templates select="r:Object" />
<xsl:apply-templates select="r:A4" />
</Item>
</xsl:template>
<xsl:template match="r:Object[#class='entity']">
<Base>
<xsl:apply-templates select="r:A3" />
</Base>
</xsl:template>
</xsl:stylesheet>
Its output is (nearly) as desired:
<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
<Header>
<MessageId>{A124-B421-C325-D467}</MessageId>
<Action>find</Action>
</Header>
<Body>
<Document>
<Item>
<A1>NA</A1>
<A2>False</A2>
<Base>
<A3>02</A3>
</Base>
<A4>ER</A4>
</Item>
</Document>
</Body>
</Envelope>
I added the adjective "nearly", because this result has no namespace, while the sample of your desired outcome is in the namespace
xmlns:m="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"
But because you defined a template that removes the namespaces of all elements, I did ignore that. I simply assume that this part of your question is erroneous.
If you like to change that, restrict the first template and add a general identity template:
<!-- move all elements to no namespace -->
<xsl:template match="*[namespace-uri() != 'http://schemas.microsoft.com/dynamics/2011/01/documents/Message']">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
This would make your output containing the root namespace:
<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Header>
<MessageId>{A124-B421-C325-D467}</MessageId>
<Action>find</Action>
</Header>
<Body>
<Document xmlns="">
<Item>
<A1>NA</A1>
<A2>False</A2>
<Base>
<A3>02</A3>
</Base>
<A4>ER</A4>
</Item>
</Document>
</Body>
</Envelope>
The XML output i am getting is not in the format that i require so tried some code to change the format but was unable to
This is the output that i am getting
<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Header>
<MessageId>{A124-B421-C325-D467}</MessageId>
<Action>find</Action>
</Header>
<Body>
<MessageParts xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Run xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/Run">
<RunObject class="entity">
<A1>NA</A1>
<A2>False</A2>
<A3>02</A3>
<A4>ER</A4>
</RunObject>
<RunObject class="entity">
<A1>NA</A1>
<A2>False</A2>
<A3>03</A3>
<A4>ER</A4>
</RunObject>
</Run>
</MessageParts>
</Body>
</Envelope>
This is the XML output that i require
<?xml version="1.0" encoding="UTF-8"?>
<Document>
<Item>
<A3>NA</A3>
<A4>False</A4>
<A2>02</A2>
<A1>ER</A1>
</Item>
<Item>
<A3>NA</A3>
<A4>False</A4>
<A2>03</A2>
<A1>ER</A1>
</Item>
</Document>
This is the code that i have used to change the format of the xml that i was getting
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:m="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"
xmlns:r="http://schemas.microsoft.com/dynamics/2008/01/documents/Run"
exclude-result-prefixes="m r">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- move all elements to no namespace -->
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="m:Envelope"/>
<xsl:template match="m:Header"/>
<xsl:template match="m:MessageId"/>
<xsl:template match="m:Action"/>
<xsl:template match="m:Body"/>
<!-- rename MessageParts to Document + skip the Run wrapper -->
<xsl:template match="m:MessageParts">
<Document>
<xsl:apply-templates select="r:Run/*"/>
</Document>
</xsl:template>
<!-- rename RunObject to Item + reorder child nodes -->
<xsl:template match="r:RunObject[#class='entity']">
<Item>
<xsl:apply-templates select="r:A3" />
<xsl:apply-templates select="r:A4" />
<xsl:apply-templates select="r:A2" />
<xsl:apply-templates select="r:A1" />
</Item>
</xsl:template>
</xsl:stylesheet>
I tried the above code but was not able to change the format of the xml
Instead of:
<xsl:template match="m:Envelope"/>
that removes the Envelope node and all its descendants, you need to remove only the Envelope wrapper and continue processing its contents:
<xsl:template match="m:Envelope">
<xsl:apply-templates/>
</xsl:template>
Likewise for the other wrappers. In fact, you could replace all of these:
<xsl:template match="m:Envelope"/>
<xsl:template match="m:Header"/>
<xsl:template match="m:MessageId"/>
<xsl:template match="m:Action"/>
<xsl:template match="m:Body"/>
with:
<xsl:template match="m:*">
<xsl:apply-templates select="*"/>
</xsl:template>
How to rename the parent node and rearrange the child nodes simultaneously.
I tried some code but was unable to get the desired output.
This was the output that i was getting
<?xml version="1.0" encoding="UTF-8"?>
<Envelope xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Header> </Header>
<Body>
<MessageParts xmlns="http://schemas.microsoft.com/dynamics/2011/01/documents/Message">
<Run xmlns="http://schemas.microsoft.com/dynamics/2008/01/documents/Run">
<RunObject class="entity">
<A1>NA</A1>
<A2>False</A2>
<A3>02</A3>
<A4>ER</A4>
</RunObject>
<RunObject class="entity">
<A1>NA</A1>
<A2>False</A2>
<A3>03</A3>
<A4>ER</A4>
</RunObject>
</Run>
</MessageParts>
</Body>
</Envelope>
I wanted to remove the namespaces, rename the Parent tags and rearrange the child tags.
So i used this code
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:m="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"
xmlns:r="http://schemas.microsoft.com/dynamics/2008/01/documents/Run"
exclude-result-prefixes="m r">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- move all elements to no namespace -->
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<!-- rename MessageParts to Document + skip the Run wrapper -->
<xsl:template match="m:MessageParts">
<Document>
<xsl:apply-templates select="r:Run/*"/>
</Document>
</xsl:template>
<xsl:template match="r:RunObject[#class='entity']">
<xsl:copy>
<xsl:apply-templates select="A3" />
<xsl:apply-templates select="A4" />
<xsl:apply-templates select="A2" />
<xsl:apply-templates select="A1" />
</xsl:copy>
</xsl:template>
<!-- rename RunObject to Item -->
<xsl:template match="r:RunObject[#class='entity']">
<Item>
<xsl:apply-templates />
</Item>
</xsl:template>
</xsl:stylesheet>
I was able to remove the namespaces, rename the parent tags but was unable to rearrange the child tags. The output i got was this
?xml version="1.0" encoding="UTF-8"?>
<Envelope>
<Header> </Header>
<Body>
<Document>
<Item>
<A1>NA</A1>
<A2>False</A2>
<A3>02</A3>
<A4>ER</A4>
</Item>
<Item>
<A1>NA</A1>
<A2>False</A2>
<A3>03</A3>
<A4>ER</A4>
</Item>
</Document>
</Body>
</Envelope>
But the desired output is this
<?xml version="1.0" encoding="UTF-8"?>
<Envelope>
<Header> </Header>
<Body>
<Document>
<Item>
<A3>02</A3>
<A4>ER</A4>
<A2>False</A2>
<A1>NA</A1>
</Item>
<Item>
<A3>03</A3>
<A4>ER</A4>
<A2>False</A2>
<A1>NA</A1>
</Item>
</Document>
</Body>
</Envelope>
Problem #1:
You have two templates matching the same nodes:
<xsl:template match="r:RunObject[#class='entity']">
Only the last one of these will be applied.
Problem #2:
The elements A1, A2, A3 and A4 are in a namespace inherited from their Run ancestor. You need to use the prefix bound to that namespace when selecting them.
See if this works for you:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:m="http://schemas.microsoft.com/dynamics/2011/01/documents/Message"
xmlns:r="http://schemas.microsoft.com/dynamics/2008/01/documents/Run"
exclude-result-prefixes="m r">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- move all elements to no namespace -->
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<!-- rename MessageParts to Document + skip the Run wrapper -->
<xsl:template match="m:MessageParts">
<Document>
<xsl:apply-templates select="r:Run/*"/>
</Document>
</xsl:template>
<!-- rename RunObject to Item + reorder child nodes -->
<xsl:template match="r:RunObject[#class='entity']">
<Item>
<xsl:apply-templates select="r:A3" />
<xsl:apply-templates select="r:A4" />
<xsl:apply-templates select="r:A2" />
<xsl:apply-templates select="r:A1" />
</Item>
</xsl:template>
</xsl:stylesheet>
I need to convert a name value pair into XML. I'm able to generate an XML, but the element name should be grouped and it should not be duplicated. Please see below. The FieldValue element contains 2 OrderItem values in the Detail node. If the FieldValue with OrderItem repeats, then the result should be grouped into one OrderItem node. Please help.
Source XML:
<SC>
<Header>
<Record>
<FieldName>Schema</FieldName>
<FieldValue>OrderHeader</FieldValue>
</Record>
<Record>
<FieldName>Order</FieldName>
<FieldValue>1234</FieldValue>
</Record>
</Header>
<Detail>
<Record>
<FieldName>Schema</FieldName>
<FieldValue>OrderItem</FieldValue>
</Record>
<Record>
<FieldName>Item</FieldName>
<FieldValue>1</FieldValue>
</Record>
<Record>
<FieldName>Qty</FieldName>
<FieldValue>10</FieldValue>
</Record>
</Detail>
<Detail>
<Record>
<FieldName>Schema</FieldName>
<FieldValue>OrderItem</FieldValue>
</Record>
<Record>
<FieldName>Item</FieldName>
<FieldValue>2</FieldValue>
</Record>
<Record>
<FieldName>Qty</FieldName>
<FieldValue>20</FieldValue>
</Record>
</Detail>
</SC>
Target XML:
<Order>
<OrderItem>
<Item>
<Item>1</Item>
<Qty>10</Qty>
</Item>
<Item>
<Item>2</Item>
<Qty>20</Qty>
</Item>
</OrderItem>
</Order>
XSLT:
<xsl:template match="#*|node()">
<Order>
<xsl:for-each select="Detail">
<Item>
<xsl:apply-templates select="Record[position()>1]"/>
</Item>
</xsl:for-each>
</Order>
</xsl:template>
<xsl:template match="Record">
<xsl:element name="{FieldName}">
<xsl:value-of select="FieldValue"/>
</xsl:element>
</xsl:template>
The grouping can be done as follows:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="2.0">
<xsl:output indent="yes"/>
<xsl:template match="SC">
<Order>
<xsl:for-each-group select="Detail" group-by="Record[1]/FieldValue">
<xsl:element name="{current-grouping-key()}">
<xsl:apply-templates select="current-group()"/>
</xsl:element>
</xsl:for-each-group>
</Order>
</xsl:template>
<xsl:template match="Detail">
<Item>
<xsl:apply-templates select="Record[position() gt 1]"/>
</Item>
</xsl:template>
<xsl:template match="Record">
<xsl:element name="{FieldName}">
<xsl:value-of select="FieldValue"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
It appears that you are trying to define two XSLT templates, when one should be sufficient. You want to match on the root and then that you want to iterate over each SC/Detail.
Then, you want to take the FieldValue of the sibling of the FieldName node that is 'Item' (for item value) and 'Qty' (for quantity value), but only those listed under 'Record'.
Note: You have specified a doubly-nested <Item> in your transformed output and this solution reflects that requirement.
This XSLT should do what you are requesting:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:for-each select="SC/Detail">
<Order>
<OrderItem>
<Item>
<Item>
<xsl:value-of select="Record[FieldName[text()='Item']]/FieldValue" />
</Item>
<Qty>
<xsl:value-of select="Record[FieldName[text()='Qty']]/FieldValue" />
</Qty>
</Item>
</OrderItem>
</Order>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
i am beginner in XSLT and i am using it to transform XML to XML
This is the source XML i receive
Source XML:
<Response>
<Pax>
<Id>1</Id>
</Pax>
<Pax>
<Id>2</Id>
</Pax>
<Travelers>
<Traveler>
<Name>ABC</Name>
</Traveler>
<Traveler>
<Name>XYZ</Name>
</Traveler>
</Travelers>
</Response>
I have written below XSLT
XSLT:
<?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:template match="Response">
<xsl:element name="Root">
<xsl:apply-templates select="Travelers/Traveler"/>
</xsl:element>
</xsl:template>
<xsl:template match="Traveler">
<xsl:element name="Person">
<xsl:element name="PId">
<xsl:value-of select="//Pax/Id[position()]" />
</xsl:element>
<xsl:element name="Name">
<xsl:value-of select="Name" />
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Output:
<Root>
<Person>
<PId>1</PId>
<Name>ABC</Name>
</Person>
<Person>
<PId>1</PId>
<Name>XYZ</Name>
</Person>
</Root>
I would like to generate below XML output
Expected Output:
<Root>
<Person>
<PId>1</PId>
<Name>ABC</Name>
</Person>
<Person>
<PId>2</PId>
<Name>XYZ</Name>
</Person>
</Root>
As shown in above XML the only issue is with PId, it should have value 2.
Please help. Thanks.
Here's a relatively simple solution.
When this XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output omit-xml-declaration="yes" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="/*">
<Root>
<xsl:apply-templates select="Pax" />
</Root>
</xsl:template>
<xsl:template match="Pax">
<xsl:variable name="vPosition" select="position()" />
<Person>
<PId>
<xsl:value-of select="Id" />
</PId>
<Name>
<xsl:value-of select="/*/Travelers/*[$vPosition]/Name" />
</Name>
</Person>
</xsl:template>
</xsl:stylesheet>
...is applied to the original XML:
<Response>
<Pax>
<Id>1</Id>
</Pax>
<Pax>
<Id>2</Id>
</Pax>
<Travelers>
<Traveler>
<Name>ABC</Name>
</Traveler>
<Traveler>
<Name>XYZ</Name>
</Traveler>
</Travelers>
</Response>
...the wanted result is produced:
<Root>
<Person>
<PId>1</PId>
<Name>ABC</Name>
</Person>
<Person>
<PId>2</PId>
<Name>XYZ</Name>
</Person>
</Root>
<?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:template match="Response">
<Root>
<xsl:for-each select="Travelers/Traveler">
<Person>
<xsl:variable name="index" select="position()" />
<Pid><xsl:value-of select="//Pax[$index]/Id"/></Pid>
<Name><xsl:value-of select="Name"/></Name>
</Person>
</xsl:for-each>
</Root>
</xsl:template>
</xsl:stylesheet>
<?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"/>
<xsl:template match="/Response">
<Root>
<xsl:for-each select="Pax">
<xsl:variable name="pos" select="position()"/>
<Person>
<PId>
<xsl:value-of select="Id"/>
</PId>
<xsl:apply-templates select="//Travelers">
<xsl:with-param name="pos" select="$pos"/>
</xsl:apply-templates>
</Person>
</xsl:for-each>
</Root>
</xsl:template>
<xsl:template match="Travelers">
<xsl:param name="pos"/>
<xsl:for-each select="//Name">
<xsl:if test="position()=$pos">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>