guys, recursion with grouping is not working. I am using XSLT 1.0.
I need some help to figure this out. Thanks in Advance.
Background:
I have 3 different Types (common, category, & complex) in the xml. My goal is to.
1 - Group xml nodes based on Types.
2 - Create a sub-group for complex under Type=common.
3 - For Type=complex create a number of collections depending upon the source xml. In each collection it should list only 4 elements where Name='A' or 'B' or 'C' or 'D'.
This is where I'm having the problem. Grouping and sub-grouping is working fine. However, when I try to create a collection using recursion then it is not giving me the intending output. For reference please see the expected out xml sample.
Source XML:
<?xml version="1.0" encoding="Windows-1252"?>
<XML>
<Attributes>
<Attribute>
<Name>Buyer ID</Name>
<Type>common</Type>
<Value>Lee</Value>
</Attribute>
<Attribute>
<Name>Enviornment</Name>
<Type>common</Type>
<Value>Dev</Value>
</Attribute>
<Attribute>
<Name>Retail</Name>
<Type>common</Type>
<Value></Value>
</Attribute>
<Attribute>
<Name>Gender</Name>
<Type>category</Type>
<Value>M</Value>
</Attribute>
<Attribute>
<Name>Collection</Name>
<Type>Complex</Type>
<Value>ing</Value>
<Path />
</Attribute>
<Attribute>
<Name>A</Name>
<Type>Complex</Type>
<Value>Testing</Value>
<Path />
</Attribute>
<Attribute>
<Name>B</Name>
<Type>Complex</Type>
<Value>Yellow</Value>
<Path />
</Attribute>
<Attribute>
<Name>C</Name>
<Type>Complex</Type>
<Value>10</Value>
<Path />
</Attribute>
<Attribute>
<Name>D</Name>
<Type>Complex</Type>
<Value>MA</Value>
<Path />
</Attribute>
<Attribute>
<Name>A</Name>
<Type>Complex</Type>
<Value>24a</Value>
<Path />
</Attribute>
<Attribute>
<Name>B</Name>
<Type>Complex</Type>
<Value>Green</Value>
<Path />
</Attribute>
<Attribute>
<Name>C</Name>
<Type>Complex</Type>
<Value>22</Value>
<Path />
</Attribute>
<Attribute>
<Name>D</Name>
<Type>Complex</Type>
<Value>AM</Value>
<Path />
</Attribute>
</Attributes>
</XML>
Expected Output:
<?xml version="1.0" encoding="utf-8"?>
<Data Schema="XML A">
<Items>
<Item>
<Attributes type="common">
<Attr name="Buyer ID" value="Lee" />
<Attr name="Enviornment" value="Dev" />
<Attr name="Retail" value="" />
<Collection name="Collection" >
<Complex>
<Attr name="A" value="Testing" />
<Attr name="B" value="Yellow" />
<Attr name="C" value="10" />
<Attr name="D" value="MA" />
</Complex>
<Complex>
<Attr name="A" value="24a" />
<Attr name="B" value="Green" />
<Attr name="C" value="22" />
<Attr name="D" value="AM" />
</Complex>
</Collection>
</Attributes>
<Attributes type="category">
<Attr name="Gender" value="M" />
</Attributes>
<errorCodes>
<errorCode>value for Retail is missing.</errorCode>
</errorCodes>
</Item>
</Items>
</Data>
Here is the XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="type" match="Attribute" use="Type"/>
<xsl:variable name="group" select="4"/>
<xsl:template match="/">
<Data Schema="XML A">
<Items>
<Item>
<xsl:apply-templates select="XML/Attributes/Attribute[generate-id() = generate-id(key('type', Type)[1])]">
<xsl:sort select="Type" order="descending"/>
</xsl:apply-templates>
<errorCodes>
<xsl:apply-templates select="XML/Attributes/Attribute" mode="errors"/>
</errorCodes>
</Item>
</Items>
</Data>
</xsl:template>
<xsl:template match="Attribute">
<xsl:variable name="compType" select="count(/XML/Attributes/Attribute[Type='Complex' and Name!='Collection'])"/>
<xsl:if test="Type!='Complex'">
<Attributes type="{Type}">
<xsl:apply-templates select="key('type',Type)" mode="out"/>
<xsl:if test="Type='common'">
<Collection>
<xsl:for-each select="/XML/Attributes/Attribute[Type='Complex']">
<xsl:choose>
<xsl:when test="(Name='A' or Name='B' or Name='C' or Name='D')">
<xsl:if test="(($compType > 0) and (Name!='Collection'))">
<xsl:apply-templates select="key('type','Complex')" mode="out"/>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<Complex>
<Attr id="" name="A" value="Default" />
<Attr id="" name="B" value="Default" />
<Attr id="" name="C" value="Default" />
<Attr id="" name="D" value="" />
</Complex>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</Collection>
</xsl:if>
</Attributes>
</xsl:if>
</xsl:template>
<xsl:template match="Attribute" mode="out">
<Collection>
<Attr name="{Name}" value="{Value}"/>
</Collection>
</xsl:template>
<xsl:template match="Attribute[Type='Complex']" mode="out">
<xsl:apply-templates select="XML/Attributes/Attribute[not(Name='Collection')]
[position() mod $group = 1]" mode="group"/>
</xsl:template>
<xsl:template match="Name" mode="group">
<xsl:if test="Name!='Collection'">
<Attr name="{Name}" value="{Value}"/>
</xsl:if>
</xsl:template>
<xsl:template match="Attribute">
<Complex>
<xsl:apply-templates
select=".|following-sibling::Attribute[position() < $group]" mode="inner" />
</Complex>
</xsl:template>
<xsl:template match="Attribute" mode="errors">
<xsl:if test="(Name='Retail' or Name='Product Description') and Value=''">
<errorCode>value for <xsl:value-of select="Name"/> is missing.</errorCode>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
I am not sure I understand all your requirements but the following sample
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:data="http://example.com/data"
exclude-result-prefixes="data"
version="1.0">
<xsl:output indent="yes"/>
<xsl:key name="att-by-type" match="Attributes/Attribute" use="Type"/>
<xsl:variable name="complex" select="key('att-by-type', 'Complex')"/>
<xsl:template match="XML">
<Data Schema="XML A">
<Items>
<Item>
<xsl:apply-templates select="Attributes/Attribute[Type = 'common' or Type = 'category'][generate-id() = generate-id(key('att-by-type', Type)[1])]" mode="group"/>
</Item>
</Items>
</Data>
</xsl:template>
<xsl:template match="Attribute" mode="group">
<Attributes type="{Type}">
<xsl:apply-templates select="key('att-by-type', Type)"/>
<xsl:if test="Type = 'common'">
<Collection name="Collection">
<xsl:apply-templates select="$complex[Name = 'A']" mode="comp-group"/>
</Collection>
</xsl:if>
</Attributes>
</xsl:template>
<xsl:template match="Attribute" mode="comp-group">
<Complex>
<xsl:variable name="pos" select="position()"/>
<xsl:apply-templates select="$complex[Name = 'A'][position() = $pos] |
$complex[Name = 'B'][position() = $pos] |
$complex[Name = 'C'][position() = $pos] |
$complex[Name = 'D'][position() = $pos]"/>
</Complex>
</xsl:template>
<xsl:template match="Attribute">
<Attr name="{Name}" value="{Value}"/>
</xsl:template>
</xsl:stylesheet>
produces the output you posted (with the exception of the errorCodes, I left that out as it seemed unrelated to the other problem).
Related
I need some help with this XSLT. It is working the way it is suppose to however I have change in requirements ... :-) and I need to modify this to give me the expected output.
I'm looking for some guidance and help.
Explanation:
I have a source xml like this
<XML>
<Attributes>
<Attribute>
<Name/>
<Type/>
<Value/>
<FromIM/>
<collection/>
<Path />
</Attribute>
</Attributes>
</XML>
In the above xml I need to look at the node "Type" and group them by the type. For example, if I have 5 attribute where Type is common, and 4 attributes where Type is category, and 3 attributes where Type is Complex then group them like this.
<?xml version="1.0" encoding="utf-8"?>
<Data Schema="XML A">
<Items>
<Item id="" shortname="FT123" longname="FT123" categorypath="FamilyName//DepartmentName//GroupName" type="Product">
<Attributes type="common">
<Attr name="common 1" value="1" path=""/>
<Attr name="common 2" value="2" path=""/>
<Attr name="common 3" value="3" path=""/>
<Attr name="common 4" value="4" path=""/>
<Attr name="common 5" value="4" path=""/>
<Collection id="" name="Collection" path="">
<Complex>
<Attr name="UPC" value="Testing" valueKey="0" />
<Attr name="Color" value="Yellow" valueKey="0"/>
<Attr name="Size" value="10" valueKey="0"/>
</Complex>
</Collection>
</Attributes>
<Attributes type="category">
<Attr name="category1" value="1" />
<Attr name="category2" value="2" />
<Attr name="category3" value="3" />
<Attr name="category4" value="4" />
</Attributes>
</Item>
</Items>
</Data>
As you can see from above that I'm group first common & category and creating a group collection for Complex under common. This is working fine (Although I'm using Iteration ... :-))
The problem is I'm creating a Complex for only 1 attribute where Name = Collection and it is hard coded. However, the new requirement is that I have create a complex collection for another attribute where name=Cost.
This is where I'm having problem. How can I do this. Below are the sample source and output xml and XSLT. Thanks in Advance.
Source XML:
<?xml version="1.0" encoding="Windows-1252"?>
<XML>
<Attributes>
<Attribute>
<Name>FamilyName</Name>
<Type>common</Type>
<Value>Footwear</Value>
<FromIM>no</FromIM>
<collection>N</collection>
<Path />
</Attribute>
<Attribute>
<Name>DepartmentName</Name>
<Type>common</Type>
<Value>Footwear</Value>
<FromIM>no</FromIM>
<collection>N</collection>
<Path />
</Attribute>
<Attribute>
<Name>GroupName</Name>
<Type>common</Type>
<Value>Men's Boots</Value>
<FromIM>no</FromIM>
<collection>N</collection>
<Path />
</Attribute>
<Attribute>
<Name>Buyer ID</Name>
<Type>common</Type>
<Value>Lee</Value>
<FromIM>no</FromIM>
<collection>N</collection>
<Path />
</Attribute>
<Attribute>
<Name>Enviornment</Name>
<Type>common</Type>
<Value>Dev</Value>
<FromIM>no</FromIM>
<collection>N</collection>
<Path />
</Attribute>
<Attribute>
<Name>Retail</Name>
<Type>common</Type>
<Value></Value>
<FromIM>no</FromIM>
<collection>N</collection>
<Path />
</Attribute>
<Attribute>
<Name>Gender</Name>
<Type>category</Type>
<Value>M</Value>
<FromIM>no</FromIM>
<collection>N</collection>
<Path />
</Attribute>
<Attribute>
<Name>Cost</Name>
<Type>Complex</Type>
<Value>20.00</Value>
<FromIM>yes</FromIM>
<collection>Y</collection>
<Path />
</Attribute>
<Attribute>
<Name>Collection</Name>
<Type>Complex</Type>
<Value>ing</Value>
<FromIM>no</FromIM>
<collection>N</collection>
<Path />
</Attribute>
<Attribute>
<Name>UPC</Name>
<Type>Complex</Type>
<Value>Testing</Value>
<FromIM>no</FromIM>
<collection>N</collection>
<Path />
</Attribute>
<Attribute>
<Name>Color</Name>
<Type>Complex</Type>
<Value>Yellow</Value>
<FromIM>no</FromIM>
<collection>N</collection>
<Path />
</Attribute>
<Attribute>
<Name>Size</Name>
<Type>Complex</Type>
<Value>10</Value>
<FromIM>no</FromIM>
<collection>N</collection>
<Path />
</Attribute>
<Attribute>
<Name>Style</Name>
<Type>Complex</Type>
<Value>MA</Value>
<FromIM>no</FromIM>
<collection>N</collection>
<Path />
</Attribute>
<Attribute>
<Name>UPC</Name>
<Type>Complex</Type>
<Value>24a</Value>
<FromIM>no</FromIM>
<collection>N</collection>
<Path />
</Attribute>
<Attribute>
<Name>Color</Name>
<Type>Complex</Type>
<Value>Green</Value>
<FromIM>no</FromIM>
<collection>N</collection>
<Path />
</Attribute>
<Attribute>
<Name>Size</Name>
<Type>Complex</Type>
<Value>22</Value>
<FromIM>no</FromIM>
<collection>N</collection>
<Path />
</Attribute>
<Attribute>
<Name>Style</Name>
<Type>Complex</Type>
<Value>AM</Value>
<FromIM>no</FromIM>
<collection>N</collection>
<Path />
</Attribute>
</Attributes>
</XML>
Expected Output:
I need 2 collection nodes also in date I need to put current date.
Note in the Collection Node I can have multiple Complex nodes. However, in the Cost I will have only 1 Complex node.
<?xml version="1.0" encoding="utf-8"?>
<Data Schema="XML A">
<Items>
<Item id="" shortname="FT123" longname="FT123" sku="FT123" action="ADD" categorypath="FamilyName//DepartmentName//GroupName" type="Product">
<Attributes type="common">
<Attr name="Buyer ID" value="Lee" path="" action="ADD" />
<Attr name="Enviornment" value="Dev" path="" action="ADD" />
<Attr name="Retail" value="" path="" action="ADD" />
<Collection id="" name="Collection" path="">
<Complex>
<Attr name="UPC" value="Testing" valueKey="0" />
<Attr name="Color" value="Yellow" valueKey="0"/>
<Attr name="Size" value="10" valueKey="0"/>
<Attr name="Style" value="MA" valueKey="0"/>
</Complex>
<Complex>
<Attr name="UPC" value="24a" valueKey="0"/>
<Attr name="Color" value="Green" valueKey="0"/>
<Attr name="Size" value="22" valueKey="0"/>
<Attr name="Style" value="AM" valueKey="0"/>
</Complex>
</Collection>
<Collection id="" name="Cost" path="">
<Complex>
<Attr name="Cost" value="22" valueKey="0" />
<Attr name="Date" value="" valueKey="0"/>
</Complex>
</Collection>
</Attributes>
<Attributes type="category">
<Attr name="Gender" value="M" />
</Attributes>
</Item>
</Items>
</Data>
XSLT: Updated based on Michael's comment
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="type" match="Attribute" use="Type"/>
<xsl:template match="/">
<Data Schema="XML A">
<Items>
<Item>
<xsl:variable name="fileName" select="XML/Attributes/Attribute[Name = 'PIFileNumber']/Value"/>
<xsl:attribute name="id"></xsl:attribute>
<xsl:attribute name="shortname">
<xsl:value-of select="$fileName"/>
</xsl:attribute>
<xsl:attribute name="longname">
<xsl:value-of select="$fileName"/>
</xsl:attribute>
<xsl:variable name="familyName" select="XML/Attributes/Attribute[Name = 'FamilyName'/id"/>
<xsl:variable name="deptName" select="XML/Attributes/Attribute[Name = 'DepartmentName']/id"/>
<xsl:variable name="groupName" select="XML/Attributes/Attribute[Name = 'GroupName']/id"/>
<xsl:variable name="catPath" select="concat($familyName,'//',$deptName,'//',$groupName)" />
<xsl:attribute name="categorypath" select="$catPath"/>
<xsl:attribute name="type">Product</xsl:attribute>
<xsl:apply-templates select="XML/Attributes/Attribute[generate-id() = generate-id(key('type', Type)[1])]">
<xsl:sort select="Type" order="descending"/>
</xsl:apply-templates>
</Item>
</Items>
</Data>
</xsl:template>
<xsl:template match="Attribute">
<xsl:variable name="compType" select="count(/XML/Attributes/Attribute[Type='Complex' and Name!='Collection'])"/>
<xsl:variable name="colid" select="/XML/Attributes/Attribute[Name = 'Collection']/id"/>
<xsl:variable name="colname" select="/XML/Attributes/Attribute[Name = 'Collection']/Name"/>
<xsl:variable name="colpath" select="/XML/Attributes/Attribute[Name = 'Collection']/Path"/>
<xsl:if test="Type!='Complex'">
<Attributes type="{Type}">
<xsl:apply-templates select="key('type',Type)" mode="out"/>
<xsl:if test="Type='common'">
<Collection id="{$colid}" name="{$colname}" path="{$colpath}" action="ADD">
<xsl:choose>
<xsl:when test="$compType > 0">
<xsl:call-template name="for.loop">
<xsl:with-param name="i">1</xsl:with-param>
<xsl:with-param name="count" select="count(/XML/Attributes/Attribute[Type='Complex' and Name='UPC'])" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<Complex refId="0">
<MaskValue />
<Attr id="" name="UPC" value="" valueKey="0"/>
<xsl:choose>
<xsl:when test="count(/XML/Attributes/Attribute[Name = 'Color']) > 0">
<Attr id="{//Attribute[Name = 'Color']/id}" name="Color" value="{//Attribute[Name = 'Color']/Value}" valueKey="0"/>
</xsl:when>
<xsl:otherwise>
<Attr id="" name="Color" value="Default" valueKey="0"/>
</xsl:otherwise>
</xsl:choose>
<xsl:choose>
<xsl:when test="count(/XML/Attributes/Attribute[Name = 'Size']) > 0">
<Attr id="{//Attribute[Name = 'Size']/id}" name="Color" value="{//Attribute[Name = 'Size']/Value}" valueKey="0"/>
</xsl:when>
<xsl:otherwise>
<Attr id="" name="Size" value="Default" valueKey="0"/>
</xsl:otherwise>
</xsl:choose>
<Attr id="" name="Style" value="" valueKey="0"/>
<Attr id="" name="Exclude" value="0" valueKey="0"/>
</Complex>
</xsl:otherwise>
</xsl:choose>
</Collection>
</xsl:if>
</Attributes>
</xsl:if>
</xsl:template>
<xsl:template match="Attribute" mode="out">
<xsl:if test="FromIM = 'yes'">
<xsl:choose>
<xsl:when test="collection = 'Y' and Name!='Color' and Name!='Size'">
<Collection id="" name="{Name}" path="{Path}">
<Attr value="{Value}" uom="" locale="en_WW"/>
</Collection>
</xsl:when>
<xsl:otherwise>
<xsl:if test="Name!='FileNumber' and Name!='NotReqInIM' and Name!='Color' and Name!='Size'">
<Attr id="{id}" name="{Name}" value="{Value}" path="{Path}" action="ADD" uom="" Locale="en_WW"/>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
<xsl:template match="Attribute[Type='Complex']" mode="out">
<xsl:if test="Name!='Collection'">
<Attr id="{id}" name="{Name}" value="{Value}" valueKey="0"/>
</xsl:if>
</xsl:template>
<!-- this is for loop code -->
<xsl:template name="for.loop">
<xsl:param name="i" />
<xsl:param name="count" />
<!--begin_: Line_by_Line_Output -->
<xsl:if test="$i <= $count">
<xsl:if test="Name!='Collection'">
<Complex refId="0">
<MaskValue />
<Attr id="{(//Attribute[Type='Complex' and Name = 'UPC'])[position() = $i]/id}" name="UPC" value="{(//Attribute[Type='Complex' and Name = 'UPC'])[position() = $i]/Value}" valueKey="0"/>
<Attr id="{(//Attribute[Type='Complex' and Name = 'Color'])[position() = $i]/id}" name="Color" value="{(//Attribute[Type='Complex' and Name = 'Color'])[position() = $i]/Value}" valueKey="0"/>
<Attr id="{(//Attribute[Type='Complex' and Name = 'Size'])[position() = $i]/id}" name="Size" value="{(//Attribute[Type='Complex' and Name = 'Size'])[position() = $i]/Value}" valueKey="0"/>
<Attr id="{(//Attribute[Type='Complex' and Name = 'Style'])[position() = $i]/id}" name="Style" value="{(//Attribute[Type='Complex' and Name = 'Style'])[position() = $i]/Value}" valueKey="0"/>
<Attr id="0" name="Exclude" value="0" valueKey="0"/>
</Complex>
</xsl:if>
</xsl:if>
<!--begin_: RepeatTheLoopUntilFinished-->
<xsl:if test="$i <= $count">
<xsl:call-template name="for.loop">
<xsl:with-param name="i">
<xsl:value-of select="$i + 1"/>
</xsl:with-param>
<xsl:with-param name="count">
<xsl:value-of select="$count"/>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Although far from perfect, here is one stylesheet that will produce the output you wanted. I'm not sure to what degree the attributes of each complex are fixed or the collection are fixed. Let me know if this helps at all.
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:key name="type" match="Attribute" use="Type"/>
<xsl:template match="/">
<Data Schema="XML A">
<Items>
<Item>
<xsl:variable name="fileName" select="XML/Attributes/Attribute/Name['PIFileNumber']/Value"/>
<xsl:attribute name="id"></xsl:attribute>
<xsl:attribute name="shortname">
<xsl:value-of select="$fileName"/>
</xsl:attribute>
<xsl:attribute name="longname">
<xsl:value-of select="$fileName"/>
</xsl:attribute>
<xsl:variable name="familyName" select="XML/Attributes/Attribute[Name='FamilyName']/Value"/>
<xsl:variable name="deptName" select="XML/Attributes/Attribute[Name='DepartmentName']/Value"/>
<xsl:variable name="groupName" select="XML/Attributes/Attribute[Name='GroupName']/Value"/>
<xsl:variable name="catPath" select="concat($familyName,'//',$deptName,'//',$groupName)" />
<xsl:attribute name="categorypath">
<xsl:value-of select="$catPath"/>
</xsl:attribute>
<xsl:attribute name="type">Product</xsl:attribute>
<xsl:apply-templates select="XML/Attributes/Attribute[generate-id() = generate-id(key('type', Type)[1])]">
<xsl:sort select="Type" order="descending"/>
</xsl:apply-templates>
</Item>
</Items>
</Data>
</xsl:template>
<xsl:template match="Attribute" mode="Collection">
<Collection id="" name='Collection'>
<xsl:apply-templates select="../Attribute[Name='Collection']" mode="Coll"/>
</Collection>
<Collection id="" name='Cost'>
<xsl:apply-templates select="../Attribute[Name='Cost']" mode="Cost"/>
</Collection>
</xsl:template>
<xsl:template match="Attribute[Type='Complex']"/>
<xsl:template match="Attribute[Type != 'Complex']">
<Attributes type="{Type}">
<xsl:variable name="Type" select="Type"/>
<xsl:apply-templates select="../Attribute[Type=$Type]" mode="Attr"/>
<xsl:if test="Type='common'">
<xsl:apply-templates select="." mode="Collection"/>
</xsl:if>
</Attributes>
</xsl:template>
<xsl:template match="Attribute" mode="Coll">
<xsl:apply-templates select="../Attribute[Name='UPC']" mode="UPC"/>
</xsl:template>
<xsl:template match="Attribute" mode="Cost">
<complex>
<Attr name="Cost" value="{../Attribute[Name='Cost']/Value}" valueKey="0"/>
<Attr name="Date" value="" valueKey="0"/>
</complex>
</xsl:template>
<xsl:template match="Attribute" mode="Attr">
<Attr name="{Name}" value="{Value}" valueKey="0"/>
</xsl:template>
<xsl:template match="Attribute" mode="UPC">
<complex>
<Attr name="UPC" value="{Value}" valueKey="0"/>
<Attr name="Color" value="{following::node()[Name='Color']/Value}" valueKey="0"/>
<Attr name="Size" value="{following::node()[Name='Size']/Value}" valueKey="0"/>
<Attr name="Style" value="{following::node()[Name='Style']/Value}" valueKey="0"/>
</complex>
</xsl:template>
</xsl:stylesheet>
I need to validate the source XML and look for Attributes/Attribute/Name. If Name = 'ComplexAttr' then make it child node of Data/Attributes(where #Type='common')/Collection/ComplexAttr. And if it is not present then create a node with default values. However, I have to validate all nodes with #Type='ComplexAttr' so it should be as dynamic as possible.
In the source XML you can see that I have only 1 node where #Type='ComplexAttr'. However, in the Transformed sample XML I have two nodes for <Attr>. This is I want to do with the following XSLT. Please let me know how I can do this.
Thanks in advance.
XSLT:
<!DOCTYPE xsl:stylesheet [<!ENTITY key "concat(Type[. != 'ComplexAttr'],substring('common',1 div (Type = 'ComplexAttr')))">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="type" match="Attribute" use="&key;"/>
<xsl:template match="/">
<Data Schema="XML A">
<xsl:apply-templates
select="XML/Attributes/Attribute[
generate-id() = generate-id(key('type', &key;)[1])
]">
<xsl:sort select="&key;" order="descending"/>
</xsl:apply-templates>
<errorCodes>
<xsl:apply-templates select="XML/Attributes/Attribute"
mode="errors"/>
</errorCodes>
</Data>
</xsl:template>
<xsl:template match="Attribute">
<xsl:variable name="vCurrent-Grouping-Key" select="&key;"/>
<Attributes type="{$vCurrent-Grouping-Key}">
<xsl:apply-templates select="key('type',$vCurrent-Grouping-Key)"
mode="out"/>
</Attributes>
</xsl:template>
<xsl:template match="Attribute" mode="out" name="makeAttr">
<Attr id="{id}" name="{Name}" value="{Value}"/>
</xsl:template>
<xsl:template match="Attribute[Type='ComplexAttr']" mode="out">
<Collection id="" name="test">
<ComplexAttr refId="0">
<MaskValue />
<xsl:call-template name="makeAttr"/>
</ComplexAttr>
</Collection>
</xsl:template>
<xsl:template match="Attribute" mode="errors"/>
<xsl:template match="Attribute[Value='']" mode="errors">
<errorCode>"value for <xsl:value-of select="Name"/> is missing."</errorCode>
</xsl:template>
</xsl:stylesheet>
Source XML:
<?xml version="1.0" encoding="windows-1252"?>
<XML>
<Attributes>
<Attribute>
<id>5</id>
<Name>Buyer ID</Name>
<Type>common</Type>
<Value>Lee</Value>
</Attribute>
<Attribute>
<id>331</id>
<Name>Enviornment</Name>
<Type>common</Type>
<Value>Development</Value>
</Attribute>
<Attribute>
<id>79</id>
<Name>Retail</Name>
<Type>common</Type>
<Value></Value>
</Attribute>
<Attribute>
<id>402</id>
<Name>Gender</Name>
<Type>category</Type>
<Value>Men</Value>
</Attribute>
<Attribute>
<id>1197</id>
<Name>UPC</Name>
<Type>ComplexAttr</Type>
<Value>Testing</Value>
<Path />
</Attribute>
</Attributes>
---- Transformed XML
<Data Schema="XML A">
<Attributes type="common">
<Attr id="5" name="Buyer ID" value="Lee" />
<Attr id="331" name="Enviornment" value="Development" />
<Attr id="79" name="Retail" value="" />
<Collection id="" name="test">
<ComplexAttr refId="0">
<MaskValue />
<Attr id="1197" name="UPC" value="Testing" />
<Attr id="123" name="Size" value="Test" />
</ComplexAttr>
</Collection>
</Attributes>
<Attributes type="category">
<Attr id="402" name="Gender" value="Men" />
</Attributes>
<errorCodes>
<errorCode>"value for Retail is missing."</errorCode>
</errorCodes>
</Data>
Update: Complete stylesheet with more push style approach (it's late, you know...)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="type" match="Attribute" use="Type"/>
<xsl:template match="/">
<Data Schema="XML A">
<xsl:apply-templates
select="XML/Attributes/Attribute[
generate-id() = generate-id(key('type', Type)[1])
]">
<xsl:sort select="Type" order="descending"/>
</xsl:apply-templates>
<errorCodes>
<xsl:apply-templates select="XML/Attributes/Attribute"
mode="errors"/>
</errorCodes>
</Data>
</xsl:template>
<xsl:template match="Attribute">
<xsl:if test="Type!='ComplexAttr'">
<Attributes type="{Type}">
<xsl:apply-templates select="key('type',Type)"
mode="out"/>
<xsl:if test="Type='common'">
<Collection id="" name="test">
<ComplexAttr refId="0">
<MaskValue />
<xsl:apply-templates
select="key('type','ComplexAttr')"
mode="out"/>
</ComplexAttr>
</Collection>
</xsl:if>
</Attributes>
</xsl:if>
</xsl:template>
<xsl:template match="Attribute" mode="out">
<Attr id="{id}" name="{Name}" value="{Value}"/>
</xsl:template>
<xsl:template match="Attribute[Type='ComplexAttr']" mode="out">
<Attr id="{id}"
name="{Name}{substring('UPC',1 div not(Name[normalize-space()]))}"
value="{Value}{substring('Testing',1 div not(Value[normalize-space()]))}"/>
</xsl:template>
<xsl:template match="Attribute" mode="errors"/>
<xsl:template match="Attribute[Value='']" mode="errors">
<errorCode>"value for <xsl:value-of select="Name"/> is missing."</errorCode>
</xsl:template>
</xsl:stylesheet>
Output:
<Data Schema="XML A">
<Attributes type="common">
<Attr id="5" name="Buyer ID" value="Lee"/>
<Attr id="331" name="Enviornment" value="Development"/>
<Attr id="79" name="Retail" value=""/>
<Collection id="" name="test">
<ComplexAttr refId="0">
<MaskValue/>
<Attr id="1197" name="UPC" value="Testing"/>
</ComplexAttr>
</Collection>
</Attributes>
<Attributes type="category">
<Attr id="402" name="Gender" value="Men"/>
</Attributes>
<errorCodes>
<errorCode>
"value for Retail is missing."
</errorCode>
</errorCodes>
</Data>
During transformation, who I can combine one node into another. for example, when Attributes/Attribute/Type=ComplexAttr then it should go under Attributes/Attribute/Type=Common only.
Below is the sample XML & XSLT that I'm trying to use which is not working. TIA (Thanks in Advance)
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="type" match="Attribute" use="Type"/>
<xsl:template match="/">
<Data Schema="XML A">
<xsl:apply-templates select="XML/Attributes/Attribute">
<xsl:sort select="Type" order="descending"/>
</xsl:apply-templates>
<errorCodes>
<xsl:apply-templates select="XML/Attributes/Attribute"
mode="errors"/>
</errorCodes>
</Data>
</xsl:template>
<xsl:template
match="Attribute[generate-id()=generate-id(key('type', Type)[1])]">
<xsl:if test="Type != 'ComplexAttr'">
<Attributes type="{Type}">
<xsl:if test="Type = 'ComplexAttr'">
<xsl:value-of select="Common"/>
</xsl:if>
<xsl:apply-templates select="../Attribute[Type=current()/Type]" mode="out"/>
</Attributes>
</xsl:if>
</xsl:template>
<xsl:template match="Attribute" mode="out">
<Attr id="{id}" name="{Name}" value="{Value}"/>
</xsl:template>
<xsl:template match="Attribute"/>
<xsl:template match="Attribute" mode="errors"/>
<xsl:template match="Attribute[Value='']" mode="errors">
<errorCode>"value for <xsl:value-of select="Name"/> is missing."</errorCode>
</xsl:template>
<xsl:template match="/Attribute">
<xsl:if test="Type = 'ComplexAttr'">
<Attributes type="Common">
<xsl:apply-templates select="../Attribute[Type=current()/Type]" mode="out"/>
<!--<Attr id="{id}" name="{Name}" value="{Value}"/>-->
</Attributes>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
---- Source XML ----
<?xml version="1.0" encoding="windows-1252"?>
<XML>
<Attributes>
<Attribute>
<id>5</id>
<Name>Buyer ID</Name>
<Type>common</Type>
<Value>Lee</Value>
</Attribute>
<Attribute>
<id>331</id>
<Name>Enviornment</Name>
<Type>common</Type>
<Value>Development</Value>
</Attribute>
<Attribute>
<id>79</id>
<Name>Retail</Name>
<Type>common</Type>
<Value></Value>
</Attribute>
<Attribute>
<id>402</id>
<Name>Gender</Name>
<Type>category</Type>
<Value>Men</Value>
</Attribute>
<Attribute>
<id>1197</id>
<Name>UPC</Name>
<Type>ComplexAttr</Type>
<Value>Testing</Value>
<Path />
</Attribute>
</Attributes>
</XML>
---- Transformed XML output
<?xml version="1.0" encoding="utf-8"?>
<Data Schema="XML A">
<Attributes type="common">
<Attr id="5" name="Buyer ID" value="Lee" />
<Attr id="331" name="Enviornment" value="Development" />
<Attr id="79" name="Retail" value="" />
<Attr id="41" name="PlusShip" value="False" />
<Collection id="" name="test">
<ComplexAttr refId="0">
<MaskValue />
<Attr id="1197" name="UPC" value="Testing" />
</ComplexAttr>
</Collection>
</Attributes>
<Attributes type="category">
<Attr id="402" name="Gender" value="Men" />
<Attr id="433" name="HeelHeight" value="" />
</Attributes>
<errorCodes>
<errorCode>"value for Retail is missing."</errorCode>
</errorCodes>
</Data>
If you want to group Attribute by Type with 'common' and 'ComplexAttr' in the same group, then you need to change the key value expression into something like:
<xsl:key name="type"
match="Attribute"
use="concat(
Type[. != 'ComplexAttr'],
substring(
'common',
1 div (Type = 'ComplexAttr')
)
)"/>
<xsl:template match="Attribute[
generate-id()
= generate-id(
key('type',
concat(
Type[. != 'ComplexAttr'],
substring(
'common',
1 div (Type = 'ComplexAttr')
)
)
)[1]
)
]">
EDIT: And in the group templates applying:
<xsl:apply-templates select="key('type',
concat(
Type[. != 'ComplexAttr'],
substring(
'common',
1 div (Type = 'ComplexAttr')
)
)
)"
mode="out"/>
EDIT: Full example. This stylesheet:
<!DOCTYPE xsl:stylesheet [
<!ENTITY key "concat(Type[. != 'ComplexAttr'],substring('common',1 div (Type = 'ComplexAttr')))">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="type" match="Attribute" use="&key;"/>
<xsl:template match="/">
<Data Schema="XML A">
<xsl:apply-templates
select="XML/Attributes/Attribute[
generate-id() = generate-id(key('type', &key;)[1])
]">
<xsl:sort select="&key;" order="descending"/>
</xsl:apply-templates>
<errorCodes>
<xsl:apply-templates select="XML/Attributes/Attribute"
mode="errors"/>
</errorCodes>
</Data>
</xsl:template>
<xsl:template match="Attribute">
<xsl:variable name="vCurrent-Grouping-Key" select="&key;"/>
<Attributes type="{$vCurrent-Grouping-Key}">
<xsl:apply-templates select="key('type',$vCurrent-Grouping-Key)"
mode="out"/>
</Attributes>
</xsl:template>
<xsl:template match="Attribute" mode="out" name="makeAttr">
<Attr id="{id}" name="{Name}" value="{Value}"/>
</xsl:template>
<xsl:template match="Attribute[Type='ComplexAttr']" mode="out">
<Collection id="" name="test">
<ComplexAttr refId="0">
<MaskValue />
<xsl:call-template name="makeAttr"/>
</ComplexAttr>
</Collection>
</xsl:template>
<xsl:template match="Attribute" mode="errors"/>
<xsl:template match="Attribute[Value='']" mode="errors">
<errorCode>"value for <xsl:value-of select="Name"/> is missing."</errorCode>
</xsl:template>
</xsl:stylesheet>
Output:
<Data Schema="XML A">
<Attributes type="common">
<Attr id="5" name="Buyer ID" value="Lee" />
<Attr id="331" name="Enviornment" value="Development" />
<Attr id="79" name="Retail" value="" />
<Collection id="" name="test">
<ComplexAttr refId="0">
<MaskValue />
<Attr id="1197" name="UPC" value="Testing" />
</ComplexAttr>
</Collection>
</Attributes>
<Attributes type="category">
<Attr id="402" name="Gender" value="Men" />
</Attributes>
<errorCodes>
<errorCode>"value for Retail is missing."</errorCode>
</errorCodes>
</Data>
I'm trying to transform this xml. However I'm having formatting issues. Could someone please guide me to solve this problem. Thanks in advance
<?xml version="1.0" encoding="windows-1252"?>
<XML>
<Attributes>
<Attribute>
<id>5</id>
<Name>Buyer ID</Name>
<Type>common</Type>
<Value>Lee</Value>
</Attribute>
<Attribute>
<id>331</id>
<Name>Enviornment</Name>
<Type>common</Type>
<Value>Development</Value>
</Attribute>
<Attribute>
<id>79</id>
<Name>Retail</Name>
<Type>common</Type>
<Value></Value>
</Attribute>
<Attribute>
<id>402</id>
<Name>Gender</Name>
<Type>category</Type>
<Value>Men</Value>
</Attribute>
<Attribute>
<id>433</id>
<Name>HeelHeight</Name>
<Type>category</Type>
<Value></Value>
</Attribute>
<Attribute>
<id>41</id>
<Name>PlusShip</Name>
<Type>common</Type>
<Value>False</Value>
<Path></Path>
</Attribute>
</Attributes>
</XML>
Into the following XML. Could someone please give me some tips in how to transform this xml based on the value of Attributes/Attribute/Type
<?xml version="1.0" encoding="utf-8" ?>
<Data Schema="XML A">
<Attributes type="Common">
<Attr id="" name="Buyer ID" value="Lee" />
<Attr id="" name="Enviornment" value="Development" />
<Attr id="" name="Retail" value="" />
<Attr id="" name="PlusShip" value="False" />
</Attributes>
<Attributes type="Category">
<Attr id="" name="Gender" value="Men" />
<Attr id="" name="HeelHeight" value="" />
</Attributes>
The following stylesheet produces the desired result:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<Data Schema="XML A">
<xsl:apply-templates/>
</Data>
</xsl:template>
<xsl:template match="Attribute[not(Type=following::Type)]">
<Attributes type="{Type}">
<xsl:for-each select="../Attribute[Type=current()/Type]">
<Attr id="{id}" name="{Name}" value="{Value}"/>
</xsl:for-each>
</Attributes>
</xsl:template>
<xsl:template match="Attribute"/>
</xsl:stylesheet>
Output on your source document:
<Data Schema="XML A">
<Attributes type="category">
<Attr id="402" name="Gender" value="Men"/>
<Attr id="433" name="HeelHeight" value=""/>
</Attributes>
<Attributes type="common">
<Attr id="5" name="Buyer ID" value="Lee"/>
<Attr id="331" name="Enviornment" value="Development"/>
<Attr id="79" name="Retail" value=""/>
<Attr id="41" name="PlusShip" value="False"/>
</Attributes>
</Data>
Edit: OK, let's get rid of that ugly for-each:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<Data Schema="XML A">
<xsl:apply-templates/>
</Data>
</xsl:template>
<xsl:template match="Attribute[not(Type=following::Type)]">
<Attributes type="{Type}">
<xsl:apply-templates
select="../Attribute[Type=current()/Type]" mode="out"/>
</Attributes>
</xsl:template>
<xsl:template match="Attribute" mode="out">
<Attr id="{id}" name="{Name}" value="{Value}"/>
</xsl:template>
<xsl:template match="Attribute"/>
</xsl:stylesheet>
I feel much better.
Edit #2: Using the Muenchian Method (with sorting):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="type" match="Attribute" use="Type"/>
<xsl:template match="/">
<Data Schema="XML A">
<xsl:apply-templates select="XML/Attributes/Attribute">
<xsl:sort select="Type" order="descending"/>
</xsl:apply-templates>
</Data>
</xsl:template>
<xsl:template
match="Attribute[generate-id()=generate-id(key('type', Type)[1])]">
<Attributes type="{Type}">
<xsl:apply-templates
select="../Attribute[Type=current()/Type]" mode="out"/>
</Attributes>
</xsl:template>
<xsl:template match="Attribute" mode="out">
<Attr id="{id}" name="{Name}" value="{Value}"/>
</xsl:template>
<xsl:template match="Attribute"/>
</xsl:stylesheet>
Produces the following (ordered) output:
<Data Schema="XML A">
<Attributes type="common">
<Attr id="5" name="Buyer ID" value="Lee"/>
<Attr id="331" name="Enviornment" value="Development"/>
<Attr id="79" name="Retail" value=""/>
<Attr id="41" name="PlusShip" value="False"/>
</Attributes>
<Attributes type="category">
<Attr id="402" name="Gender" value="Men"/>
<Attr id="433" name="HeelHeight" value=""/>
</Attributes>
</Data>
I'm trying to transform this xml. However I'm having formatting issues. Could someone please guide me to solve this problem. Thanks in advance
<?xml version="1.0" encoding="windows-1252"?>
<XML>
<Attributes>
<Attribute>
<id>5</id>
<Name>Buyer ID</Name>
<Type>common</Type>
<Value>Lee</Value>
</Attribute>
<Attribute>
<id>331</id>
<Name>Enviornment</Name>
<Type>common</Type>
<Value>Development</Value>
</Attribute>
<Attribute>
<id>79</id>
<Name>Retail</Name>
<Type>common</Type>
<Value></Value>
</Attribute>
<Attribute>
<id>402</id>
<Name>Gender</Name>
<Type>category</Type>
<Value>Men</Value>
</Attribute>
<Attribute>
<id>433</id>
<Name>HeelHeight</Name>
<Type>category</Type>
<Value></Value>
</Attribute>
<Attribute>
<id>41</id>
<Name>PlusShip</Name>
<Type>common</Type>
<Value>False</Value>
<Path></Path>
</Attribute>
</Attributes>
</XML>
Into the following XML. Could someone please give me some tips in how to transform this xml based on the value of Attributes/Attribute/Type
<?xml version="1.0" encoding="utf-8" ?>
<Data Schema="XML A">
<Attributes type="Common">
<Attr id="" name="Buyer ID" value="Lee" />
<Attr id="" name="Enviornment" value="Development" />
<Attr id="" name="Retail" value="" />
<Attr id="" name="PlusShip" value="False" />
</Attributes>
<Attributes type="Category">
<Attr id="" name="Gender" value="Men" />
<Attr id="" name="HeelHeight" value="" />
</Attributes>
The following stylesheet produces the desired result:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<Data Schema="XML A">
<xsl:apply-templates/>
</Data>
</xsl:template>
<xsl:template match="Attribute[not(Type=following::Type)]">
<Attributes type="{Type}">
<xsl:for-each select="../Attribute[Type=current()/Type]">
<Attr id="{id}" name="{Name}" value="{Value}"/>
</xsl:for-each>
</Attributes>
</xsl:template>
<xsl:template match="Attribute"/>
</xsl:stylesheet>
Output on your source document:
<Data Schema="XML A">
<Attributes type="category">
<Attr id="402" name="Gender" value="Men"/>
<Attr id="433" name="HeelHeight" value=""/>
</Attributes>
<Attributes type="common">
<Attr id="5" name="Buyer ID" value="Lee"/>
<Attr id="331" name="Enviornment" value="Development"/>
<Attr id="79" name="Retail" value=""/>
<Attr id="41" name="PlusShip" value="False"/>
</Attributes>
</Data>
Edit: OK, let's get rid of that ugly for-each:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<Data Schema="XML A">
<xsl:apply-templates/>
</Data>
</xsl:template>
<xsl:template match="Attribute[not(Type=following::Type)]">
<Attributes type="{Type}">
<xsl:apply-templates
select="../Attribute[Type=current()/Type]" mode="out"/>
</Attributes>
</xsl:template>
<xsl:template match="Attribute" mode="out">
<Attr id="{id}" name="{Name}" value="{Value}"/>
</xsl:template>
<xsl:template match="Attribute"/>
</xsl:stylesheet>
I feel much better.
Edit #2: Using the Muenchian Method (with sorting):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="type" match="Attribute" use="Type"/>
<xsl:template match="/">
<Data Schema="XML A">
<xsl:apply-templates select="XML/Attributes/Attribute">
<xsl:sort select="Type" order="descending"/>
</xsl:apply-templates>
</Data>
</xsl:template>
<xsl:template
match="Attribute[generate-id()=generate-id(key('type', Type)[1])]">
<Attributes type="{Type}">
<xsl:apply-templates
select="../Attribute[Type=current()/Type]" mode="out"/>
</Attributes>
</xsl:template>
<xsl:template match="Attribute" mode="out">
<Attr id="{id}" name="{Name}" value="{Value}"/>
</xsl:template>
<xsl:template match="Attribute"/>
</xsl:stylesheet>
Produces the following (ordered) output:
<Data Schema="XML A">
<Attributes type="common">
<Attr id="5" name="Buyer ID" value="Lee"/>
<Attr id="331" name="Enviornment" value="Development"/>
<Attr id="79" name="Retail" value=""/>
<Attr id="41" name="PlusShip" value="False"/>
</Attributes>
<Attributes type="category">
<Attr id="402" name="Gender" value="Men"/>
<Attr id="433" name="HeelHeight" value=""/>
</Attributes>
</Data>