for each in for each logic - xslt

I am trying to do following in XSLT 1.0:
Copy all the Fram element as is into output, where the condition is any of Fram's attributes (#name AND #type AND #ref) value matches with any XFram element then it should update the Fram/text() with XFram/text(). After that copy rest of XFram element (all those dint match the above criteria) and convert them into Fram tag.
Note: Third Fram element's all attribute matches with third Xfram element, hence text appended in Third Fram element. Rest of XFarm element got transform into Fram element and added after last Fram element. Also you will notice that Fram's order is not changed.
Input.XML
<Doc>AL
<Frams>
<Fram type="x" name="Fram1" ref="ref1">This is Fram One</Fram>
<Fram type="y" name="Fram2" ref="ref2">This is Fram Two</Fram>
<Fram type="z" name="Fram3" ref="ref3">This is Fram Three</Fram>
<Fram type="a" name="Fram4" ref="ref3">This is Fram Four</Fram>
<Fram type="b" name="Fram5" ref="ref3">This is Fram Five</Fram>
</Frams>
<XFram>
<XFram type="e" name="XFram1" ref="Xref1">This is XFram One</Fram>
<XFram type="f" name="XFram2" ref="Xref2">This is XFram Two</Fram>
<XFram type="z" name="XFram3" ref="Xref3">This is XFram Three</Fram>
<XFram type="e" name="XFram1" ref="Xref1">This is XFram Four</Fram>
<XFram>
<Doc>
Output should be:
<Doc>
<Frams>
<Fram type="x" name="Fram1" ref="ref1">This is Fram One</Fram>
<Fram type="y" name="Fram2" ref="ref2">This is Fram Two</Fram>
<Fram type="z" name="Fram3" ref="ref3">This is XFram Three</Fram>
<Fram type="a" name="Fram4" ref="ref3">This is Fram Four</Fram>
<Fram type="b" name="Fram5" ref="ref3">This is Fram Five</Fram>
<XFram type="e" name="XFram1" ref="Xref1">This is XFram One</Fram>
<XFram type="f" name="XFram2" ref="Xref2">This is XFram Two</Fram>
<XFram type="e" name="XFram1" ref="Xref1">This is XFram Four</Fram>
</Frams>
<Doc>
I am doing something like this but not able to think of the logic:
<xsl:template match="/">
<xsl:for-each select="XFram">
<xsl:variable name="type">
<xsl:value-of select="type"/>
</xsl:variable>
<xsl:variable name="name">
<xsl:value-of select="name"/>
</xsl:variable>
<xsl:variable name="ref">
<xsl:value-of select="ref"/>
</xsl:variable>
<xsl:for-each select="//Fram">
<xsl:choose>
<xsl:when test="(type = $type) and (name = $name) and (ref = $ref)"> </xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:for-each>
</xsl:template>

This transformation:
<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:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Fram[last()]">
<xsl:call-template name="identity"/>
<xsl:apply-templates
select="../../XFram/*"/>
</xsl:template>
<xsl:template match="XFram/XFram">
<xsl:if test=
"not(/*/Frams/*
[#type = current()/#type
and
concat('X',#name) = current()/#name
and
concat('X',#ref) = current()/#ref
]
)">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:template>
<xsl:template match="XFram"/>
</xsl:stylesheet>
when applied on the provided XML document (with corrected severe malformedness!!!):
<Doc>
<Frams>
<Fram type="x" name="Fram1" ref="ref1">This is Fram One</Fram>
<Fram type="y" name="Fram2" ref="ref2">This is Fram Two</Fram>
<Fram type="z" name="Fram3" ref="ref3">This is Fram Three</Fram>
<Fram type="a" name="Fram4" ref="ref3">This is Fram Four</Fram>
<Fram type="b" name="Fram5" ref="ref3">This is Fram Five</Fram>
</Frams>
<XFram>
<XFram type="e" name="XFram1" ref="Xref1">This is XFram One</XFram>
<XFram type="f" name="XFram2" ref="Xref2">This is XFram Two</XFram>
<XFram type="z" name="XFram3" ref="Xref3">This is XFram Three</XFram>
<XFram type="e" name="XFram1" ref="Xref1">This is XFram Four</XFram></XFram>
</Doc>
produces the wanted, correct result:
<Doc>
<Frams>
<Fram type="x" name="Fram1" ref="ref1">This is Fram One</Fram>
<Fram type="y" name="Fram2" ref="ref2">This is Fram Two</Fram>
<Fram type="z" name="Fram3" ref="ref3">This is Fram Three</Fram>
<Fram type="a" name="Fram4" ref="ref3">This is Fram Four</Fram>
<Fram type="b" name="Fram5" ref="ref3">This is Fram Five</Fram>
<XFram type="e" name="XFram1" ref="Xref1">This is XFram One</XFram>
<XFram type="f" name="XFram2" ref="Xref2">This is XFram Two</XFram>
<XFram type="e" name="XFram1" ref="Xref1">This is XFram Four</XFram>
</Frams>
</Doc>

Related

Uncaught TypeError: Cannot read properties of null (reading 'hashCode') [SaxonJS]

I'm getting this error in Chrome console:
Seems to be a problem with XSLT or SEF as other SEF works.
I complie SEF with the xslt3 tool on Node.js version v14.17.3.
I can run the XSLT with Saxon 9.8.12-EE in oXygen with no problem.
Maybe the problem with maps, xsl:include or xsl:import?
Here is the XSLT code:
<stylesheet exclude-result-prefixes="xs xd dme functx dita mei map array" extension-element-prefixes="ixsl" version="3.0" xmlns="http://www.w3.org/1999/XSL/Transform" xmlns:array="http://www.w3.org/2005/xpath-functions/array" xmlns:dita="http://dita-ot.sourceforge.net" xmlns:dme="http://www.mozarteum.at/ns/dme" xmlns:functx="http://www.functx.com" xmlns:ixsl="http://saxonica.com/ns/interactiveXSLT" xmlns:map="http://www.w3.org/2005/xpath-functions/map" xmlns:mei="http://www.music-encoding.org/ns/mei" xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl" xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xpath-default-namespace="http://www.music-encoding.org/ns/mei">
<xi:include href="docs.xsl" xpointer="element(/1/1)"/>
<import href="changeLog.xsl"/>
<!--This parameter is a needed for XSpec. Cf. https://github.com/xspec/xspec/wiki/Global-Context-Item-->
<param name="global-context-item" select="."/>
<param name="source"/>
<include href="../modules/identity-transform.xsl"/>
<xd:doc>
<xd:desc>
<xd:p>Replace current scoreDef with the alternative. Note that this should be defined in the following-sibling <choice>.</xd:p>
</xd:desc>
</xd:doc>
<template match="scoreDef[not(#corresp = concat('#', $source))]">
<copy-of select="following-sibling::choice[#type = 'scoring']/*[#corresp = concat('#', $source)]/scoreDef"/>
</template>
<xd:doc>
<xd:desc/>
</xd:doc>
<template match="choice[#type = 'scoring']"/>
<xd:doc>
<xd:desc>
<xd:p>Map staffDef#n of the new scoreDef and #dme.parts.</xd:p>
</xd:desc>
</xd:doc>
<variable as="map(xs:string, item())*" name="map_staves_order">
<for-each select="$global-context-item//choice[#type = 'scoring']/*[#corresp = concat('#', $source)]/scoreDef">
<map>
<for-each select="descendant::staffDef">
<map-entry key="string(#n)" select="#dme.parts"/>
</for-each>
</map>
</for-each>
</variable>
<xd:doc>
<xd:desc>
<xd:p>Get the maximum number of staves.</xd:p>
</xd:desc>
</xd:doc>
<variable as="xs:integer" name="count_staves" select="
max(for $a in map:keys($map_staves_order)
return
if (matches($a, '\d')) then
xs:integer($a)
else
())"/>
<xd:doc>
<xd:desc>
<xd:p>Key: old #n.</xd:p>
<xd:p>Value: new #n</xd:p>
</xd:desc>
</xd:doc>
<variable as="map(xs:string, xs:string)*" name="map_new_old_staves">
<map>
<for-each select="map:keys($map_staves_order)">
<variable name="current_key" select="."/>
<map-entry key="$global-context-item//measure[#n = 1]/staff[#dme.parts = map:get($map_staves_order, $current_key)]/#n/string()" select="."/>
</for-each>
</map>
</variable>
<xd:doc>
<xd:desc>
<xd:p>Copies elements before first staff without changes, e.g. <tempo></xd:p>
<xd:p>Copies staves accordingly to the new score order. Changes the #n-attribute accordingly</xd:p>
<xd:p>Copies ControlEvents and updates the #staff attribute if neccessary.</xd:p>
</xd:desc>
</xd:doc>
<template match="measure">
<copy>
<apply-templates select="#*"/>
<apply-templates select="child::staff[1]/preceding-sibling::*"/>
<variable as="element()" name="currentMeasure" select="."/>
<for-each select="1 to $count_staves">
<variable as="xs:string" name="currentItem" select="string()"/>
<for-each select="$currentMeasure/child::staff[map:get($map_staves_order, $currentItem) = #dme.parts]">
<copy>
<attribute name="n" select="$currentItem"/>
<apply-templates select="(#* except #n) | node()"/>
</copy>
</for-each>
</for-each>
<call-template name="update_staff_controlEvents">
<with-param name="elements" select="child::staff[last()]/following-sibling::*"/>
</call-template>
</copy>
</template>
<xd:doc>
<xd:desc>
<xd:p>Receives one or more elements (controlEvents) and updates their #staff according to the $map_new_old_staves variable.</xd:p>
<xd:p>Recursive template</xd:p>
</xd:desc>
<xd:param name="elements"/>
</xd:doc>
<template name="update_staff_controlEvents">
<param name="elements"/>
<for-each select="$elements">
<choose>
<when test="#staff">
<copy>
<attribute name="staff" select="map:get($map_new_old_staves, #staff)"/>
<apply-templates select="#* except #staff"/>
<if test="node()">
<call-template name="update_staff_controlEvents">
<with-param name="elements" select="node()"/>
</call-template>
</if>
</copy>
</when>
<otherwise>
<copy>
<apply-templates select="#*"/>
<if test="node()">
<call-template name="update_staff_controlEvents">
<with-param name="elements" select="node()"/>
</call-template>
</if>
</copy>
</otherwise>
</choose>
</for-each>
</template>
</stylesheet>
JS which I run on Console:
var options = {
stylesheetLocation: "./assets/xsl/origScoring.sef.json",
sourceText: xmlString,
stylesheetParams: {"source": "source_A"},
destination: "document"
}
var result = SaxonJS.transform(options);
result.principalResult
Owing to the Saxon developers I could easily solve my issue by adding namespace prefixes in the XPath expressions when creating maps. Originally, I used xpath-default-namespace. Fo instance, here on the elements measure and staff should be:
<map-entry key="$global-context-item//mei:measure[#n = 1]/mei:staff[#dme.parts = map:get($map_staves_order, $current_key)]/#n/string()" select="."/>
instead of
<map-entry key="$global-context-item//measure[#n = 1]/staff[#dme.parts = map:get($map_staves_order, $current_key)]/#n/string()" select="."/>

XSLT3 Streaming for appending integer position of node

I have a large XML file to transform using XSLT to append the integer position of sibling node . I’m using XSLT3 streaming and accumulators. I did get desired output. However, my code looks so lengthy that I’m unable to simplify my code. I also need to group same sibling nodes as sibling nodes in the source xml is not grouped always. Could someone help me here please?
Requirement: Sibling nodes such as Positions, Payments etc.. need to be appended with their corresponding integer position such as <Locations1>, <Locations2>etc.<Payments1>,< Payments2> etc..
Now that I have declared two accumulators, each for each sibling nodes. However, my source XML has many sibling nodes.. I’m not sure if I need to use as many accumulators and template match as my sibling nodes.
Input XML
``
<?xml version="1.0" encoding="UTF-8"?>
<Members>
<Member>
<Name>
<fname>Fred</fname>
<id>1234</id>
</Name>
<Locations>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations>
<Locations>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations>
<Payments>
<amount>1000</amount>
<currency>USD</currency>
</Payments>
<Payments>
<amount>1000</amount>
<currency>USD</currency>
</Payments>
<Locations>
<name>New York</name>
<days>5</days>
<hours>40</hours>
</Locations>
<Locations>
<name>Boston</name>
<days>4</days>
<hours>32</hours>
</Locations>
</Member>
<Member>
<Name>
<fname>Jack</fname>
<id>4567</id>
</Name>
<Locations>
<name>New York</name>
<days>5</days>
<hours>30</hours>
</Locations>
<Locations>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations>
<Payments>
<amount>1500</amount>
<currency>USD</currency>
</Payments>
<Payments>
<amount>1800</amount>
<currency>USD</currency>
</Payments>
</Member>
</Members>
``
Expected Output
``
<?xml version="1.0" encoding="UTF-8"?>
<Members>
<Member>
<Name>
<fname>Fred</fname>
<id>1234</id>
</Name>
<Locations_1>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_1>
<Locations_2>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_2>
<Locations_3>
<name>New York</name>
<days>5</days>
<hours>40</hours>
</Locations_3>
<Locations_4>
<name>Boston</name>
<days>4</days>
<hours>32</hours>
</Locations_4>
<Payments_1>
<amount>1000</amount>
<currency>USD</currency>
</Payments_1>
<Payments_2>
<amount>1000</amount>
<currency>USD</currency>
</Payments_2>
</Member>
<Member>
<Name>
<fname>Jack</fname>
<id>4567</id>
</Name>
<Locations_1>
<name>New York</name>
<days>5</days>
<hours>30</hours>
</Locations_1>
<Locations_2>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_2>
<Payments_1>
<amount>1500</amount>
<currency>USD</currency>
</Payments_1>
<Payments_2>
<amount>1800</amount>
<currency>USD</currency>
</Payments_2>
</Member>
</Members>
``
Current code
``
<?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="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:mode streamable="yes" on-no-match="shallow-copy" use-accumulators="#all"/>
<xsl:accumulator name="loc-count" as="xs:integer" initial-value="0" streamable="yes">
<xsl:accumulator-rule match="Member" select="0"/>
<xsl:accumulator-rule match="Member/Locations" select="$value + 1"/>
</xsl:accumulator>
<xsl:accumulator name="pay-count" as="xs:integer" initial-value="0" streamable="yes">
<xsl:accumulator-rule match="Member" select="0"/>
<xsl:accumulator-rule match="Member/Payments" select="$value + 1"/>
</xsl:accumulator>
<xsl:template match="Locations">
<xsl:element name="Locations_{accumulator-before('loc-count')}">
<xsl:copy-of select="#* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="Payments">
<xsl:element name="Payments_{accumulator-before('pay-count')}">
<xsl:copy-of select="#* | node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
``
Current Output
<?xml version="1.0" encoding="UTF-8"?>
<Members>
<Member>
<Name>
<fname>Fred</fname>
<id>1234</id>
</Name>
<Locations_1>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_1>
<Locations_2>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_2>
<Payments_1>
<amount>1000</amount>
<currency>USD</currency>
</Payments_1>
<Payments_2>
<amount>1000</amount>
<currency>USD</currency>
</Payments_2>
<Locations_3>
<name>New York</name>
<days>5</days>
<hours>40</hours>
</Locations_3>
<Locations_4>
<name>Boston</name>
<days>4</days>
<hours>32</hours>
</Locations_4>
</Member>
<Member>
<Name>
<fname>Jack</fname>
<id>4567</id>
</Name>
<Locations_1>
<name>New York</name>
<days>5</days>
<hours>30</hours>
</Locations_1>
<Locations_2>
<name>Chicago</name>
<days>3</days>
<hours>24</hours>
</Locations_2>
<Payments_1>
<amount>1500</amount>
<currency>USD</currency>
</Payments_1>
<Payments_2>
<amount>1800</amount>
<currency>USD</currency>
</Payments_2>
</Member>
</Members>
If you want to group the Member child elements by node-name() then I think you need to wrap the xsl:for-each-group into xsl:fork:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="#all" version="3.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:mode on-no-match="shallow-copy" streamable="yes" use-accumulators="counters"/>
<xsl:accumulator name="counters" as="map(xs:QName, xs:integer)" initial-value="map{}" streamable="yes">
<xsl:accumulator-rule match="Member" select="map{}"/>
<xsl:accumulator-rule match="Member/*"
select="map:put($value, node-name(), if (map:contains($value, node-name())) then map:get($value, node-name()) + 1 else 1)"/>
</xsl:accumulator>
<xsl:template match="Member">
<xsl:copy>
<xsl:fork>
<xsl:for-each-group select="*" group-by="node-name()">
<xsl:apply-templates select="current-group()"/>
</xsl:for-each-group>
</xsl:fork>
</xsl:copy>
</xsl:template>
<xsl:template match="Member/*">
<xsl:element name="{node-name()}_{accumulator-before('counters')(node-name())}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
This approach only shows the grouping, it doesn't try to special case Name elements or some other way to not output an index if there is only one such element.
Firstly, my sympathy. XML that uses names like Payments_1 and Payments_2 is really bad news, someone is going to hate you for generating it like this. But if that's the kind of XML you've been told to produce, I guess it's not your job to question it.
As far as the requirements are concerned, you haven't made it clear whether the various kinds of sibling nodes are always grouped as in your example (all Locations, then all Payments, etc), or whether they can be interleaved.
One way you might be able to reduce the volume of code is by having a single accumulator holding a map. The map would use element names as the key and the current sibling count for that element as the value.
<accumulator name="counters" as="map(xs:QName, xs:integer)" initial-value="map{}">
<xsl:accumulator-rule match="Member" select="map{}"/>
<xsl:accumulator-rule match="Member/*" select="map:put($value, node-name(.), if (map:contains($value, node-name(.)) then map:get($value, node-name(.))+1 else 1"/>
</accumulator>
<xsl:template match="Members/*">
<xsl:element name="{name()}_{accumulator-before('counters')(node-name(.))}">
....
Another way to do the conditional map:put is
map:put($value, node-name(.), ($value(node-name(.)), 0)[1] + 1)

I need to merge corresponding language pairs from two separate TMX files, can anyone suggest the most straighforward approach?

Sample file 1:
<root>
<tu creationdate="20130704T142811Z" changedate="20130704T142811Z" lastusagedate="20130704T142811Z">
<prop type="x-Context">0, 0</prop>
<tuv xml:lang="es-ES">
<seg>Kit de aislamiento del ARN de tejido fijado en formol e incluido en parafina (FFIP)</seg>
</tuv>
</tu>
<tu creationdate="20130704T142811Z" changedate="20130704T142811Z" lastusagedate="20130704T142834Z" usagecount="1">
<prop type="x-Context">-2654973059922618556, -1379942944751394277</prop>
<tuv xml:lang="es-ES">
<seg>Para diagnóstico <ph x="1" type="13" />in vitro<ph x="2" type="14" />.</seg>
</tuv>
<!--(Corresponding <tuv xml:lang="de-DE"> node to be inserted here, matched by attribute value of element "prop", attribue "type")-->
</tu>
</root>
Sample file 2:
<body>
<tu creationdate="20130704T142816Z" changedate="20130704T142816Z" lastusagedate="20130704T142837Z" usagecount="1">
<prop type="x-Context">0, 0</prop>
<prop type="x-Context">106215398363146103, 106215398363146103</prop>
<prop type="x-Origin">TM</prop>
<prop type="x-ConfirmationLevel">Translated</prop>
<tuv xml:lang="de-DE">
<seg>FFPET RNA Isolation Kit2</seg>
</tuv>
</tu>
<tu creationdate="20130704T142816Z" changedate="20130704T142816Z" lastusagedate="20130704T142837Z" usagecount="1">
<prop type="x-Context">-2654973059922618556, -2654973059922618556</prop>
<prop type="x-Origin">TM</prop>
<prop type="x-ConfirmationLevel">Translated</prop>
<tuv xml:lang="de-DE">
<seg><ph x="1" type="13" />In-vitro-<ph x="2" type="14" />Diagnostikum.</seg>
</tuv>
</tu>
Please try the XSLT below:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:variable name="lookup" select="document('test.xml')"/>
<xsl:template match="tu">
<!-- store the left value, before comma and space -->
<xsl:variable name="propID">
<xsl:value-of select="tokenize(./prop[#type='x-Context'], ', ')[1]"/>
</xsl:variable>
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
<!-- loop through the lookup XML -->
<xsl:for-each select="$lookup/body/tu/prop[#type='x-Context']">
<!-- store the left value, before comma and space from the lookup -->
<xsl:variable name="lookup_propID" select="tokenize(., ', ')[1]"/>
<!-- compare the two variables,
is it possible that the value can be '0'?
If it is, replace $propID[.!='0'] to $propID-->
<xsl:choose>
<xsl:when test="$propID[.!='0']=$lookup_propID">
<!-- apply the target matched node -->
<xsl:apply-templates select="following-sibling::tuv[#xml:lang='de-DE']"/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Using XSLT 1.0 apply-templates only on certain attribute values

I'm trying to stay away from a procedural approach with this solution and I'm not sure that's possible.
Here's my XML:
<countData>
<count countId="37" name="Data Response 1">
<year yearId="2013">
<month monthId="5">
<day dayId="23" countVal="6092"/>
<day dayId="24" countVal="6238"/>
<day dayId="27" countVal="6324"/>
<day dayId="28" countVal="6328"/>
<day dayId="29" countVal="3164"/>
</month>
<day dayId="23" countVal="7000"/>
<day dayId="24" countVal="7000"/>
<day dayId="27" countVal="7000"/>
<day dayId="28" countVal="7000"/>
<day dayId="29" countVal="7000"/>
</month>
</year>
</count>
<count countId="39" name="Data Response 2">
<year yearId="2013">
<month monthId="5">
<day dayId="23" countVal="675"/>
<day dayId="24" countVal="709"/>
<day dayId="27" countVal="754"/>
<day dayId="28" countVal="731"/>
<day dayId="29" countVal="377"/>
</month>
</year>
</count>
I want to apply-templates (in this example) for all count/#countIds that are 37 or 39. Here's where I am:
<xsl:template match="/">
<xsl:apply-templates mode="TimeFrame"/>
</xsl:template>
<xsl:template match="*" mode="TimeFrame">
<xsl:if test="count[#countId=37] or count[#countId=39]">
<magic>Only hitting this once for countId 37</magic>
</xsl:if>
</xsl:template>
I'm going to have quite a few of these templates with "modes" since I'm processing the same response a multitude of different ways.
Not sure how I'm missing the "range" match and only getting 1.
I'm sure it has to do with my "procedural mind". :)
Any help on this would be great!
Thanks,
Your main template, <xsl:template match="/">, runs only once - namely for the <countData> element.
Which means that you either forgot to recurse:
<xsl:template match="*" mode="TimeFrame">
<xsl:if test="count[#countId=37] or count[#countId=39]">
<magic>Only hitting this once for countId 37</magic>
</xsl:if>
<xsl:apply-templates mode="TimeFrame"/> <!-- ! -->
</xsl:template>
...or you failed to set the right context for the the main template:
<xsl:template match="/countData"><!-- ! -->
<xsl:apply-templates mode="TimeFrame"/>
</xsl:template>
<!-- or, alternatively -->
<xsl:template match="/">
<xsl:apply-templates select="countData/*" mode="TimeFrame"/>
</xsl:template>

XSLT - A generic rule in apply template to include multiple nodes

I am working over sorting the Grand Parent based on a field in Father. The source file looks like
<Root>
<AllData>
<Data_not_to_be_sorted>
<Additional_data1>
<Some_test_data1/>
<Some_test_data2/>
</Additional_data1>
</Data_not_to_be_sorted>
<RealData>
<Some_data1/>
<Some_data2/>
<GrandFather>
<Data_required_as_it_is></Data_required_as_it_is>
<Father>
<Value>4</Value>
<Name>name in 4</Name>
</Father>
</GrandFather>
<GrandFather>
<Data_required_as_it_is></Data_required_as_it_is>
<Father>
<Value>3</Value>
<Name>name in 3</Name>
</Father>
</GrandFather>
</RealData>
<RealData>
<Some_data1/>
<Some_data2/>
<GrandFather>
<Data_required_as_it_is></Data_required_as_it_is>
<Father>
<Value>2</Value>
<Name>name in 2</Name>
</Father>
</GrandFather>
<GrandFather>
<Data_required_as_it_is></Data_required_as_it_is>
<Father>
<Value>1</Value>
<Name>name in 1</Name>
</Father>
</GrandFather>
</RealData>
</AllData>
</Root>
The XSLT code, I am using is as below:
<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:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="RealData">
<xsl:copy>
<xsl:apply-templates select="Data_required_as_it_is"></xsl:apply-templates>
<xsl:apply-templates select="GrandFather">
<xsl:sort select="Father/Value" data-type="number"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The code is working well...My query is - Can I write a generic line for
Data_required_as_it_is. Imagine a situation where I have 20 different xml tags for "Data_required_as_it_is" so either I need write all of them manually or just write them in a generic way...
The output of the code is as below:
<Root>
<AllData>
<Data_not_to_be_sorted>
<Additional_data1>
<Some_test_data1/>
<Some_test_data2/>
</Additional_data1>
</Data_not_to_be_sorted>
<RealData>
<Some_data1/>
<Some_data2/>
<GrandFather>
<Data_required_as_it_is></Data_required_as_it_is>
<Father>
<Value>3</Value>
<Name>name in 3</Name>
</Father>
</GrandFather>
<GrandFather>
<Data_required_as_it_is></Data_required_as_it_is>
<Father>
<Value>4</Value>
<Name>name in 4</Name>
</Father>
</GrandFather>
</RealData>
<RealData>
<Some_data1/>
<Some_data2/>
<GrandFather>
<Data_required_as_it_is></Data_required_as_it_is>
<Father>
<Value>1</Value>
<Name>name in 1</Name>
</Father>
</GrandFather>
<GrandFather>
<Data_required_as_it_is></Data_required_as_it_is>
<Father>
<Value>2</Value>
<Name>name in 2</Name>
</Father>
</GrandFather>
</RealData>
</AllData>
</Root>
<xsl:template match="RealData">
<xsl:copy>
<xsl:apply-templates select="Data_required_as_it_is"></xsl:apply-templates>
<xsl:apply-templates select="GrandFather">
<xsl:sort select="Father/Value" data-type="number"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
One could use a more generic code like this:
<xsl:template match="RealData">
<xsl:copy>
<xsl:apply-templates>
<xsl:sort select="self::GrandFather/Father/Value" data-type="number"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
Note: This will sort any any non-GrandFather node before any GrandFather node and will do the specified sorting of GrandFather elements by their Father/Value elements.
In case it is desired that the non-GrandFather elements remain interspersed within the GrandFather elements, this can also be accomplished -- ask another question if interested.
If you want to match anything that isn't a GrandFather element at this point, you should be able to do something like this:
<xsl:apply-templates select="*[not(self::GrandFather)]"></xsl:apply-templates>
Note that if you had elements after any existing GrandFather elements, they would be moved to before the sorted GrandFather elements in the output in this case though.