Gather filename information into XML node - xslt

I have several XML files, as follows:
file : 1.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<config>
<info>
<info1>val1</info1>
<info2>val2</info2>
</info>
<info>
<info1>val3</info1>
<info2>val4</info2>
</info>
</config>
file : 2.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<config>
<info>
<info1>val5</info1>
<info2>val6</info2>
</info>
<info>
<info1>val7</info1>
<info2>val8</info2>
</info>
</config>
file: 3.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<config>
<info>
<info1>val9</info1>
<info2>val10</info2>
</info>
<info>
<info1>val11</info1>
<info2>val12</info2>
</info>
</config>
using XSLT2.0 (saxon), I would like to merge them and also add to each node:
<info3>XXX</info3>
and also
<file>filename.xml</file>
filename.xml was the file from which the info has been copied.
The output should look like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<config>
<info>
<info1>val1</info1>
<info2>val2</info2>
<info3>XXX</info3>
<file>1.xml</file>
</info>
<info>
<info1>val3</info1>
<info2>val4</info2>
<info3>XXX</info3>
<file>1.xml</file>
</info>
<info>
<info1>val5</info1>
<info2>val6</info2>
<info3>XXX</info3>
<file>2.xml</file>
</info>
<info>
<info1>val7</info1>
<info2>val8</info2>
<info3>XXX</info3>
<file>2.xml</file>
</info>
<info>
<info1>val9</info1>
<info2>val10</info2>
<info3>XXX</info3>
<file>3.xml</file>
</info>
<info>
<info1>val11</info1>
<info2>val12</info2>
<info3>XXX</info3>
<file>3.xml</file>
</info>
</config>
So far I have been able to merge the file by creating an XML file that lists the file I want to merge (merge.xml):
<mergeData newRoot="config">
<filelist>
<fileItem>1.xml</fileItem>
<fileItem>2.xml</fileItem>
<fileItem>3.xml</fileItem>
</filelist>
</mergeData>
using the following XSL (merge.xsl):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0" exclude-result-prefixes="#all">
<xsl:param name="new">
<info>XXX</info>
</xsl:param>
<xsl:template match="/">
<xsl:element name="{mergeData/#newRoot}">
<xsl:apply-templates select="mergeData/fileList/fileItem"/>
</xsl:element>
</xsl:template>
<xsl:template match="fileItem">
<xsl:apply-templates select="document(translate(., '\', '/'))/config/*"/>
</xsl:template>
<xsl:template match="config/*">
<xsl:copy>
<xsl:copy-of select="node()"/>
<xsl:copy-of select="$new"/>
</xsl:copy>
<file><xsl:value-of select="tokenize(document-uri(.), '/')[last()]"/></file>
</xsl:template>
How should I modify the XSL to get the filename into each info at the same time.

Really the only thing you should have to do is move file inside of the xsl:copy.
Example (with a couple of other minor mods):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0" exclude-result-prefixes="#all">
<xsl:output indent="yes"/>
<xsl:param name="new">
<info3>XXX</info3>
</xsl:param>
<xsl:template match="/">
<xsl:element name="{mergeData/#newRoot}">
<xsl:apply-templates select="mergeData/filelist/fileItem"/>
</xsl:element>
</xsl:template>
<xsl:template match="fileItem">
<xsl:apply-templates select="document(translate(., '\', '/'))/config/*"/>
</xsl:template>
<xsl:template match="config/*">
<xsl:copy>
<xsl:copy-of select="node(),$new"/>
<file><xsl:value-of select="tokenize(document-uri(/), '/')[last()]"/></file>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
You could also do this using collection() instead of creating the separate mergeData.xml file:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="newRoot" select="'config'"/>
<xsl:param name="new">
<info3>XXX</info3>
</xsl:param>
<xsl:template match="/">
<xsl:element name="{$newRoot}">
<xsl:apply-templates select="collection('file:///C:/some/path?select=[0-9]*.xml')/*/info"/>
</xsl:element>
</xsl:template>
<xsl:template match="info">
<xsl:copy>
<xsl:copy-of select="#*|node(),$new"/>
<file><xsl:value-of select="tokenize(document-uri(/),'/')[last()]"/></file>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
One additional alternative, since you're using Saxon, is to use saxon:discard-document() along with your mergeData.xml input. If you have a lot of files listed in mergeData.xml, this can help with memory consumption. (It does require Saxon PE or EE or an older version of Saxon that allows the extension functions.)
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="new">
<info3>XXX</info3>
</xsl:param>
<xsl:template match="/mergeData">
<xsl:element name="{#newRoot}">
<xsl:apply-templates select="filelist/fileItem"/>
</xsl:element>
</xsl:template>
<xsl:template match="fileItem">
<xsl:apply-templates select="document(.)/saxon:discard-document(.)/*/*" xmlns:saxon="http://saxon.sf.net/"/>
</xsl:template>
<xsl:template match="info">
<xsl:copy>
<xsl:copy-of select="#*|node(),$new"/>
<file><xsl:value-of select="tokenize(document-uri(/),'/')[last()]"/></file>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

The following XSLT yields your required result:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >
<xsl:output method="xml" encoding="UTF-8"/>
<xsl:param name="new">
<info>XXX</info>
</xsl:param>
<xsl:template match="/mergeData">
<config>
<xsl:for-each select="filelist/fileItem">
<xsl:variable name="filename" select="text()"/>
<xsl:for-each select="document($filename)/config/info">
<info>
<xsl:copy-of select="./*"/>
<xsl:element name="info{count(*)+1}">
<xsl:value-of select="$new"/>
</xsl:element>
<file><xsl:value-of select="$filename"/></file>
</info>
</xsl:for-each>
</xsl:for-each>
</config>
</xsl:template>
</xsl:stylesheet>
Notes:
This already works with XSLT 1.0, hence I changed the XSLT declaration.
Since this approach is top down with a predefined sub structure it does not use the attribute newRoot of your input file anymore.
The answer does not extract the basename of your input files but uses the full path supplied in the merge configuration. You may want to revert this simplification. Of course, using tokenize pushes it back to XSLT 2.0 or extended functions.

Related

XSLT - add prefix for namespace

Here's my input XML:
<?xml version="1.0" encoding="UTF-8"?>
<Sync
xmlns="http://schema.infor.com/InforOAGIS/2" languageCode="en-US" versionID="2.8.0">
<Data>
<ID>0001</ID>
<Text>ABCD</Text>
</Data>
</Sync>
And here's my expected outcome:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Sync xmlns:ns0="http://schema.infor.com/InforOAGIS/2"
languageCode="en-US"
versionID="2.8.0">
<DataArea xmlns:dns="http://schema.infor.com/InforOAGIS/2" xmlns="">
<ID>0001</ID>
<Text>ABCD</Text>
</DataArea>
</ns0:Sync>
My current XSLT as below (https://xsltfiddle.liberty-development.net/nbiE19N).
There are 2 problems:
I have the extra xmlns="" in DataArea element. I only want to add the dns namespace.
I cannot add the ns0 prefix for my namespace
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/*:Sync">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*:Sync/*:Data">
<DataArea>
<xsl:namespace name="dns" select="'http://schema.infor.com/InforOAGIS/2'"/>
<ID>
<xsl:value-of select="/*:Sync/*:Data/*:ID"/>
</ID>
<Text>
<xsl:value-of select="/*:Sync/*:Data/*:Text"/>
</Text>
</DataArea>
</xsl:template>
</xsl:stylesheet>
Any suggestion is appreciated!
Does this return the expected result:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="http://schema.infor.com/InforOAGIS/2">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/Sync">
<ns0:Sync xmlns:ns0="http://schema.infor.com/InforOAGIS/2">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</ns0:Sync>
</xsl:template>
<xsl:template match="Data">
<DataArea xmlns:dns="http://schema.infor.com/InforOAGIS/2">
<xsl:apply-templates/>
</DataArea>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
P.S. I am not sure why you need the xmlns:dns="http://schema.infor.com/InforOAGIS/2" declaration; it's not being used anywhere.

Splitting Multiline XML tag into multiple Nodes

I have a XML below, where new lines are added after each line at Note__c tag. I need to produce the XML by splitting them into multiple Note__c tags.
Input XML-
<?xml version="1.0" encoding="UTF-8"?>
<snotification>
<data>
<schema>yify-xjmoeLTbNXA560rHQ</schema>
<payload>
<Note__c>01/15/2020
123456
DFGRTE766
6tgBFR</Note__c>
<Line_Length__c>72.0</Line_Length__c>
<CreatedById>00554000003OENsAAO</CreatedById>
<Contact_Name__c/>
<Sent_By_Name__c>SBM</Sent_By_Name__c>
<CreatedDate>2020-01-15T16:10:40.551Z</CreatedDate>
<Order_Number__c>14831</Order_Number__c>
<Does_not_require_reformatting__c>false</Does_not_require_reformatting__c>
</payload>
<event>
<replayId>139219</replayId>
</event>
</data>
<channel>/event/Order_Note__e</channel>
</snotification>
Where Note__c contains multiple strings with new line added after each(except the last one)
Expected Output -
<?xml version="1.0" encoding="UTF-8"?>
<snotification>
<data>
<schema>yify-xjmoeLTbNXA560rHQ</schema>
<payload>
<Notes>
<Note__c>01/15/2020</Note__c>
<Note__c>123456</Note__c>
<Note__c>DFGRTE766</Note__c>
<Note__c>6tgBFR</Note__c>
</Notes>
<Line_Length__c>72.0</Line_Length__c>
<CreatedById>00554000003OENsAAO</CreatedById>
<Contact_Name__c/>
<Sent_By_Name__c>SBM</Sent_By_Name__c>
<CreatedDate>2020-01-15T16:10:40.551Z</CreatedDate>
<Order_Number__c>14831</Order_Number__c>
<Does_not_require_reformatting__c>false</Does_not_require_reformatting__c>
</payload>
<event>
<replayId>139219</replayId>
</event>
</data>
<channel>/event/Order_Note__e</channel>
</snotification>
I have written this XSLT but it is missing few tags under the payload element -
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="snotification/data/payload">
<Notes>
<xsl:for-each select="tokenize(Note__c,'\n')">
<Note__c>
<xsl:value-of select="."/>
</Note__c>
</xsl:for-each>
</Notes>
</xsl:template>
</xsl:stylesheet>
Output of this-
<?xml version="1.0" encoding="UTF-8"?>
<snotification>
<data>
<schema>yify-xjmoeLTbNXA560rHQ</schema>
<Notes>
<Note__c>01/15/2020</Note__c>
<Note__c> 123456</Note__c>
<Note__c> DFGRTE766</Note__c>
<Note__c> 6tgBFR</Note__c>
</Notes>
<event>
<replayId>139219</replayId>
</event>
</data>
<channel>/event/Order_Note__e</channel>
</snotification>
not sure what is missing.
Thanks
Sugata
Change your XSLT to
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="snotification/data/payload/Note__c">
<Notes>
<xsl:for-each select="tokenize(.,'\n')">
<Note__c>
<xsl:value-of select="normalize-space(.)"/>
</Note__c>
</xsl:for-each>
</Notes>
</xsl:template>
</xsl:stylesheet>
The output should be as desired.

XSLT transformation with multiple nodes

I have multiple occurence nodes which need to be generated at output using XSLT transformation. Could you please help me on this.
Following XSLT code only generate one node occurrence only. Could you please help me with below XSLT code how to generate multiple nodes elements in Input XML
Input XML
<?xml version="1.0" encoding="UTF-8" ?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" >
<soapenv:Body>
<ns1:getGenResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
<ns1:getGenReturn xsi:type="soapenc:Array" soapenc:arrayType="xsd:anyType[2]" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
</ns1:getGenReturn>
</ns1:getGenResponse>
<multiRef id="id0" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns2:Gen" xmlns:soapenc=http://schemas.xmlsoap.org/soap/encoding/>
<name xsi:type="xsd:string">ULM</name>
<mail xsi:type="xsd:string">ulm#gmail.com</mail>
</multiRef>
<multiRef id="id1" soapenc:root="0" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xsi:type="ns3:Gen" " xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">
<name xsi:type="xsd:string">ABC</name>
<mail xsi:type="xsd:string">abc#gmail.com</mail>
</multiRef>
</soapenv:Body>
</soapenv:Envelope>
XSLT Code used for this transformation
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" x
xmlns:response="http://tempuri.org/"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- Output -->
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:if test="//soap:Body/multiRef">
<xsl:element name="getGenResponse">
<xsl:element name="getGenReturn">
<xsl:element name="name"><xsl:value-of select="//name"/></xsl:element>
<xsl:element name="mail"><xsl:value-of select="//mail"/></xsl:element>
</xsl:element>
</xsl:element>
</xsl:if>
</xsl:template>
<!-- 'Copy ' node -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Output from above XSLT
<?xml version="1.0" encoding="UTF-8"?>
<getGenResponse>
<getGenReturn>
<name> ULM </name>
<mail>ulm#gmail.com<mail>
</getGenReturn>
/getGenResponse>
Output expected
<?xml version="1.0" encoding="UTF-8"?>
<getGenResponse>
<getGenReturn>
<name> ULM </name>
<mail>ulm#gmail.com<mail>
</getGenReturn>
<getGenReturn>
<name>ABC</name>
<mail>abc#gmail.com<mail>
</getGenReturn>
/getGenResponse>
At you moment all you are doing is testing a multiRef element exists, and outputting only one new getGenReturn element.
All you really need to do is replace the xsl:if with xsl:for-each to select all the elements, then you will get one getGenReturn for each. And also change the xsl:value-of to use a relative path
<xsl:template match="/">
<xsl:element name="getGenResponse">
<xsl:for-each select="//soap:Body/multiRef">
<xsl:element name="getGenReturn">
<xsl:element name="name"><xsl:value-of select="name"/></xsl:element>
<xsl:element name="mail"><xsl:value-of select="mail"/></xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
Or better still, do this, as xsl:element is not really needed here if you are using static names
<xsl:template match="/">
<getGenResponse>
<xsl:for-each select="//soap:Body/multiRef">
<getGenReturn>
<name><xsl:value-of select="name"/></name>
<mail><xsl:value-of select="mail"/></mail>
</getGenReturn>
</xsl:for-each>
</getGenResponse>
</xsl:template>
Note, you don't actually need the identity template in this case. Try this XSLT:
<xsl:stylesheet version="1.0" xmlns:response="http://tempuri.org/"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="soap response">
<!-- Output -->
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<getGenResponse>
<xsl:for-each select="//soap:Body/multiRef">
<getGenReturn>
<name><xsl:value-of select="name"/></name>
<mail><xsl:value-of select="mail"/></mail>
</getGenReturn>
</xsl:for-each>
</getGenResponse>
</xsl:template>
</xsl:stylesheet>
<xsl:stylesheet version="1.0" xmlns:response="http://tempuri.org/"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="soap response">
<!-- Output -->
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<getGenResponse>
<xsl:for-each select="//soap:Body/multiRef">
<getGenReturn>
<name><xsl:value-of select="name"/></name>
<mail><xsl:value-of select="mail"/></mail>
</getGenReturn>
</xsl:for-each>
</getGenResponse>
</xsl:template>
</xsl:stylesheet>

XSLT add root node if not exists

I have some XML and having a difficult time transforming it.
Example XML:
<?xml version="1.0" encoding="utf-8"?>
<Cars xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Car> ... </Car>
</Cars>
I would like to change it to:
<?xml version="1.0" encoding="utf-8"?>
<Depot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Cars>
<Car> ... </Car>
</Cars>
</Depot>
Sounds simple enough but the problem is some data is already in the expected format, in which case I don't want to apply the transform. How do I achieve this?
EDIT
Some starting XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" mlns:xsi="http://www.w3.org/2001/XMLSchema-
instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="Cars">
<Depot>
<Cars>
<xsl:apply-templates select="*"/>
</Cars>
</Depot>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I think you only want to match Cars if it is the root element, so instead of your template matching "Cars", change it to match "/Cars"
<xsl:template match="/Cars">
Try this XSLT (which I have slightly amended to get the first template to call the identity template)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
<xsl:output method="xml" indent="yes" />
<xsl:template match="/Cars">
<Depot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsl:call-template name="identity" />
</Depot>
</xsl:template>
<xsl:template match="#*|node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I think that it is just necessary to use a choose in the root template to test if the node Depot exists, if not create it:
<xsl:template match="/">
<xsl:choose>
<xsl:when test="Depot">
<xsl:apply-templates/>
</xsl:when>
<xsl:otherwise>
<Depot>
<xsl:apply-templates/>
</Depot>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()" />
</xsl:copy>
</xsl:template>
This also gives same output.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
<xsl:template match="Cars">
<Depot>
<xsl:copy-of select="."/>
</Depot>
</xsl:template>
</xsl:stylesheet>

Grouping of same tags under an element in xslt

I am working XSLT where the source looks like this.
Source:
<Data>
<AB>all</AB>
<AB>all2</AB>
<CD>hhhhhh</CD>
<DE>hhhshhh</DE>
</Data>
Need to write XSLT to get output as
<Info>
<XXX>
<TTT value="all"/>
<TTT value="all2"/>
</XXX>
<!-- ....-->
<!-- ..to het all the elements.. -->
</Info>
I have to write xslt to match tag.
<xsl:template match="AB">
</xsl:template>
I can do it by matching Data tag.
<?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" indent="yes"/>
<xsl:template match="Data">
<info>
<XXX>
<xsl:for-each select="AB">
<TTT>
<xsl:attribute name="value">
<xsl:value-of select="."/>
</xsl:attribute>
</TTT>
</xsl:for-each>
</XXX>
</info>
</xsl:template>
</xsl:stylesheet>
Can any one help me out how to do it by matching AB tag
<xsl:template match="AB">
</xsl:template>
Thank you.
I think you are asking how do you use xsl:apply-templates. If so, your XSLT would look like this
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Data">
<info>
<XXX>
<xsl:apply-templates select="AB"/>
</XXX>
</info>
</xsl:template>
<xsl:template match="AB">
<TTT value="{.}"/>
</xsl:template>
</xsl:stylesheet>
Do also note the use of Attribute Value Templates in the AB template to simplify the XSLT.
If you also require the other non-AB elements to be output unchanged, you would make use of the identity transform in your XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Data">
<info>
<XXX>
<xsl:apply-templates select="AB"/>
</XXX>
<xsl:apply-templates select="node()[not(self::AB)]" />
</info>
</xsl:template>
<xsl:template match="AB">
<TTT value="{.}"/>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This would also match all non-AB elements, outputing following the XXX element. In your case, it would output the following:
<info>
<XXX>
<TTT value="all" />
<TTT value="all2" />
</XXX>
<CD>hhhhhh</CD>
<DE>hhhshhh</DE>
</info>
Of course, there is no reason you couldn't have other templates matching elements like CD or DE to transform those too.