XSLT_1: Add missing node based on child element value - xslt-1.0

could you please assist with solution below.
The sources files some times missing hole segment (which has child 'count' element with number 3). In this case I need to map all missing node segment and add some specific values.
Please see correct source xml file below which has all 'node' segments.
<?xml version="1.0" encoding="utf-8"?>
<root>
<group>
<node segment="1">
<count>2</count>
<value>value_2</value>
</node>
<node segment="1">
<count>3</count>
<value>value_3</value>
</node>
<node segment="1">
<count>1</count>
<value>value_1</value>
</node>
</group>
</root>
The below one doesn't have all segments in place.
<?xml version="1.0" encoding="utf-8"?>
<root>
<group>
<node segment="1">
<count>2</count>
<value>value_2</value>
</node>
<node segment="1">
<count>1</count>
<value>value_1</value>
</node>
</group>
</root>
Mising part is:
<node segment="1">
<count>3</count>
<value>value_3</value>
</node>
I have worked on xslt script below which doesn't give me the correct output results. Please assist
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="root/group[not(node/count='3')]">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<node>
<count><xsl:value-of select="'some_results'"></xsl:value-of></count>
<value><xsl:value-of select="'some_results'"/></value>
</node>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node[not(count='3')]">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<count><xsl:value-of select="count"></xsl:value-of></count>
<value><xsl:value-of select="value"/></value>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
If element 'node' which has child element 'count'=3 present,- map as it is.
Otherwise the output would be:
<?xml version="1.0" encoding="utf-8"?>
<root>
<group>
<node segment="1">
<count>2</count>
<value>value_2</value>
</node>
<node segment="1">
<count>some_results</count>
<value>some_results</value>
</node>
<node segment="1">
<count>1</count>
<value>value_1</value>
</node>
</group>
</root>
Thanks,
Darius

Couldn't it be simply:
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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="group[not(node/count='3')]">
<xsl:copy>
<!-- add missing node-->
<node segment="1">
<count>some_results</count>
<value>some_results</value>
</node>
<!-- process existing nodes -->
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

Removing Parent Nodes without affecting child nodes

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 remove Namespaces when copying nodes to a different node in XML through XSLT1.0?

I'm new to XSL and i'm trying to copy a node of XML to another node in the same XML using XSLT. I can able to transform the file as expected but the XMLNS attribute is getting added to the destination which I don't want.
I have tried all the option of copy-namespaces='no' using XSLT2.0 but that doesn't work. Also, I cant have a prefix of namespaces in the XSLT and use exclude namespaces to avoid xmlns because the incoming XML file is dynamic and namespaces keep on changing. I can't have all the namespaces declared as a prefix in XSLT.
Incoming XML:
<?xml version="1.0" encoding="UTF-8"?>
<PropertySet>
<Message>
<NotificationHeader>
<BusinessId></BusinessId>
<CorrelationId>0201201916:21:24CKG3N</CorrelationId>
<SourceName></SourceName>
<SourceId></SourceId>
<EventType></EventType>
<SecurityIdentifierId></SecurityIdentifierId>
<ClientRequestId></ClientRequestId>
<TargetId>ESB</TargetId>
<EventTime></EventTime>
<RequestUser></RequestUser>
</NotificationHeader>
<EventList>
<Event>
<PayLoad>
<ListOfActionIo xmlns="http://www.test.com/IO">
<Action>
<ActivityId>4-309C7WV</ActivityId>
<ActivitySRId></ActivitySRId>
<ActivityTemplateId></ActivityTemplateId>
<ActivityUID>4-309C7WV</ActivityUID>
<Category> Notification</Category>
<Comment></Comment>
<Type>Action</Type>
<ListOfDestination>
<Destination>
<DestinationName>NSW Aboriginal Education Consultative Group Incorporated</DestinationName>
</Destination>
<Destination>
<DestinationName>NSW Aboriginal Education Consultative Group Incorporated</DestinationName>
</Destination>
</ListOfDestination>
</Action>
</ListOfActionIo>
</PayLoad>
</Event>
</EventList>
<DestinationList>
<Destination>
<DestinationName></DestinationName>
<DestinationId></DestinationId>
</Destination>
</DestinationList>
</Message>
</PropertySet>
XSLT Code:
<?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" method="xml" encoding="UTF-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//*[name()='Message']/*[name()='DestinationList']//*[name()='Destination']"></xsl:template>
<!--Copy the destination from one node to another node-->
<xsl:template match="//*[name()='Message']/*[name()='DestinationList']">
<xsl:copy>
<xsl:copy-of select="//*[name()='Message']/*[name()='EventList']//*[name()='Destination']"/>
</xsl:copy>
</xsl:template>
<!--Remove the original node from where the destination was copied-->
<xsl:template match="//*[name()='Message']/*[name()='EventList']//*[name()='ListOfDestination']"></xsl:template>
</xsl:stylesheet>
Output:
<PropertySet>
<Message>
<NotificationHeader>
<BusinessId/>
<CorrelationId>0201201916:21:24CKG3N</CorrelationId>
<SourceName/>
<SourceId/>
<EventType/>
<SecurityIdentifierId/>
<ClientRequestId/>
<TargetId>ESB</TargetId>
<EventTime/>
<RequestUser/>
</NotificationHeader>
<EventList>
<Event>
<PayLoad>
<ListOfActionIo xmlns="http://www.test.com/IO">
<Action>
<ActivityId>4-309C7WV</ActivityId>
<ActivitySRId/>
<ActivityTemplateId/>
<ActivityUID>4-309C7WV</ActivityUID>
<Category> Notification</Category>
<Comment/>
<Type>Action</Type>
</Action>
</ListOfActionIo>
</PayLoad>
</Event>
</EventList>
<DestinationList>
<Destination xmlns="http://www.test.com/IO">
<DestinationName>NSW Aboriginal Education Consultative Group Incorporated</DestinationName>
</Destination>
<Destination xmlns="http://www.test.com/IO">
<DestinationName>NSW Aboriginal Education Consultative Group Incorporated</DestinationName>
</Destination>
</DestinationList>
</Message>
</PropertySet>
Expected Output:
Without the Namespaces on the copied node.
<PropertySet>
<Message>
<NotificationHeader>
<BusinessId/>
<CorrelationId>0201201916:21:24CKG3N</CorrelationId>
<SourceName/>
<SourceId/>
<EventType/>
<SecurityIdentifierId/>
<ClientRequestId/>
<TargetId>ESB</TargetId>
<EventTime/>
<RequestUser/>
</NotificationHeader>
<EventList>
<Event>
<PayLoad>
<ListOfActionIo xmlns="http://www.test.com/IO">
<Action>
<ActivityId>4-309C7WV</ActivityId>
<ActivitySRId/>
<ActivityTemplateId/>
<ActivityUID>4-309C7WV</ActivityUID>
<Category> Notification</Category>
<Comment/>
<Type>Action</Type>
</Action>
</ListOfActionIo>
</PayLoad>
</Event>
</EventList>
<DestinationList>
<Destination>
<DestinationName>NSW Aboriginal Education Consultative Group Incorporated</DestinationName>
</Destination>
<Destination>
<DestinationName>NSW Aboriginal Education Consultative Group Incorporated</DestinationName>
</Destination>
</DestinationList>
</Message>
</PropertySet>
You are asking the wrong question. You don't want to "remove namespaces" - you want to change the namespace of transformed elements. This is akin to renaming the elements.
Now, since your example is confusing (and too long), consider the following:
XML
<root>
<colors>
<color>red</color>
<color>blue</color>
</colors>
<shapes xmlns="some-unknown-namespace">
<shape>circle</shape>
<shape>square</shape>
</shapes>
<sizes>
<size>small</size>
<size>large</size>
</sizes>
</root>
To change the namespace of shapes (and its descendants, that inherit it!) to no-namespace, without knowing in advance what the original namespace will be, you can 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:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[ancestor-or-self::*[name()='shapes']]">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<root>
<colors>
<color>red</color>
<color>blue</color>
</colors>
<shapes>
<shape>circle</shape>
<shape>square</shape>
</shapes>
<sizes>
<size>small</size>
<size>large</size>
</sizes>
</root>
Added:
lets modify the code to move the shapes into sizes and remove the shapes node.
OK, let's do that:
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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="sizes">
<xsl:copy>
<xsl:apply-templates/>
<!-- move the shapes into sizes -->
<xsl:apply-templates select="../*[name()='shapes']/*"/>
</xsl:copy>
</xsl:template>
<!-- remove the shapes element -->
<xsl:template match="*[name()='shapes']"/>
<xsl:template match="*[ancestor::*[name()='shapes']]">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*|node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<root>
<colors>
<color>red</color>
<color>blue</color>
</colors>
<sizes>
<size>small</size>
<size>large</size>
<shape>circle</shape>
<shape>square</shape>
</sizes>
</root>

XSLT: Convert Name/Value pair and transform an XML

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>

template not getting applied with XSLT

I am using xslt to convert xml to xml.
<root>
<elem>
<confs>
<conf1>1</conf1>
<conf2>2</conf2>
</confs>
</elem>
</root>
My XSL
<xsl:template match="elem">
<xsl:copy>
<xsl:attribute name="className">confs</xsl:attribute>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="confs">
<confs>
<xsl:for-each select="*">
<conf>
<value>
<xsl:value-of select="node()"></xsl:value-of>
</value>
</conf>
</confs>
</xsl:template>
desired output:
<root>
<elem className="confs>
<confs>
<conf>
<value>1</value>
</conf>
<conf>
<value>1</value>
</conf>
</confs>
</elem>
</root>
When ran each template individaully they are good. But I run both the confs template is not affected at all.
Any help?
I believe the most straightforward way to achieve your output would be by:
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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="elem">
<elem className="confs">
<xsl:apply-templates/>
</elem>
</xsl:template>
<xsl:template match="confs/*">
<conf>
<value>
<xsl:value-of select="."/>
</value>
</conf>
</xsl:template>
</xsl:stylesheet>

Create Empty element using XSLT 1.0

I am unable to figure it out how to build below XML using XSLT 1.0
<values>
<field name="abc"></field>
<field name="nch"></field>
</values>
there should not be any space between elements start and end tag.
Kindly help me as soon as possible.
Thanks.
In Saxon you have to change the output method to "html".
Example:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<values>
<field name="abc"></field>
<field name="nch"></field>
</values>
</xsl:template>
</xsl:stylesheet>
HereĀ“s a workearound that works for vs2010.
Example 1:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<values>
<field name="abc">
<xsl:value-of select="substring-before(' ',' ')"/>
</field>
<field name="nch">
<xsl:value-of select="substring-before(' ',' ')"/>
</field>
</values>
</xsl:template>
</xsl:stylesheet>
Example 2:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<xsl:element name="values">
<xsl:element name="filed">
<xsl:attribute name="name">abc</xsl:attribute>
<xsl:value-of select="substring-before(' ',' ')"/>
</xsl:element>
<xsl:element name="filed">
<xsl:attribute name="name">nch</xsl:attribute>
<xsl:value-of select="substring-before(' ',' ')"/>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Output:
<?xml version="1.0" encoding="utf-8"?>
<values>
<filed name="abc"></filed>
<filed name="nch"></filed>
</values>
The difference between <x></x> and <x/> is purely lexical and generally cannot be controlled by the XSLT processor, because the final output is performed by the Serializer.
With some XSLT processors (using their built-in serializers), it may be possible to output the full form of an empty element. However, with other processors, such as Saxon, this isn't (easily) possible.