I Have an XML file like the one below. What I need to do is to add a Flag to each <TOKEN> node with a <MULTIPLE TYPE='YES'> node when:
there exist more than one <TOKEN> with the same #BEGIN-#END
at least one of the <TOKEN> #TEXT is different from the others with the same#BEGIN-#END
The xml code is the following:
<?xml version="1.0" encoding="UTF-8"?>
<ALL>
<RECORD TEMPLATE="PRODUCTS" DB="0">
<FIELD NAME="PRODUCT" BASE="AST" >
<TOKEN TEXT="AST" BEGIN="0" END="100"/>
</FIELD>
</RECORD>
<RECORD TEMPLATE="PRODUCTS" DB="1" >
<FIELD NAME="PRODUCT" BASE="BUC" >
<TOKEN TEXT="BUC" BEGIN="0" END="100"/>
<TOKEN TEXT="BUC" BEGIN="0" END="100"/>
<TOKEN TEXT="BUC" BEGIN="0" END="100"/>
<TOKEN TEXT="BUC" BEGIN="0" END="100"/>
<TOKEN TEXT="BUC" BEGIN="102" END="133"/>
<TOKEN TEXT="BUC" BEGIN="102" END="133"/>
</FIELD>
</RECORD>
<RECORD TEMPLATE="PRODUCTS" DB="1" >
<FIELD NAME="PRODUCT" BASE="BUC" >
<TOKEN TEXT="ART" BEGIN="300" END="450"/>
<TOKEN TEXT="ART" BEGIN="300" END="450"/>
</FIELD>
</RECORD>
</ALL>
The desired output is the following:
<ALL>
<RECORD DB="0" TEMPLATE="PRODUCTS">
<FIELD BASE="AST" NAME="PRODUCT">
<MULTIPLE TYPE="YES"/>
<TOKEN BEGIN="0" END="100" TEXT="AST"/>
</FIELD>
</RECORD>
<RECORD DB="1" TEMPLATE="PRODUCTS">
<FIELD BASE="BUC" NAME="PRODUCT">
<MULTIPLE TYPE="YES"/>
<TOKEN BEGIN="0" END="100" TEXT="BUC"/>
<MULTIPLE TYPE="YES"/>
<TOKEN BEGIN="0" END="100" TEXT="BUC"/>
<MULTIPLE TYPE="YES"/>
<TOKEN BEGIN="0" END="100" TEXT="BUC"/>
<MULTIPLE TYPE="YES"/>
<TOKEN BEGIN="0" END="100" TEXT="BUC"/>
<TOKEN BEGIN="102" END="133" TEXT="BUC"/>
<TOKEN BEGIN="102" END="133" TEXT="BUC"/>
</FIELD>
</RECORD>
<RECORD DB="1" TEMPLATE="PRODUCTS">
<FIELD BASE="BUC" NAME="PRODUCT">
<TOKEN BEGIN="300" END="450" TEXT="ART"/>
<TOKEN BEGIN="300" END="450" TEXT="ART"/>
</FIELD>
</RECORD>
</ALL>
I tried using the following xslt, and try to match either the #BEGIN-#END and the #TEXT, but it didn't work.
<?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" cdata-section-elements="DOCUMENT" method="xml" indent="yes" omit-xml-declaration="no" />
<xsl:key name="token" match="//RECORD[#TEMPLATE='PRODUCTS']/FIELD[#NAME='PRODUCT']/TOKEN" use="concat(#BEGIN, '|', #END)"/>
<xsl:key name="text" match="//RECORD[#TEMPLATE='PRODUCTS']/FIELD[#NAME='PRODUCT']/TOKEN" use="#TEXT"/>
<xsl:template match="#*|node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//RECORD[#TEMPLATE='PRODUCTS']/FIELD[#NAME='PRODUCT']/TOKEN [key('token', concat(#BEGIN, '|', #END))[2]] [(key('word', #TEXT)[2])] ">
<xsl:element name="MULTIPLE">
<xsl:attribute name="TYPE">YES</xsl:attribute>
</xsl:element>
<xsl:call-template name="identity" />
</xsl:template>
</xsl:stylesheet>
Removing form the xslt above the [(key('word', #TEXT)[2])] part of code, the <MULTIPLE TYPE='YES'> node is added, but on every <TOKEN> with the same #BEGIN-#END, not checking if exists a #TEXT node different from the others.
Can you check this:
<?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" method="xml" indent="yes" omit-xml-declaration="no" />
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="TOKEN">
<xsl:variable name="current" select="concat(#BEGIN,#END)"/>
<xsl:variable name="text" select="#TEXT"/>
<xsl:for-each select="ancestor::RECORD/following-sibling::RECORD/FIELD/TOKEN|ancestor::RECORD/preceding-sibling::RECORD/FIELD/TOKEN">
<xsl:if test="(concat(#BEGIN,#END) = $current) and (#TEXT!=$text)">
<MULTIPLE TYPE="YES"/>
</xsl:if>
</xsl:for-each>
<TOKEN>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates/>
</TOKEN>
</xsl:template>
</xsl:stylesheet>
Related
I have the following XML:
<?xml version="1.0" encoding="utf-8" ?>
<DOCUMENT>
<SECTION>
<PARAGRAPH TRACK="4">
<SENTENCE NAME="PRIMARY" COUNT="4">
<TOKEN BEGIN="9" END="11" SENTENCE_BEGIN="0" SENTENCE_END="156" />
<TOKEN BEGIN="32" END="37" SENTENCE_BEGIN="0" SENTENCE_END="156" />
<TOKEN BEGIN="167" END="169" SENTENCE_BEGIN="158" SENTENCE_END="316" />
<TOKEN BEGIN="210" END="215" SENTENCE_BEGIN="158" SENTENCE_END="316" />
</SENTENCE>
<SENTENCE NAME="SECONDARY" COUNT="2">
<TOKEN BEGIN="139" END="141" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="A" DOUBLE="YES" />
<TOKEN BEGIN="143" END="145" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="B" />
</SENTENCE>
<SENTENCE NAME="SECONDARY" COUNT="1">
<TOKEN BEGIN="17" END="19" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="C" DOUBLE="YES" />
</SENTENCE>
</PARAGRAPH>
</SECTION>
</DOCUMENT>
And I need to obtain the following result, with the node TOKEN selected by the IF statement, updated with the attribute NEW:
<?xml version="1.0" encoding="utf-8" ?>
<DOCUMENT>
<SECTION>
<PARAGRAPH TRACK="4">
<SENTENCE NAME="PRIMARY" COUNT="4">
<TOKEN BEGIN="9" END="11" SENTENCE_BEGIN="0" SENTENCE_END="156" />
<TOKEN BEGIN="32" END="37" SENTENCE_BEGIN="0" SENTENCE_END="156" />
<TOKEN BEGIN="167" END="169" SENTENCE_BEGIN="158" SENTENCE_END="316" />
<TOKEN BEGIN="210" END="215" SENTENCE_BEGIN="158" SENTENCE_END="316" />
</SENTENCE>
<SENTENCE NAME="SECONDARY" COUNT="2">
<TOKEN BEGIN="139" END="141" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="A" DOUBLE="YES" />
<TOKEN BEGIN="143" END="145" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="B" />
</SENTENCE>
<SENTENCE NAME="SECONDARY" COUNT="1">
<TOKEN BEGIN="17" END="19" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="C" DOUBLE="YES" NEW="YES" />
<TOKEN></TOKEN>
</SENTENCE>
</PARAGRAPH>
</SECTION>
</DOCUMENT>
Using the following XSLT transformation I obtain a wrong result, where the node TOKEN selected by the IF statement is copied at the beginning of the tree:
<?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"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:key name="primary_tokens"
match="SENTENCE[#NAME='PRIMARY']/TOKEN"
use="concat(#SENTENCE_BEGIN,'|',#SENTENCE_END)"/>
<xsl:template match="/*">
<xsl:for-each select=".//TOKEN[#DOUBLE='YES'][key('primary_tokens',concat(#SENTENCE_BEGIN,'|',#SENTENCE_END))]">
<xsl:if test="key('primary_tokens',concat(#SENTENCE_BEGIN,'|',#SENTENCE_END))[#BEGIN > current()/#BEGIN]">
<xsl:copy>
<xsl:attribute name="NEW">YES</xsl:attribute>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:if>
</xsl:for-each>
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Unwanted result:
<?xml version="1.0" encoding="utf-16"?>
<TOKEN NEW="YES" BEGIN="17" END="19" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="C" DOUBLE="YES" />
<DOCUMENT>
<SECTION>
<PARAGRAPH TRACK="4">
<SENTENCE NAME="PRIMARY" COUNT="4">
<TOKEN BEGIN="9" END="11" SENTENCE_BEGIN="0" SENTENCE_END="156" />
<TOKEN BEGIN="32" END="37" SENTENCE_BEGIN="0" SENTENCE_END="156" />
<TOKEN BEGIN="167" END="169" SENTENCE_BEGIN="158" SENTENCE_END="316" />
<TOKEN BEGIN="210" END="215" SENTENCE_BEGIN="158" SENTENCE_END="316" />
</SENTENCE>
<SENTENCE NAME="SECONDARY" COUNT="2">
<TOKEN BEGIN="139" END="141" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="A" DOUBLE="YES" />
<TOKEN BEGIN="143" END="145" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="B" />
</SENTENCE>
<SENTENCE NAME="SECONDARY" COUNT="1">
<TOKEN BEGIN="17" END="19" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="C" DOUBLE="YES" />
</SENTENCE>
</PARAGRAPH>
</SECTION>
</DOCUMENT>
I came up with a possible solution, adding in xsl:template the match="//SECTION/PARAGRAPH/SENTENCE[#NAME='SECONDARY']/TOKEN condition. Unfortunately even this solution is not correct, because the content TOKENnode is simply copied in the SENTENCE node:
<?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"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:key name="primary_tokens"
match="SENTENCE[#NAME='PRIMARY']/TOKEN"
use="concat(#SENTENCE_BEGIN,'|',#SENTENCE_END)"/>
<xsl:template match="//SECTION/PARAGRAPH/SENTENCE[#NAME='SECONDARY']/TOKEN">
<xsl:for-each select="current()[key('primary_tokens',concat(#SENTENCE_BEGIN,'|',#SENTENCE_END))]">
<xsl:if test="key('primary_tokens',concat(#SENTENCE_BEGIN,'|',#SENTENCE_END))[#BEGIN > current()/#BEGIN]">
<xsl:attribute name="NEW">YES</xsl:attribute>
<xsl:apply-templates select="#*|node()"/>
</xsl:if>
</xsl:for-each>
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
Wrong output with TOKEN in SENTENCE node:
<?xml version="1.0" encoding="utf-16"?>
<DOCUMENT>
<SECTION>
<PARAGRAPH TRACK="4">
<SENTENCE NAME="PRIMARY" COUNT="4">
<TOKEN BEGIN="9" END="11" SENTENCE_BEGIN="0" SENTENCE_END="156" />
<TOKEN BEGIN="32" END="37" SENTENCE_BEGIN="0" SENTENCE_END="156" />
<TOKEN BEGIN="167" END="169" SENTENCE_BEGIN="158" SENTENCE_END="316" />
<TOKEN BEGIN="210" END="215" SENTENCE_BEGIN="158" SENTENCE_END="316" />
</SENTENCE>
<SENTENCE NAME="SECONDARY" COUNT="2">
<TOKEN BEGIN="139" END="141" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="A" DOUBLE="YES" />
<TOKEN BEGIN="143" END="145" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="B" />
</SENTENCE>
<SENTENCE NAME="SECONDARY" COUNT="1" NEW="YES" BEGIN="17" END="19" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="C" DOUBLE="YES">
<TOKEN BEGIN="17" END="19" SENTENCE_BEGIN="0" SENTENCE_END="156" PROP="C" DOUBLE="YES" />
<TOKEN />
</SENTENCE>
</PARAGRAPH>
</SECTION>
</DOCUMENT>
My question is: How do I obtain the wanted result reported above? Is my second try any close to a good solution?
I think your second try is close; you do want to match TOKEN if that's what you want to update.
Try this...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="primary_tokens"
match="SENTENCE[#NAME='PRIMARY']/TOKEN"
use="concat(#SENTENCE_BEGIN,'|',#SENTENCE_END)"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="TOKEN[#DOUBLE='YES']">
<xsl:copy>
<xsl:if test="key('primary_tokens',concat(#SENTENCE_BEGIN,'|',#SENTENCE_END))[#BEGIN > current()/#BEGIN]">
<xsl:attribute name="NEW">YES</xsl:attribute>
</xsl:if>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Fiddle: http://xsltfiddle.liberty-development.net/bdxtpX
I'm trying to transform the following XML file, to remove each <AGGREGATION> node if followed by a <MULTIPLE> node.
<?xml version="1.0" encoding="UTF-8"?>
<RECORD TEMPLATE="PRODUCTS" TRACK="1">
<FIELD NAME="PRODUCT" BASE="CT300" COUNT="2">
<AGGREGATION DOMAIN="4" />
<MULTIPLE TYPE="YES" />
<MULTIPLE TYPE="YES" />
<MULTIPLE TYPE="YES" />
<MULTIPLE TYPE="YES" />
<MULTIPLE TYPE="YES" />
<TOKEN TEXT="CT300" BEGIN="11379" END="11384"/>
<AGGREGATION DOMAIN="9" />
<AGGREGATION DOMAIN="4" />
<AGGREGATION DOMAIN="4" />
<MULTIPLE TYPE="YES" />
<MULTIPLE TYPE="YES" />
<MULTIPLE TYPE="YES" />
<MULTIPLE TYPE="YES" />
<MULTIPLE TYPE="YES" />
<TOKEN TEXT="CT300" BEGIN="11379" END="11384"/>
</FIELD>
</RECORD>
With the following xslt transformation, I was able to remove only the first occurrence of the <AGGREGATION> node:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//RECORD[#TEMPLATE='PRODUCTS']/FIELD[#NAME='PRODUCT']/AGGREGATION[following-sibling::*[1][self::MULTIPLE]]">
<xsl:choose>
<xsl:when test="//RECORD[#TEMPLATE='PRODUCTS']/FIELD[#NAME='PRODUCT']/AGGREGATION[following-sibling::*[1][self::MULTIPLE]]">
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
This is the output I receive:
<?xml version="1.0" encoding="UTF-8"?>
<RECORD TEMPLATE="PRODUCTS" TRACK="1">
<FIELD BASE="CT300" COUNT="2" NAME="PRODUCT">
<MULTIPLE TYPE="YES"/>
<MULTIPLE TYPE="YES"/>
<MULTIPLE TYPE="YES"/>
<MULTIPLE TYPE="YES"/>
<MULTIPLE TYPE="YES"/>
<TOKEN BEGIN="11379" END="11384" TEXT="CT300"/>
<AGGREGATION DOMAIN="9"/>
<AGGREGATION DOMAIN="4"/>
<MULTIPLE TYPE="YES"/>
<MULTIPLE TYPE="YES"/>
<MULTIPLE TYPE="YES"/>
<MULTIPLE TYPE="YES"/>
<MULTIPLE TYPE="YES"/>
<TOKEN BEGIN="11379" END="11384" TEXT="CT300"/>
</FIELD>
</RECORD>
While the desired output is the following:
<?xml version="1.0" encoding="UTF-8"?>
<RECORD TEMPLATE="PRODUCTS" TRACK="1">
<FIELD BASE="CT300" COUNT="2" NAME="PRODUCT">
<MULTIPLE TYPE="YES"/>
<MULTIPLE TYPE="YES"/>
<MULTIPLE TYPE="YES"/>
<MULTIPLE TYPE="YES"/>
<MULTIPLE TYPE="YES"/>
<TOKEN BEGIN="11379" END="11384" TEXT="CT300"/>
<MULTIPLE TYPE="YES"/>
<MULTIPLE TYPE="YES"/>
<MULTIPLE TYPE="YES"/>
<MULTIPLE TYPE="YES"/>
<MULTIPLE TYPE="YES"/>
<TOKEN BEGIN="11379" END="11384" TEXT="CT300"/>
</FIELD>
</RECORD>
How can I implement a recursive deletion of the <AGGREGATION> node?
My solution is as follows:
Write a template matching AGGREGATION.
The decision whether it shoud copy anything is as follows:
Take the first following sibling with name != AGGREGATION.
Check whether its name is MULTIPLE.
If yes, do nothing (skip the current AGGREGATION element).
If not, apply templates for the current element.
You need also the identity template.
So the whole script looks like below:
<?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" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="AGGREGATION">
<xsl:if test="not(following-sibling::*[name()!='AGGREGATION'][1]
[name()='MULTIPLE'])">
<AGGREGATION>
<xsl:apply-templates select="#*|node()"/>
</AGGREGATION>
</xsl:if>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#*|node()"/></xsl:copy>
</xsl:template>
</xsl:transform>
For a working example see http://xsltransform.net/bEJaofQ/1
Just use this empty template plus the identity template to eradicate the unwanted <AGGREGATION> elements conditionally:
<xsl:template match="AGGREGATION[following-sibling::*[1] = (self::MULTIPLE or self::AGGREGATION)]" />
I think you just need to ignore the following sibling AGGREGATION elements...
<xsl:template match="RECORD[#TEMPLATE='PRODUCTS']/FIELD[#NAME='PRODUCT']/AGGREGATION[following-sibling::*[not(self::AGGREGATION)][1][self::MULTIPLE]]"/>
I have a strange transformation that I'm trying to do.
XML looks like this:
<?xml version="1.0" standalone="yes"?>
<Parent>
<RecordCount>4</RecordCount>
<Record name="1">
<Child1>Value 1</Child1>
<Child2>Value 2</Child2>
</Record>
</Parent>
And this is what it needs to look like:
<?xml version="1.0" standalone="yes"?>
<Parent>
<RecordCount>4</RecordCount>
<Record name="1">
<Child1>Value 1</Child1>
<Child2>Value 2</Child2>
</Record>
<Record name="2">
<Child1>Value 1</Child1>
<Child2>Value 2</Child2>
</Record>
<Record name="3">
<Child1>Value 1</Child1>
<Child2>Value 2</Child2>
</Record>
<Record name="4">
<Child1>Value 1</Child1>
<Child2>Value 2</Child2>
</Record>
</Parent>
Is something like this even possible with XSLT or should I rather just handle this in code?
Here's another way using XSLT 2.0...
XML Input
<Parent>
<RecordCount>4</RecordCount>
<Record name="1">
<Child1>Value 1</Child1>
<Child2>Value 2</Child2>
</Record>
</Parent>
XSLT 2.0
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="RecordCount">
<xsl:variable name="record" select="../Record"/>
<xsl:copy-of select="."/>
<xsl:for-each select="1 to .">
<xsl:apply-templates select="$record" mode="replicate">
<xsl:with-param name="cnt" select="."/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:template>
<xsl:template match="Record" mode="replicate">
<xsl:param name="cnt"/>
<Record name="{$cnt}">
<xsl:apply-templates select="#* except #name|node()"/>
</Record>
</xsl:template>
<xsl:template match="Record"/>
</xsl:stylesheet>
Output
<Parent>
<RecordCount>4</RecordCount>
<Record name="1">
<Child1>Value 1</Child1>
<Child2>Value 2</Child2>
</Record>
<Record name="2">
<Child1>Value 1</Child1>
<Child2>Value 2</Child2>
</Record>
<Record name="3">
<Child1>Value 1</Child1>
<Child2>Value 2</Child2>
</Record>
<Record name="4">
<Child1>Value 1</Child1>
<Child2>Value 2</Child2>
</Record>
</Parent>
Try following xlst
<?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" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/Parent">
<Parent>
<xsl:variable name="count" select="RecordCount" />
<xsl:call-template name="multiply">
<xsl:with-param name="maxCount" select="$count" />
<xsl:with-param name="nodeToCopy" select="Record" />
</xsl:call-template>
</Parent>
</xsl:template>
<xsl:template name="multiply">
<xsl:param name="maxCount" />
<xsl:param name="i" select="1" />
<xsl:param name="nodeToCopy" />
<xsl:choose>
<xsl:when test="$i <= $maxCount">
<xsl:element name="{name($nodeToCopy)}">
<xsl:attribute name="name">
<xsl:value-of select="$i" />
</xsl:attribute>
<xsl:copy-of select="$nodeToCopy/child::*" />
</xsl:element>
<xsl:call-template name="multiply">
<xsl:with-param name="maxCount" select="$maxCount" />
<xsl:with-param name="nodeToCopy" select="$nodeToCopy" />
<xsl:with-param name="i" select="$i+1" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise />
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
It is based on recursive calling of named template with increasing of "iterating" value. If would be something unclear just write a comment.
Im having issues getting my XML to transform correctly using the EXSLT str:replace template.
Here is my XML:
<XML>
<Item>
<Field>
<Name>ID</Name>
<Value>98765</Value>
</Field>
<Field>
<Name>ParentID</Name>
<Value>10002</Value>
</Field>
<Field>
<Name>Type</Name>
<Value>content</Value>
</Field>
<Field>
<Name>Name</Name>
<Value>TestAPI Course</Value>
</Field>
<Field>
<Name>URL</Name>
<Value></Value>
</Field>
<Field>
<Name>Description</Name>
<Value>a description</Value>
</Field>
<Field>
<Name>DateBegin</Name>
<Value>2012-04-04T00:00:00.000-07:00</Value>
</Field>
<Field>
<Name>DateEnd</Name>
<Value>2012-04-04T00:00:00.000-08:00</Value>
</Field>
<Field>
<Name>DateCreated</Name>
<Value>2012-04-03T00:00:00.000-07:00</Value>
</Field>
<Field>
<Name>DateModified</Name>
<Value>2012-04-04T00:00:00.000-06:00</Value>
</Field>
</Item>
Here is my XSL:
<?xml version="1.0" encoding="utf-16"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:str="http://exslt.org/strings" extension-element-prefixes="str" version="1.0">
<xsl:import href="C:\inetpub\wwwroot\LMSConnector\LMS\XSL\str.xsl"/>
<xsl:template match="/Items">
<xsl:call-template name="str:replace">
<xsl:with-param name="string" select="Field/Name"/>
<xsl:with-param name="search" select="ID"/>
<xsl:with-param name="replace" select="sco-id"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
My problem is that I just get a XML document header and nothing else, but nothing else? I dont think I am far off a working solution and the problems probably lie in the values i am setting for the "match" parameter on the template, and the select parameters within the call-template with-param nodes.
Any help would be appreciated.
Mike
You are trying to match <xsl:template match="/Items">, however your XML contains <Item> elements, not <Items>.
EDIT
Is this what you are trying to do?
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:str="http://exslt.org/strings" extension-element-prefixes="str">
<xsl:import href="http://delicious-library-export.googlecode.com/svn/trunk/str/str.xsl"/>
<xsl:output method="xml" encoding="utf-8" indent="no"/>
<xsl:template match="/Item/Field/Name/text()">
<xsl:call-template name="str:replace">
<xsl:with-param name="string" select="."/>
<xsl:with-param name="search" select="'ID'"/>
<xsl:with-param name="replace" select="'sco-id'"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Result:
<?xml version="1.0" encoding="utf-8"?>
<Item>
<Field>
<Name>sco-id</Name>
<Value>98765</Value>
</Field>
<Field>
<Name>Parentsco-id</Name>
<Value>10002</Value>
</Field>
<Field>
<Name>Type</Name>
<Value>content</Value>
</Field>
<Field>
<Name>Name</Name>
<Value>TestAPI Course</Value>
</Field>
<Field>
<Name>URL</Name>
<Value/>
</Field>
<Field>
<Name>Description</Name>
<Value>a description</Value>
</Field>
<Field>
<Name>DateBegin</Name>
<Value>2012-04-04T00:00:00.000-07:00</Value>
</Field>
<Field>
<Name>DateEnd</Name>
<Value>2012-04-04T00:00:00.000-08:00</Value>
</Field>
<Field>
<Name>DateCreated</Name>
<Value>2012-04-03T00:00:00.000-07:00</Value>
</Field>
<Field>
<Name>DateModified</Name>
<Value>2012-04-04T00:00:00.000-06:00</Value>
</Field>
</Item>
Say I have the following XML:
<root>
<tokens>
<token ID="t1">blah</token>
<token ID="t2">blabla</token>
<token ID="t3">shovel</token>
</tokens>
<relatedStuff>
<group gID="s1">
<references tokID="t1"/>
<references tokID="t2"/>
</group>
<group gID="s2">
<references tokID="t3"/>
</group>
</relatedStuff>
</root>
Now, considering that a for-each loop for every token would be pretty inefficient and a bad idea, how would one go about using template matching, to transform this xml into the following?
<s id="everything_merged">
<tok id="t1" gID="s1" >blah</tok>
<tok id="t2" gID="s1" >blabla</tok>
<tok id="t3" gID="s2" >shovel</tok>
</s>
All I want from <s> is the "gID", the gID corresponding to the token in the <tokens>.
<xsl:for-each select="b:root/a:tokens/a:token">
<!-- and here some template matching -->
<xsl:attribute name="gID">
<xsl:value-of select="--correspondingNode's--#gID"/>
</xsl:attribute>
</xsl:for-each>
I'm pretty fuzzy on this sort of thing, so thank you very much for any help!
The following stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<s id="everything_merged">
<xsl:apply-templates select="/root/tokens/token" />
</s>
</xsl:template>
<xsl:template match="token">
<tok id="{#ID}" gID="{/root/relatedStuff/group[
references[#tokID=current()/#ID]]/#gID}">
<xsl:apply-templates />
</tok>
</xsl:template>
</xsl:stylesheet>
Applied to this input (corrected for well-formedness):
<root>
<tokens>
<token ID="t1">blah</token>
<token ID="t2">blabla</token>
<token ID="t3">shovel</token>
</tokens>
<relatedStuff>
<group gID="s1">
<references tokID="t1" />
<references tokID="t2" />
</group>
<group gID="s2">
<references tokID="t3" />
</group>
</relatedStuff>
</root>
Produces:
<s id="everything_merged">
<tok id="t1" gID="s1">blah</tok>
<tok id="t2" gID="s1">blabla</tok>
<tok id="t3" gID="s2">shovel</tok>
</s>
A solution using keys and pure "push-style:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="kgIDfromTokId" match="#gID"
use="../*/#tokID"/>
<xsl:template match="tokens">
<s id="everything_merged">
<xsl:apply-templates/>
</s>
</xsl:template>
<xsl:template match="token">
<tok id="{#ID}" gID="{key('kgIDfromTokId', #ID)}">
<xsl:apply-templates/>
</tok>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<root>
<tokens>
<token ID="t1">blah</token>
<token ID="t2">blabla</token>
<token ID="t3">shovel</token>
</tokens>
<relatedStuff>
<group gID="s1">
<references tokID="t1" />
<references tokID="t2" />
</group>
<group gID="s2">
<references tokID="t3" />
</group>
</relatedStuff>
</root>
the wanted, correct result is produced:
<s id="everything_merged">
<tok id="t1" gID="s1">blah</tok>
<tok id="t2" gID="s1">blabla</tok>
<tok id="t3" gID="s2">shovel</tok>
</s>