XSLT multiple stylesheets - xslt

I have the following xml
<TopLevel>
<data m="R263">
<s ut="263firstrecord" lt="2013-02-16T09:21:40.393" />
<s ut="263secondrecord" lt="2013-02-16T09:21:40.393" />
</data>
<data m="R262">
<s ut="262firstrecord" lt="2013-02-16T09:21:40.393" />
<s ut="262secondrecord" lt="2013-02-16T09:21:40.393" />
</data>
</TopLevel>
I have some XSLT that does the call template but it's not itterating correctly.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="data">
<xsl:value-of select="#m" />
<xsl:variable name="vYourName" select="#m"/>
<xsl:choose>
<xsl:when test="#m='R262'">
<xsl:call-template name="R262"/>
</xsl:when>
</xsl:choose>
<xsl:choose>
<xsl:when test="#m='R263'">
<xsl:call-template name="R263"/>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="R262">
<xsl:for-each select="/TopLevel/data/s">
Column1=<xsl:value-of select="#ut" />
Column2=<xsl:value-of select="#lt" />
</xsl:for-each>
</xsl:template>
<xsl:template name="R263">
<xsl:for-each select="/TopLevel/data/s">
Column1=<xsl:value-of select="#ut" />
Column2=<xsl:value-of select="#lt" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This gives me 8 records insead of the 4 (<s> level) records. I know it has to do with my iteration ... but I am not sure how to address this.
I am also aware of the apply stylesheets but I couldn't unravel that mystery either ... If someone can help me with XSLT that will only process everything from <TopLevel> to <\TopLevel> checking the value of m at the <data> level and applying the stylesheet at the <s> level for each <s> record I will be greateful beyond belief.

I don't know what output you want to produce, but I suspect you want to replace
<xsl:for-each select="/TopLevel/data/s">
by
<xsl:for-each select="s">
that is, you only want to process the "s" elements within the "data" you are currently processing, rather than selecting all the "s" elements in the whole document.
Why not do this using apply-templates?
<xsl:template match="data">
...
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="s[../#m='R262']">
...
</xsl:template>
<xsl:template match="s[../#m='R263']">
...
</xsl:template>

If you want to use match template and apply-templates you could do the following which gives you also a text output just like your stylesheet does. So this XSLT applied to your original source XML:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="data">
<xsl:value-of select="#m"/>
<xsl:apply-templates select="s"/>
</xsl:template>
<xsl:template match="s">
Column1=<xsl:value-of select="#ut"/>
Column2=<xsl:value-of select="#lt"/>
</xsl:template>
</xsl:stylesheet>
gives you this output:
<?xml version="1.0" encoding="UTF-8"?>
R263
Column1=263firstrecord
Column2=2013-02-16T09:21:40.393
Column1=263secondrecord
Column2=2013-02-16T09:21:40.393
R262
Column1=262firstrecord
Column2=2013-02-16T09:21:40.393
Column1=262secondrecord
Column2=2013-02-16T09:21:40.393
You basically only match on the s and give out the attributes "ut" and "lt". You can also output XML which would look better.
Using this XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<root>
<xsl:apply-templates/>
</root>
</xsl:template>
<xsl:template match="data">
<list>
<xsl:apply-templates select="s"/>
</list>
</xsl:template>
<xsl:template match="s">
<xsl:element name="record">
<xsl:attribute name="m">
<xsl:value-of select="parent::data/#m"/>
</xsl:attribute>
<item>Column1=<xsl:value-of select="#ut"/></item>
<item>Column2=<xsl:value-of select="#lt"/></item>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
will give you this nice XML output:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<list>
<record m="R263">
<item>Column1=263firstrecord</item>
<item>Column2=2013-02-16T09:21:40.393</item>
</record>
<record m="R263">
<item>Column1=263secondrecord</item>
<item>Column2=2013-02-16T09:21:40.393</item>
</record>
</list>
<list>
<record m="R262">
<item>Column1=262firstrecord</item>
<item>Column2=2013-02-16T09:21:40.393</item>
</record>
<record m="R262">
<item>Column1=262secondrecord</item>
<item>Column2=2013-02-16T09:21:40.393</item>
</record>
</list>
You have to adapt the original XSLT a little bit to get a nice XML structure. Also when matching s you "climb" up to element data to get the R-numbers for your attribute values.
The template matching root you need for a proper XML root element. <list> you could also get rid off then you have <record> as child of <root>.

Related

Add mandatory nodes with XSLT

I am facing an xslt/xpath problem and hope someone could help, in a few words here is what I try to achieve.
I have to transform an XML document where some nodes may be missing, these missing nodes are mandatory in the final result. I have the set of mandatory node names available in an xsl:param.
The base document is:
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="TRANSFORM.xslt"?>
<BEGIN>
<CLIENT>
<NUMBER>0021732561</NUMBER>
<NAME1>John</NAME1>
<NAME2>Connor</NAME2>
</CLIENT>
<PRODUCTS>
<PRODUCT_ID>12</PRODUCT_ID>
<DESCRIPTION>blah blah</DESCRIPTION>
</PRODUCTS>
<PRODUCTS>
<PRODUCT_ID>13</PRODUCT_ID>
<DESCRIPTION>description ...</DESCRIPTION>
</PRODUCTS>
<OPTIONS>
<OPTION_ID>1</OPTION_ID>
<DESCRIPTION>blah blah blah ...</DESCRIPTION>
</OPTIONS>
<PROMOTIONS>
<PROMOTION_ID>1</PROMOTION_ID>
<DESCRIPTION>blah blah blah ...</DESCRIPTION>
</PROMOTIONS>
</BEGIN>
Here is the stylesheet so far:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:param name="mandatoryNodes" as="xs:string*" select=" 'PRODUCTS', 'OPTIONS', 'PROMOTIONS' "/>
<xsl:template match="/">
<xsl:apply-templates select="child::node()"/>
</xsl:template>
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="BEGIN">
<xsl:element name="BEGIN">
<xsl:for-each select="$mandatoryNodes">
<!-- If there is no node with this name -->
<xsl:if test="count(*[name() = 'current()']) = 0">
<xsl:element name="{current()}" />
</xsl:if>
</xsl:for-each>
<xsl:apply-templates select="child::node()"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
I tried the transformation in XML Spy, the xsl:iftest failed saying that 'current item is PRODUCTS of type xs:string.
I've tried the same xsl:if outside of a for-each and it seems to work ... what am I missing ?
Inside of <xsl:for-each select="$mandatoryNodes"> the context item is a string but you want to access the primary input document and its nodes so you need to store that document or the template's context node in a variable and use that e.g.
<xsl:template match="BEGIN">
<xsl:variable name="this" select="."/>
<xsl:element name="BEGIN">
<xsl:for-each select="$mandatoryNodes">
<!-- If there is no child node of `BEGIN` with this name -->
<xsl:if test="count($this/*[name() = current()]) = 0">
<xsl:element name="{current()}" />
</xsl:if>
</xsl:for-each>
<xsl:apply-templates select="child::node()"/>
</xsl:element>
</xsl:template>

Filemaker xml output via xslt with column names

I am new to xslt programming and xlm. I have created the code below this works fine, except that instead variable names for each column, it just shows "colno" How do I get the column names into the output?
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fmp="http://www.filemaker.com/fmpxmlresult"
exclude-result-prefixes="fmp"
>
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:variable name="kMetaData" select="fmp:METADATA/fmp:FIELD"/>
<xsl:variable name="colno"
select="count($kMetaData[following-sibling::fmp:FIELD/#NAME]) + 1" />
<xsl:template match="/fmp:FMPXMLRESULT">
<PERSON>
<xsl:apply-templates select="fmp:RESULTSET/fmp:ROW" />
</PERSON>
</xsl:template>
<xsl:template match="fmp:ROW">
<ELEMENTS>
<xsl:apply-templates select="fmp:COL" />
</ELEMENTS>
</xsl:template>
<xsl:template match="fmp:COL">
<xsl:element name="colno">
<xsl:value-of select="fmp:DATA" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
It is hard to make some suggestion without input xml. But at first sight this <xsl:element name="colno"> says "output an element <colno>". I think you should use something like <xsl:element name="{xpath/to/columnName}">
edit:
According to your input xml your template for "COL" element should look like
<xsl:template match="COL">
<xsl:variable name="colPosition" select="position()" />
<!-- Prevent spaces in NAME attribute of FIELD element -->
<xsl:variable name="colName" select="translate($kMetaData[$colPosition]/#NAME, ' ', '_')" />
<xsl:element name="{$colName}">
<xsl:value-of select="DATA"/>
</xsl:element>
</xsl:template>
Then the output looks like
<?xml version="1.0" encoding="utf-8"?>
<PERSON>
<ELEMENTS>
<FIRSTNAME>Richard</FIRSTNAME>
<LASTNAME>Katz</LASTNAME>
<MIDDLENAME>David</MIDDLENAME>
<REQUESTDT>1/1/2001</REQUESTDT>
<salutation>Mr</salutation>
<Bargaining_Unit>CSEA (02,03,04)</Bargaining_Unit>
<Field_134>b</Field_134>
</ELEMENTS>
</PERSON>

How to insert an element into a previously created element in xslt?

I have several records from the DB for a corresponding record in a file.
Example
Record no. XML
<XML_FILE_HEADER file_name="sample.txt" />
<XML_RECORD record_number="1" name="John Doe" Age="21"/>
<XML_RECORD record_number="2" name""Jessica Sanchez" Age="23"/>
<XML_FILE_FOOTER total_records="2"/>
Now for each record I have an xslt template that would create the output file in xml.
For record no 1:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xslt">
<xsl:output method="xml"/>
<xsl:template match="XML_FILE_HEADER">
<xsl:element name="File">
<xsl:attribute name="FileName"><xsl:value-of select="#file_name"/></xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
For records 2 and 3:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xslt">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="XML_RECORD">
<xsl:element name="Record">
<xsl:attribute name="Name"><xsl:value-of select="#name"/></xsl:attribute>
<xsl:element name="Details">
<xsl:attribute name="Age"><xsl:value-of select="#Age"/></xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
For record 4:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xslt">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="XML_FILE_FOOTER">
<xsl:element name="Totals">
<xsl:attribute name="Total Records"><xsl:value-of select="#total_records"/></xsl:attribute>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
The problem with is is I would have an output of this after appending each record using the templates above:
<?xml version="1.0" encoding="UTF-8"?>
<File FileName="sample.txt"></File>
<Record Name="John Doe" Age="21"></Record>
<Record Name="Jessica Sanchez" Age="22"></Record>
<Totals Total Records="2"></Totals>
How would I be able to insert the Record and Totals elements under File? so that it would have an output like this:
<?xml version="1.0" encoding="UTF-8"?>
<File FileName="sample.txt">
<Record Name="John Doe" Age="21"></Record>
<Record Name="Jessica Sanchez" Age="22"></Record>
<Totals Total Records="2"></Totals>
</File>
Any help would be very much appreciated. Thanks.
As short and easy as this:
<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="/*">
<xsl:apply-templates select="XML_FILE_HEADER"/>
</xsl:template>
<xsl:template match="XML_FILE_HEADER">
<File FileName="{#file_name}">
<xsl:apply-templates select="../*[not(self::XML_FILE_HEADER)]"/>
</File>
</xsl:template>
<xsl:template match="XML_RECORD">
<Record name="{#name}" Age="{#Age}"/>
</xsl:template>
<xsl:template match="XML_FILE_FOOTER">
<Totals TotalRecords="{#total_records}"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML (corrected to be well-formed) document:
<t>
<XML_FILE_HEADER file_name="sample.txt" />
<XML_RECORD record_number="1" name="John Doe" Age="21"/>
<XML_RECORD record_number="2" name="Jessica Sanchez" Age="23"/>
<XML_FILE_FOOTER total_records="2"/>
</t>
the wanted, correct result is produced:
<File FileName="sample.txt">
<Record name="John Doe" Age="21"/>
<Record name="Jessica Sanchez" Age="23"/>
<Totals TotalRecords="2"/>
</File>
Explanation:
Proper use of templates.
Proper use of xsl:apply-templates for ordering the results.
Proper use of AVT (Attribute Value Templates).
Avoided the use of xsl:element
No use of xsl:call-template.
Implemented in "push style" almost completely.
What you want is the <xsl:call-template name="templatename" /> element. This allows you to call a template from inside another template.
Something like
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xslt">
    <xsl:output method="xml"/>
<xsl:template match="/XML_FILE/XML_FILE_HEADER">
<xsl:element name="File">
<xsl:attribute name="FileName">
<xsl:value-of select="#file_name"/>
</xsl:attribute>
<xsl:for-each select="/XML_FILE/XML_RECORD">
<xsl:call-template name="RecordTemplate" />
</xsl:for-each>
<xsl:call-template name="TotalTemplate" />
</xsl:element>
</xsl:template>
<xsl:template name="RecordTemplate">
<xsl:element name="Record">
<xsl:attribute name="Name"><xsl:value-of select="#name"/></xsl:attribute>
<xsl:attribute name="Age"><xsl:value-of select="#Age"/></xsl:attribute>
</xsl:element>
</xsl:template>
<xsl:template match="/XML_FILE/XML_FILE_FOOTER" name="TotalTemplate">
<xsl:element name="Totals">
<xsl:attribute name="Total Records"><xsl:value-of select="#total_records"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
of course your input you have to be XML valid (i.e. have a single root node) like so
<XML_FILE>
<XML_FILE_HEADER file_name="sample.txt" />
<XML_RECORD record_number="1" name="John Doe" Age="21"/>
<XML_RECORD record_number="2" name""Jessica Sanchez" Age="23"/>
<XML_FILE_FOOTER total_records="2"/>
</XML_FILE>

How to avoid generation of empty attributes in XSLT?

I have written XSLT file with the following code:
<xsl:attribute name="subCode">
<xsl:value-of select="Field[#key='XY']"/>
</xsl:attribute>
Let's say that my INPUT XML looks like this:
[...]
<Field key="XY"/>
[...]
In this case my XSLT would generate the following output:
<SomeElement subCode="">
[...]
</SomeElement>
My question is: How to ged rid of empty attribute subCode=""?
I know that it is possible by using some conditional instruction like <xsl:if>, however, this seems to be an ugly solution (because I have thousands of similar attributes generated in my XSLT).
It must be done in the same XSLT. I cannot add post-processing in additional XSLT file.
Besides that, the output XML has got its XSD Schema defined. And the schema says that this attribute is optional. Maybe there is some way to apply that XSD schema for the output XML?
Thanks in advance for any help!
Use:
<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="/*">
<someElement>
<xsl:apply-templates mode="attr"/>
</someElement>
</xsl:template>
<xsl:template match="Field[#key='XY' and not(.='')]" mode="attr">
<xsl:attribute name="subCode">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the following XML document:
<t>
<Field key="XY"/>
</t>
the wanted, correct result (no attribute at all is generated) is produced:
when the source XML document is this:
<t>
<Field key="XY">1</Field>
</t>
the same transformation again produces the correct, wanted result;
<someElement subCode="1"/>
You can define a template to match the attribute which will give you the correct output for a missing #key but if you also add the predicate [.!=''] it should then also ignore the attributes with no value.
<xsl:template match="#key[.!='']">
<xsl:attribute name="subCode"><xsl:value-of select="."/></xsl:attribute>
</xsl:template>
Or in your example if you only want the #key='XY' matches use:
<xsl:template match="#key[.='XY']">
<xsl:attribute name="subCode"><xsl:value-of select="."/></xsl:attribute>
</xsl:template>
Edit: Here is a more complete example that I used to test this.
Source XML
<Fields>
<Field key="XY">A</Field>
<Field key="XY">B</Field>
<Field key="">C</Field>
<Field>D</Field>
</Fields>
Accompanying XSL
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="xsl">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<output>
<xsl:apply-templates />
</output>
</xsl:template>
<xsl:template match="Field">
<xsl:element name="NewField">
<xsl:apply-templates select="#*"/>
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<xsl:template match="#key[.='XY']">
<xsl:attribute name="subCode"><xsl:value-of select="."/></xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Result
<output>
<NewField subCode="XY">A</NewField>
<NewField subCode="XY">B</NewField>
<NewField>C</NewField>
<NewField>D</NewField>
</output>
You should apply the following templates to your attributes:
This template matches all key the attributes who's value is 'XY':
<xsl:template match="#key['XY']">
<xsl:attribute name="subCode">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
This template matches all the key attributes with a (text)value longer than 0:
<xsl:template match="#key[string-length(.)]">
<xsl:attribute name="other">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
And finally this template matches all the attributes (*) that are empty:
<xsl:template match="#*[not(string-length(.))]" />
So if you have an input like
<?xml version="1.0"?>
<Root>
<Field key='v'>One</Field>
<Field key='XY'>Two</Field>
<Field key='a'>Three</Field>
<Field key='v'>Four</Field>
<Field key='XY'>Five</Field>
<Field>Six</Field>
<Field>Seven</Field>
<Field key='b'>Eight</Field>
<Field key=''>Nine</Field>
<Field></Field>
<Field key='x'>Eleven</Field>
<Field key=''>Twelve</Field>
<Field lat='x'>Thirteen</Field>
</Root>
You can generate an output:
<?xml version='1.0' ?>
<Root>
<SomeField other="v">One</SomeField>
<SomeField subCode="XY">Two</SomeField>
<SomeField other="a">Three</SomeField>
<SomeField other="v">Four</SomeField>
<SomeField subCode="XY">Five</SomeField>
<SomeField>Six</SomeField>
<SomeField>Seven</SomeField>
<SomeField other="b">Eight</SomeField>
<SomeField>Nine</SomeField>
<SomeField/>
<SomeField other="x">Eleven</SomeField>
<SomeField>Twelve</SomeField>
<SomeField>xThirteen</SomeField>
</Root>
using the following xslt:
<?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:strip-space elements="*"/>
<xsl:template match="/">
<Root>
<xsl:apply-templates/>
</Root>
</xsl:template>
<xsl:template match="Field">
<xsl:element name="SomeField">
<xsl:apply-templates select="#*"/>
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#key['XY']">
<xsl:attribute name="subCode">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="#key[string-length(.)]">
<xsl:attribute name="other">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="#key[not(string-length(.))]" />
</xsl:stylesheet>

How do I combine or merge grouped nodes?

Using the 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="2.0">
<xsl:output method="xml"/>
<xsl:template match="/">
<records>
<record>
<!-- Group record by bigID, for further processing -->
<xsl:for-each-group select="records/record" group-by="bigID">
<xsl:sort select="bigID"/>
<xsl:for-each select="current-group()">
<!-- Create new combined record -->
<bigID>
<!-- <xsl:value-of select="."/> -->
<xsl:for-each select=".">
<xsl:value-of select="bigID"/>
</xsl:for-each>
</bigID>
<text>
<xsl:value-of select="text"/>
</text>
</xsl:for-each>
</xsl:for-each-group>
</record>
</records>
</xsl:template>
</xsl:stylesheet>
I'm trying to change:
<?xml version="1.0" encoding="UTF-8"?>
<records>
<record>
<bigID>123</bigID>
<text>Contains text for 123</text>
<bigID>456</bigID>
<text>Some 456 text</text>
<bigID>123</bigID>
<text>More 123 text</text>
<bigID>123</bigID>
<text>Yet more 123 text</text>
</record>
</records>
into:
<?xml version="1.0" encoding="UTF-8"?>
<records>
<record>
<bigID>123
<text>Contains text for 123</text>
<text>More 123 text</text>
<text>Yet more 123 text</text>
</bigID>
<bigID>456
<text>Some 456 text</text>
</bigID>
</record>
</records>
Right now, I'm just listing the grouped <bigID>s, individually. I'm missing the step after grouping, where I combine the grouped <bigID> nodes. My suspicion is that I need to use the "key" function somehow, but I'm not sure.
Thanks for any help.
Here is the wanted XSLT 2.0 transformation:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kTextforId" match="text"
use="preceding-sibling::bigID[1]"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="record">
<record>
<xsl:for-each-group select="bigID" group-by=".">
<bigID>
<xsl:sequence select="current-grouping-key()"/>
<xsl:copy-of select=
"key('kTextforId', current-grouping-key())"/>
</bigID>
</xsl:for-each-group>
</record>
</xsl:template>
</xsl:stylesheet>
When performed on the provided XML document, the wanted result is produced.
In XSLT 2.0 you don't need keys for grouping.
Since you are just copying the text elements in the group, the inner for-each can be removed.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<records>
<record>
<xsl:for-each-group select="records/record/bigID" group-by=".">
<xsl:sort select="." data-type="number" />
<bigID>
<xsl:value-of select="." />
<xsl:copy-of select="current-group()/following-sibling::text[1]" />
</bigID>
</xsl:for-each-group>
</record>
</records>
</xsl:template>
</xsl:stylesheet>
If you instead wanted to output the bigID elements followed by their text elements, then my loop would be replaced by the following.
<xsl:for-each-group select="records/record/bigID" group-by=".">
<xsl:sort select="." data-type="number" />
<xsl:copy-of select="." />
<xsl:copy-of select="current-group()/following-sibling::text[1]" />
</xsl:for-each-group>