I have a XML file when I use SSIS XML as Source it is generating multiple outputs. I am using XML task to design XSLT for getting single output.
Following is the XML. I need only single output in SSIS. How to write XSLT for the same?
<?xml version="1.0" encoding="utf-8"?>
<Test>
<iden type="shiftid">100</iden >
<iden type="trailid">25</iden >
<TestHeader>
<SequenceNum>111</SequenceNum>
<TestType>CONTAINER</TestType>
<hub role="origin">
<name />
<hubId>030</hubId>
<hubType>New</hubType>
</hub>
<hub role="Dest">
<name />
<hubId>0757</hubId>
<hubType>hublet</hubType>
</hub>
<TestItem>
<lineNumber>1</lineNumber>
<container>1005</container>
<conditionCode>OK</conditionCode>
<hub role="originator">
<name />
<hubId>TRANS-SHIPMENT</hubId>
<hubType>depot</hubType>
</hub>
</TestItem>
<TestItem>
<lineNumber>2</lineNumber>
<container>1005</container>
<conditionCode>OK</conditionCode>
<hub role="originator">
<name />
<hubId>SHIPMENT</hubId>
<hubType>depot</hubType>
</hub>
</TestItem>
<TestItem>
<lineNumber>1</lineNumber>
<container>1006</container>
<conditionCode>OK</conditionCode>
<hub role="originator">
<name />
<hubId>SHIPMENT</hubId>
<hubType>depot</hubType>
</hub>
</TestItem>
</TestHeader>
<TestContainer type="RAPP" id="1005">
<uom hb="m3" type="board">10.0729</uom>
</TestContainer>
<TestContainer type="RAMP" id="1006">
<uom hb="m3" type="board">10.0729</uom>
</TestContainer>
</Test>
I have struggled and comeup with XSLT as follows
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template name="Iden" match="Test">
<xsl:for-each select="../iden">
<xsl:choose>
<xsl:when test="#type='shiftid'">
<ShipmentID>
<xsl:value-of select="."/>
</ShipmentID>
</xsl:when>
<xsl:when test="#type='trailid'">
<TrailerID>
<xsl:value-of select="."/>
</TrailerID>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:template>
<xsl:template name="Item" match="Test">
<xsl:param name="MatchContainer"/>
<xsl:for-each select="//TestItem">
<xsl:if test="$MatchContainer=container">
<Testitem>
<lineNumber>
<xsl:value-of select="lineNumber"/>
</lineNumber>
<containerTestitemID>
<xsl:value-of select="container"/>
</containerTestitemID>
<ConditionCode>
<xsl:value-of select="conditionCode"/>
</ConditionCode>
<Originator>
<xsl:value-of select="hub[#role='originator']/hubId"/>
</Originator>
<OriginatorType>
<xsl:value-of select="hub[#role='originator']/hubType"/>
</OriginatorType>
</Testitem>
</xsl:if>
</xsl:for-each>
</xsl:template>
<xsl:template name="container" match="Test">
<xsl:for-each select="../TestContainer">
<Testcontainerlist>
<ContainerType>
<xsl:value-of select="#type"/>
</ContainerType>
<ContainerID>
<xsl:value-of select="#id"/>
</ContainerID>
<Volume>
<xsl:value-of select="uom"/>
</Volume>
<VolumeUOM>
<xsl:value-of select="volume/#hb"/>
</VolumeUOM>
<VolumeType>
<xsl:value-of select="volume/#type"/>
</VolumeType>
<xsl:call-template name="Item">
<xsl:with-param name="MatchContainer" select="#id"/>
</xsl:call-template>
</Testcontainerlist>
</xsl:for-each>
</xsl:template>
<xsl:template name="Header" match="Test">
<xsl:for-each select="TestHeader">
<Test>
<xsl:call-template name="Iden">
</xsl:call-template>
<dropSequenceNumber>
<xsl:value-of select="SequenceNum"/>
</dropSequenceNumber>
<TestType>
<xsl:value-of select="TestType"/>
</TestType>
<OriginID>
<xsl:value-of select="hub[#role='origin']/hubId"/>
</OriginID>
<DestinationID>
<xsl:value-of select="hub[#role='Dest']/hubId"/>
</DestinationID>
<OriginType>
<xsl:value-of select="hub[#role='origin']/hubType"/>
</OriginType>
<DestinationType>
<xsl:value-of select="hub[#role='Dest']/hubType"/>
</DestinationType>
<xsl:call-template name="container">
</xsl:call-template>
</Test>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
My desired output should be as follows
<?xml version="1.0" encoding="utf-8"?>
<Test>
<List>
<ShipmentID>100</ShipmentID>
<TrailerID>25</TrailerID>
<dropSequenceNumber>111</dropSequenceNumber>
<TestType>CONTAINER</TestType>
<OriginID>030</OriginID>
<DestinationID>0757</DestinationID>
<OriginType>New</OriginType>
<DestinationType>hublet</DestinationType>
<ContainerType>RAPP</ContainerType>
<ContainerID>1005</ContainerID>
<Volume>10.0729</Volume>
<VolumeUOM>m3</VolumeUOM>
<VolumeType>board</VolumeType>
<lineNumber>1</lineNumber>
<containerTestitemID>1005</containerTestitemID>
<ConditionCode>OK</ConditionCode>
<Originator>TRANS-SHIPMENT</Originator>
<OriginatorType>depot</OriginatorType>
</List>
<List>
<ShipmentID>100</ShipmentID>
<TrailerID>25</TrailerID>
<dropSequenceNumber>111</dropSequenceNumber>
<TestType>CONTAINER</TestType>
<OriginID>030</OriginID>
<DestinationID>0757</DestinationID>
<OriginType>New</OriginType>
<DestinationType>hublet</DestinationType>
<ContainerType>RAPP</ContainerType>
<ContainerID>1005</ContainerID>
<Volume>10.0729</Volume>
<VolumeUOM>m3</VolumeUOM>
<VolumeType>board</VolumeType>
<lineNumber>2</lineNumber>
<containerTestitemID>1005</containerTestitemID>
<ConditionCode>OK</ConditionCode>
<Originator>SHIPMENT</Originator>
<OriginatorType>depot</OriginatorType>
</List>
<List>
<ShipmentID>100</ShipmentID>
<TrailerID>25</TrailerID>
<dropSequenceNumber>111</dropSequenceNumber>
<TestType>CONTAINER</TestType>
<OriginID>030</OriginID>
<DestinationID>0757</DestinationID>
<OriginType>New</OriginType>
<DestinationType>hublet</DestinationType>
<ContainerType>RAMP</ContainerType>
<ContainerID>1006</ContainerID>
<Volume>10.0729</Volume>
<VolumeUOM>m3</VolumeUOM>
<VolumeType>board</VolumeType>
<lineNumber>1</lineNumber>
<containerTestitemID>1006</containerTestitemID>
<ConditionCode>OK</ConditionCode>
<Originator>SHIPMENT</Originator>
<OriginatorType>depot</OriginatorType>
</List>
</Test>
Related
I would like to change an image path.
The code to display the image is:
<xsl:value-of select="image"/>
the image path it takes from the feed is:
https://www.website.com/media/catalog/product/1/7/image.jpg
what i would like is:
https://www.website.com/media/catalog/product/thumbnail/folder/1/7/image.jpg
i tried the following:
<xsl:template name="string-replace-all">
<xsl:param name="text"/>
<xsl:param name="replace"/>
<xsl:param name="with"/>
<xsl:choose>
<xsl:when test="contains($text,$replace)">
<xsl:value-of select="substring-before($text,$replace)"/>
<xsl:value-of select="$with"/>
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="substring-after($text,$replace)"/>
<xsl:with-param name="replace" select="$replace"/>
<xsl:with-param name="with" select="$with"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="image"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="root">
<xsl:for-each select="product[position() = 1]">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="'https://www.website.com/media/catalog/product/'"/>
<xsl:with-param name="replace" select="'product/'" />
<xsl:with-param name="with" select="'product/thumbnail/folder/'"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
the output is:
https://www.website.com/media/catalog/product/thumbnail/folder/https://www.website.com/media/catalog/product/image.jpg
not getting a real error message, it is just showing twice the http part
Couldn't this be a lot simpler? For example, consider:
XML
<root>
<product>
<image>https://www.website.com/media/catalog/product/1/7/image.jpg</image>
</product>
<product>
<image>https://www.website.com/media/catalog/product/2/56/image.jpg</image>
</product>
<product>
<image>https://www.website.com/media/catalog/product/3/123/image.jpg</image>
</product>
</root>
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">
<images>
<xsl:for-each select="product">
<image>
<xsl:text>https://www.website.com/media/catalog/product/thumbnail/folder/</xsl:text>
<xsl:value-of select="substring-after(image, 'https://www.website.com/media/catalog/product/')" />
</image>
</xsl:for-each>
</images>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<images>
<image>https://www.website.com/media/catalog/product/thumbnail/folder/1/7/image.jpg</image>
<image>https://www.website.com/media/catalog/product/thumbnail/folder/2/56/image.jpg</image>
<image>https://www.website.com/media/catalog/product/thumbnail/folder/3/123/image.jpg</image>
</images>
I have an XML which needs to be formatted to a CSV. I have chosen XSL to do achieve this. In addition to the XML file, I have a Properties file which needs to be looked through for getting the values for the variables defined in the XML.
Can any one help me in how to do the lookups via an XSL using an external csv file?
CSV file after transformation:
Article.AclFlag|%field.Article.AclFlag.name|false|true|true|||||||||||master-data|%category.MasterData|Integer||0|||Enum.Acls|%enum.Acls.name|%enum.Acls.entry.0;%enum.Acls.entry.1;%enum.Acls.entry.2;%enum.Acls.entry.3;%enum.Acls.entry.4;%enum.Acls.entry.5;%enum.Acls.entry.6;|0;1;2;3;4;5;6;|0;1;2;3;4;5;6;|Article|%entity.Article.name|Product2G Variant
XSL
<?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="1.0">
<xsl:strip-space elements="*"/>
<xsl:output method="text" omit-xml-declaration="yes" indent="no"/>
<xsl:key name="kEntity" match="entity-type" use="#identifier" />
<xsl:key name="kCategory" match="category" use="#identifier" />
<xsl:key name="kFieldID" match="field-type" use="#identifier" />
<xsl:key name="kEnumID" match="enum" use="#identifier" />
<xsl:key name="k1" match="entry" use="#key"/>
<xsl:variable name="map-doc" select="document('../transform/Properties.properties')"/>
<xsl:template match="/">
<xsl:apply-templates select="repository/custom/entity/field"/>
</xsl:template>
<xsl:template match="field">
<xsl:copy>
<field>
<xsl:value-of select="#identifier"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="name"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="editable"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="visible"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="visible-from-top"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="max-length"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="exportPurpose"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="importPurpose"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="upper-bound"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="average-length"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="active"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="multiline"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="display-by-default"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="richtext"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="value"/>
<xsl:text>|</xsl:text>
</field>
<category-ref-name>
<xsl:value-of select="key('kCategory', #category-ref)/#identifier"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="key('kCategory', #category-ref)/name"/>
<xsl:text>|</xsl:text>
</category-ref-name>
<field-type-ref>
<xsl:value-of select="substring-after(key('kFieldID', #field-type-ref)/persistence-class-name, 'java.lang.')"/>
<xsl:text>|</xsl:text>
</field-type-ref>
<proxy-entity-ref>
<xsl:value-of select="key('kFieldID', #field-type-ref)/#proxy-ref"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="key('kFieldID', #field-type-ref)/lower-bound"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="key('kFieldID', #field-type-ref)/range-min"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="key('kFieldID', #field-type-ref)/range-max"/>
<xsl:text>|</xsl:text>
</proxy-entity-ref>
<enum-ref-name>
<xsl:value-of select="key('kEnumID', #enum-ref)/#identifier"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="key('kEnumID', #enum-ref)/name"/>
<xsl:text>|</xsl:text>
<xsl:for-each select="key('kEnumID', #enum-ref)/entry">
<xsl:value-of select="#label"/>
<xsl:text>;</xsl:text>
</xsl:for-each>
<xsl:text>|</xsl:text>
<xsl:for-each select="key('kEnumID', #enum-ref)/entry">
<xsl:value-of select="#external-code"/>
<xsl:text>;</xsl:text>
</xsl:for-each>
<xsl:text>|</xsl:text>
<xsl:for-each select="key('kEnumID', #enum-ref)/entry">
<xsl:value-of select="#key"/>
<xsl:text>;</xsl:text>
</xsl:for-each>
<xsl:text>|</xsl:text>
</enum-ref-name>
<entity>
<xsl:value-of select="../#identifier"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="../name"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="../#parentEntities-ref"/>
</entity>
</xsl:copy>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
Properties
field.Article.AclFlag.name= Object right type
category.MasterData= Header data
enum.Acls.name=Object right types
The csv file is also not formatted well.
Sample XML
<?xml version="1.0" encoding="UTF-8"?>
<repository>
<types>
<entity-type identifier="ArticleType" identifying-field-type-ref="ArticleType.Id">
<object-name>article</object-name>
<class-name>ArticleType</class-name>
<persistence-xpath>/Article</persistence-xpath>
<persistence-class-name>db.model.Article</persistence-class-name>
<lower-bound>1</lower-bound>
<upper-bound>1</upper-bound>
<field-type identifier="ArticleType.AclFlag">
<object-name>aclFlag</object-name>
<class-name>commons.AclFlags</class-name>
<persistence-xpath>/aclFlag</persistence-xpath>
<persistence-class-name>java.lang.Integer</persistence-class-name>
<fragment-column-access>Article.AclFlag</fragment-column-access>
<internal>true</internal>
<lower-bound>0</lower-bound>
<range-min></range-min>
<range-max></range-max>
<min-length>0</min-length>
</field-type>
</entity-type>
</types>
<custom>
<category identifier="master-data" order="1">
<name>%category.MasterData</name>
</category>
<enum identifier="Enum.Acls">
<name>%enum.Acls.name</name>
<description>%enum.Acls.description</description>
<class-name>com.heiler.ppm.repository.enumerations.StdEnumProvider</class-name>
<key-class-name>commons.AclFlags</key-class-name>
<entry label="%enum.Acls.entry.0" external-code="0" key="0"/>
<entry label="%enum.Acls.entry.1" external-code="1" key="1"/>
<entry label="%enum.Acls.entry.2" external-code="2" key="2"/>
<entry label="%enum.Acls.entry.3" external-code="3" key="3"/>
<entry label="%enum.Acls.entry.4" external-code="4" key="4"/>
<entry label="%enum.Acls.entry.5" external-code="5" key="5"/>
<entry label="%enum.Acls.entry.6" external-code="6" key="6"/>
</enum>
<entity entity-type-ref="ArticleType" identifier="Article" parentEntities-ref="Product2G Variant">
<name>%entity.Article.name</name>
<description>%entity.Article.description</description>
<label-pattern-short>{Article.SupplierAID}</label-pattern-short>
<label-pattern-long>{Article.SupplierAID} - {ArticleLang.DescriptionShort}</label-pattern-long>
<label-pattern-description>{ArticleLang.DescriptionLong}</label-pattern-description>
<field identifier="Article.AclFlag" category-ref="master-data" enum-ref="Enum.Acls" field-type-ref="ArticleType.AclFlag">
<name>%field.Article.AclFlag.name</name>
<description>%field.Article.AclFlag.description</description>
<editable>false</editable>
<visible>true</visible>
<visible-from-top>true</visible-from-top>
<help-context></help-context>
<mergeable>false</mergeable>
</field>
</custom>
</repository>
Sample Properties in XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>Default</comment>
<entry key="field.Article.AclFlag.name">Object right type</entry>
<entry key="category.MasterData">Header data</entry>
<entry key="enum.Acls.name">Object right types</entry>
</properties>
Any help is much appreciated.
Given that your properties are of this form
<entry key="field.Article.AclFlag.name">Object right type</entry>
You can define a key to look up entry elements by their key attribute, and I see such a key already exists in your XSLT
<xsl:key name="k1" match="entry" use="#key"/>
Next, ensure you have a variable reference to your external XML document
<xsl:variable name="map-doc" select="document('../transform/Properties.properties.xml')"/>
Then, to look up a value from the properties you can do this (obviously replacing the second argument with the actual value you want to look up):
<xsl:variable name="test" select="'enum.Acls.name'" />
<xsl:value-of select="key('k1', $test, $map-doc)" />
Note this form of the key function, with a third parameter, is only valid in XSLT 2.0. If you were to do <xsl:value-of select="key('k1', $test) />, it would look for the value in the input XML, not your properties XML.
In XSLT 1.0 you could do this to change the document context for the key
<xsl:variable name="test" select="'enum.Acls.name'" />
<xsl:for-each select="$map-doc">
<xsl:value-of select="key('k1', $test)" />
</xsl:for-each>
Alternatively, do it without a key
<xsl:variable name="test" select="'enum.Acls.name'" />
<xsl:value-of select="$map-doc//entry[#key=$test]" />
I have an issue while concatenating all the the child elements under parent element.
Here is the source data
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<PurchaseOrder id="abc">
<attr attr-name="A">
<new-value>123</new-value>
</attr>
<attr attr-name="B">
<new-value>99</new-value>
</attr>
<attr attr-name="B">
<new-value>77</new-value>
</attr>
<attr attr-name="C">
<new-value>1</new-value>
<new-value>2</new-value>
<new-value>3</new-value>
<new-value>4</new-value>
<new-value>5</new-value>
<new-value>6</new-value>
<new-value>7</new-value>
<new-value>8</new-value>
</attr>
<attr attr-name="D">
<new-value>
<child1>567</child1>
<child2>2</child2>
</new-value>
</attr>
<attr attr-name="E">
<new-value>
<child3>890</child3>
<child4>3</child4>
</new-value>
</attr>
</PurchaseOrder>
XSLT Transformation used
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns0="urn:demo:PurchaseOrder">
<xsl:output method="xml" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:key name="keyAttrName" match="attr" use="#attr-name" />
<xsl:template match="PurchaseOrder">
<ns0:PurchaseOrderMSG>>
<Orders>
<Order id="{#id}">
<xsl:for-each select="attr[generate-id() = generate-id(key('keyAttrName', #attr-name)[1])]">
<xsl:variable name="nodeName" select="#attr-name" />
<xsl:choose>
<xsl:when test="key('keyAttrName', #attr-name)/new-value/*/node()">
<xsl:for-each select="new-value">
<xsl:element name="{$nodeName}">
<xsl:copy-of select="*" />
</xsl:element>
</xsl:for-each>
</xsl:when>
<xsl:when test="key('keyAttrName', #attr-name)/new-value/materials/material">
<xsl:for-each select="key('keyAttrName', #attr-name)">
<xsl:element name="{$nodeName}">
<xsl:copy-of select="attr" />
</xsl:element>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:for-each select="key('keyAttrName', #attr-name)">
<xsl:element name="{$nodeName}">
<xsl:if test="position()!=1">
<ns0:text>|</ns0:text>
</xsl:if>
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</Order>
</Orders>
</ns0:PurchaseOrderMSG>
</xsl:template>
</xsl:stylesheet>
The expected Result should be
<?xml version="1.0" encoding="UTF-8"?>
<ns0:PurchaseOrderMSG xmlns:ns0="urn:demo:PurchaseOrder">
<Orders>
<Order>
<A>123</A>
<B>99</B>
<B>77</B>
<C>1|2|3|4|5|6|7|8</C>
<D>
<child1>567</child1>
<child2>2</child2>
</D>
<E>
<child3>890</child3>
<child4>3</child4>
</E>
</Order>
</Orders>
</ns0:PurchaseOrderMSG>
The output that is coming with the XSLT I have used is as below. Separator logic is not working.
<?xml version="1.0" encoding="UTF-8"?>
<ns0:PurchaseOrderMSG xmlns:ns0="urn:demo:PurchaseOrder">
<Orders>
<Order>
<A>123</A>
<B>99</B>
<B>77</B>
<C>12345678</C>
<D>
<child1>567</child1>
<child2>2</child2>
</D>
<E>
<child3>890</child3>
<child4>3</child4>
</E>
</Order>
</Orders>
</ns0:PurchaseOrderMSG>
I have tried logic mentioned in some blogs but no luck
XSLT merging/concatenating values of siblings nodes of same name into single node
Concatenate several child items into one child item using XSLT
You need to modify the <xsl:otherwise> condition to handle the values in <new-value> node as below. If the count of <new-value> child nodes is > 1, then perform the concatenation using the separator else just output the value as is.
<xsl:otherwise>
<xsl:for-each select="key('keyAttrName', #attr-name)">
<xsl:element name="{$nodeName}">
<xsl:choose>
<xsl:when test="count(new-value) > 1">
<xsl:for-each select="new-value">
<xsl:value-of select="." />
<xsl:if test="position() != last()">
<xsl:value-of select="'|'" />
</xsl:if>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:for-each>
</xsl:otherwise>
The complete XSLT and the output is as below.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns0="urn:demo:PurchaseOrder">
<xsl:output method="xml" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:key name="keyAttrName" match="attr" use="#attr-name" />
<xsl:template match="PurchaseOrder">
<ns0:PurchaseOrderMSG>
<Orders>
<Order id="{#id}">
<xsl:for-each select="attr[generate-id() = generate-id(key('keyAttrName', #attr-name)[1])]">
<xsl:variable name="nodeName" select="#attr-name" />
<xsl:choose>
<xsl:when test="key('keyAttrName', #attr-name)/new-value/*/node()">
<xsl:for-each select="new-value">
<xsl:element name="{$nodeName}">
<xsl:copy-of select="*" />
</xsl:element>
</xsl:for-each>
</xsl:when>
<xsl:when test="key('keyAttrName', #attr-name)/new-value/materials/material">
<xsl:for-each select="key('keyAttrName', #attr-name)">
<xsl:element name="{$nodeName}">
<xsl:copy-of select="attr" />
</xsl:element>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:for-each select="key('keyAttrName', #attr-name)">
<xsl:element name="{$nodeName}">
<xsl:choose>
<xsl:when test="count(new-value) > 1">
<xsl:for-each select="new-value">
<xsl:value-of select="." />
<xsl:if test="position() != last()">
<xsl:value-of select="'|'" />
</xsl:if>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="." />
</xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</Order>
</Orders>
</ns0:PurchaseOrderMSG>
</xsl:template>
</xsl:stylesheet>
Output
<ns0:PurchaseOrderMSG xmlns:ns0="urn:demo:PurchaseOrder">
<Orders>
<Order id="abc">
<A>123</A>
<B>99</B>
<B>77</B>
<C>1|2|3|4|5|6|7|8</C>
<D>
<child1>567</child1>
<child2>2</child2>
</D>
<E>
<child3>890</child3>
<child4>3</child4>
</E>
</Order>
</Orders>
</ns0:PurchaseOrderMSG>
This is my xml:
<A>
<D>dd</D>
<E>ee</E>
<B>
<C>1</C>
<C>2</C>
</B>
</A>
This was my xsl. I had to call template1 and template2 only few times for the same params:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/A">
<xsl:call-template name="items1">
<xsl:with-param name="key">aa</xsl:with-param>
<xsl:with-param name="value">bb</xsl:with-param>
</xsl:call-template>
<xsl:call-template name="items2">
<xsl:with-param name="key">aa</xsl:with-param>
<xsl:with-param name="value">bb</xsl:with-param>
</xsl:call-template>
...
</xsl:template>
<xsl:template name="template1 ">
<xsl:param name="key"/>
<xsl:param name="value"/>
<xsl:value-of select="/D"/>
<xsl:value-of select="$key"/>
<xsl:value-of select="$value"/>
...
</xsl:template>
<xsl:template name="template2">
<xsl:param name="key"/>
<xsl:param name="value"/>
<xsl:value-of select="/E"/>
<xsl:value-of select="$key"/>
<xsl:value-of select="$value"/>
...
</xsl:template>
</xsl:stylesheet>
Now, I have to call my templates about 350 times for template1 and 350 times for template2, I have many key, value pairs.
I thought to make one dictionary with key value pairs and iterate over it on template1 and template2
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:variable name="values">
<item key="A" value="aa"/>
<item key="B" value="bb"/>
...
</xsl:variable>
<xsl:template match="/A">
<xsl:for-each select="$values">
<xsl:call-template name="items1">
<xsl:with-param name="key">aa</xsl:with-param>
<xsl:with-param name="value">bb</xsl:with-param>
</xsl:call-template>
</xsl:for-each>
<xsl:for-each select="$values">
<xsl:call-template name="items2">
<xsl:with-param name="key">aa</xsl:with-param>
<xsl:with-param name="value">bb</xsl:with-param>
</xsl:call-template>
</xsl:for-each>
...
</xsl:template>
<xsl:template name="template1 ">
<xsl:param name="key"/>
<xsl:param name="value"/>
<xsl:value-of select="/D"/>
<xsl:value-of select="$key"/>
<xsl:value-of select="$value"/>
...
</xsl:template>
<xsl:template name="template2">
<xsl:param name="key"/>
<xsl:param name="value"/>
<xsl:value-of select="/E"/>
<xsl:value-of select="$key"/>
<xsl:value-of select="$value"/>
...
</xsl:template>
</xsl:stylesheet>
My question is that Is it good idea to solve my problem. How to get root node in templates, now current node is "item". I wanted to get value od D node at my template.
It's difficult to answer your question without seeing the complete picture. Without it I can only guess you want to do:
<xsl:for-each select="$prodDict/item">
<xsl:call-template name="Item">
<xsl:with-param name="param1" select="#key"/>
<xsl:with-param name="param" select="#value"/>
</xsl:call-template>
</xsl:for-each>
Note that this requires XSLT 2.0 or higher.
Added:
How to get root node in templates, now current node is "item".
A convenient method is to place the root node in a global variable, so that it's accessible from anywhere. Here's a simplified example:
XML
<A>
<D>dd</D>
<E>ee</E>
<B>
<C>1</C>
<C>2</C>
</B>
</A>
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="values">
<item key="A" value="aa"/>
<item key="B" value="bb"/>
</xsl:variable>
<xsl:variable name="xml" select="/" />
<xsl:template match="/">
<result>
<xsl:for-each select="$values/item">
<xsl:call-template name="template1">
<xsl:with-param name="param1" select="#key"/>
<xsl:with-param name="param2" select="#value"/>
</xsl:call-template>
</xsl:for-each>
</result>
</xsl:template>
<xsl:template name="template1 ">
<xsl:param name="param1"/>
<xsl:param name="param2"/>
<item>
<d>
<xsl:value-of select="$xml/A/D"/>
</d>
<key>
<xsl:value-of select="$param1"/>
</key>
<value>
<xsl:value-of select="$param2"/>
</value>
</item>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<result>
<item>
<d>dd</d>
<key>A</key>
<value>aa</value>
</item>
<item>
<d>dd</d>
<key>B</key>
<value>bb</value>
</item>
</result>
A customer supplied XML contains the delivery address as a comma separated string, I would like to split this string into named nodes using XSLT 1.0.
<?xml version="1.0" encoding="UTF-8"?>
<root>
<text>
Company, Streetaddress 20, 1234 AA, City
</text>
</root>
Output
<?xml version="1.0" encoding="UTF-8"?>
<root>
<text>
<COMPANY>Company</COMPANY>
<ADDRESS>Streetaddress 20</ADDRESS>
<ZIPCODE>1234 AA</ZIPCODE>
<CITY>City</CITY>
</text>
</root>
I tried several recursive templates for XSLT 1.0 which do a fine job splitting but the resulting nodes are identically named.
If possible, how can this be achieved using XSLT 1.0?
Does it have to be a recursive template? How about a straight-forward chain of substring-before and substring-after like this:
<xsl:template match="text">
<xsl:copy>
<COMPANY>
<xsl:value-of select="normalize-space(substring-before(., ','))"/>
</COMPANY>
<xsl:variable name="s1" select="substring-after(., ',')"/>
<ADDRESS>
<xsl:value-of select="normalize-space(substring-before($s1, ','))"/>
</ADDRESS>
<xsl:variable name="s2" select="substring-after($s1, ',')"/>
<ZIPCODE>
<xsl:value-of select="normalize-space(substring-before($s2, ','))"/>
</ZIPCODE>
<CITY>
<xsl:value-of select="normalize-space(substring-after($s2, ','))"/>
</CITY>
</xsl:copy>
</xsl:template>
For the fun of it, here is a generic version using a recursive template.
<xsl:template match="text">
<xsl:copy>
<xsl:call-template name="parse-comma-separated">
<xsl:with-param name="elements" select="'COMPANY,ADDRESS,ZIPCODE,CITY'"/>
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<xsl:template name="parse-comma-separated">
<xsl:param name="elements"/>
<xsl:param name="text"/>
<xsl:choose>
<xsl:when test="contains($elements, ',')">
<xsl:element name="{normalize-space(substring-before($elements, ','))}">
<xsl:value-of select="normalize-space(substring-before($text, ','))"/>
</xsl:element>
<xsl:call-template name="parse-comma-separated">
<xsl:with-param name="elements" select="substring-after($elements, ',')"/>
<xsl:with-param name="text" select="substring-after($text, ',')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{normalize-space($elements)}">
<xsl:value-of select="normalize-space($text)"/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
As the supplied XML only had the address as single node (Streettaddress 20) I added a second variable to split the address string into streetaddress and housenumber.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="UTF-8" indent="yes" method="xml"/>
<xsl:template match="text">
<root>
<COMPANY>
<xsl:value-of select="normalize-space(substring-before(., ','))"/>
</COMPANY>
<xsl:variable name="s1" select="substring-after(., ',')"/>
<xsl:variable name="address_temp" select="normalize-space(substring-before($s1, ','))"/>
<ADDRESS>
<xsl:value-of select="normalize-space(substring-before($address_temp, ' '))"/>
</ADDRESS>
<HOUSENUMBER>
<xsl:value-of select="normalize-space(substring-after($address_temp, ' '))"/>
</HOUSENUMBER>
<xsl:variable name="s2" select="substring-after($s1, ',')"/>
<ZIPCODE>
<xsl:value-of select="normalize-space(substring-before($s2, ','))"/>
</ZIPCODE>
<CITY>
<xsl:value-of select="normalize-space(substring-after($s2, ','))"/>
</CITY>
</root>
</xsl:template>
</xsl:stylesheet>
Result:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<COMPANY>Company</COMPANY>
<ADDRESS>Streetaddress</ADDRESS>
<HOUSENUMBER>20</HOUSENUMBER>
<ZIPCODE>1234 AA</ZIPCODE>
<CITY>City</CITY>
</root>