Transforming xml with namespaces using XSLT - xslt

I have the following xml
<?xml version="1.0" encoding="UTF-8"?>
<typeNames xmlns="http://www.dsttechnologies.com/awd/rest/v1" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<typeName recordType="case" href="awdServer/awd/services/v1/businessareas/SAMPLEBA/types/SAMPLECASE">SAMPLECASE</typeName>
<typeName recordType="folder" href="awdServer/awd/services/v1/businessareas/SAMPLEBA/types/SAMPLEFLD">SAMPLEFLD</typeName>
<typeName recordType="source" href="awdServer/awd/services/v1/businessareas/SAMPLEBA/types/SAMPLEST">SAMPLEST</typeName>
<typeName recordType="transaction" href="awdServer/awd/services/v1/businessareas/SAMPLEBA/types/SAMPLEWT">SAMPLEWT</typeName>
</typeNames>
I want to transform above xml as below by using XSLT:
<response>
<results>
<source>
SAMPLEST
</source>
</results>
</response>
</xsl:template>
I just want to get the source from the input xml to the output xml.
I am trying with the following xml, but couldn't get the required output xml:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:v="http://www.dsttechnologies.com/awd/rest/v1" version="2.0" exclude-result-prefixes="v">
<xsl:output method="xml" version="1.0" omit-xml-declaration="yes" 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="typeNames">
<response>
<results>
<source>
<xsl:value-of select="source" />
</source>
</results>
</response>
</xsl:template>
</xsl:stylesheet>

I. Namespace in input xml
<typeNames xmlns="http://www.dsttechnologies.com/awd/rest/v1"...
xmlns puts self + all child nodes into a namespace. This namespace does not need any prefix.
II. Namespace in XSLT
... xmlns:v="http://www.dsttechnologies.com/awd/rest/v1"...
You prefixed the namespace (same uri as source) with v, so you have to write this prefix in your xpath as well.
<xsl:template match="v:typeNames">
[XSLT 2.0: you also can add xpath-default-namespace="uri" in the stylesheet section, to define a default namespace for all xpath-expressions. Therefore you dont have to prefix the namespace.]
III. Guessing on given input xml
<xsl:value-of select="source" /> -> <typeName recordType="source"..>SAMPLEST</typeName>
If you want to select the shown xml-node, you have to write one of the following:
absolute, without any context node:
/v:typeNames/v:typeName[#recordType = 'source']
on context-node typeNames:
v:typeName[#recordType = 'source']
[<xsl:value-of select="..."/> will return the text-node(s), e.g. "SAMPLEST"]
EDIT:
What if there are two tags.
First things first: <xsl:value-of in XSLT 1 can only work with 1 node! If the xpath expression matches more than one node, it will just process the first one!
Solve it like this way:
...
<results>
<xsl:apply-templates select="v:typeName[#recordType = 'source']"/>
</results>
...
<xsl:template match="v:typeName[#recordType = 'source']">
<source>
<xsl:value-of select="."/>
</source>
</xsl:template>
The apply-templates within results searches for all typeName..source. The matching template listens to that node and creates the xml <source>....

Related

Inserting XML String into XSLT between XSLT templates

I have a problem on inserting XML strings into the XSLT I have. Particularly, I have a sample XML string here:
<md:People>
<md:Job>
<md:JobFunction>Actor</md:JobFunction>
<md:BillingBlockOrder>1</md:BillingBlockOrder>
</md:Job>
<md:Name>
<md:DisplayName language="en-US">Vice Ganda</md:DisplayName>
</md:Name>
</md:People>
and I want to insert it into the XSLT I have (see <!-- INSERT "People" Metadata XML STRING HERE -->):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soa="urn:telestream.net:soa:core" exclude-result-prefixes='soa' version="1.0">
<xsl:variable name="basicContentID"><xsl:value-of select="/soa:Label/soa:Parameter[#name='basicContentID']/text()"/></xsl:variable>
<xsl:variable name="movieTitle"><xsl:value-of select="/soa:Label/soa:Parameter[#name='movieTitle']/text()"/></xsl:variable>
<xsl:variable name="releaseYear"><xsl:value-of select="/soa:Label/soa:Parameter[#name='releaseYear']/text()"/></xsl:variable>
<xsl:variable name="releaseDate"><xsl:value-of select="/soa:Label/soa:Parameter[#name='releaseDate']/text()"/></xsl:variable>
<xsl:template match="/">
<mdmec:CoreMetadata xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:md="http://www.movielabs.com/schema/md/v2.6/md" xmlns:mdmec="http://www.movielabs.com/schema/mdmec/v2.6" xsi:schemaLocation="http://www.movielabs.com/schema/mdmec/v2.6/mdmec-v2.6.xsd">
<mdmec:Basic ContentID="{/soa:Label/soa:Parameter[#name='basicContentID']/text()}">
<md:LocalizedInfo language="{/soa:Label/soa:Parameter[#name='metadataLanguage']/text()}">
<md:TitleDisplayUnlimited><xsl:value-of select="$movieTitle"/></md:TitleDisplayUnlimited>
</md:LocalizedInfo>
<!-- INSERT "People" Metadata XML STRING HERE -->
</mdmec:Basic>
<md:ReleaseYear><xsl:value-of select="$releaseYear"/></md:ReleaseYear>
<md:ReleaseDate><xsl:value-of select="$releaseDate"/></md:ReleaseDate>
</mdmec:CoreMetadata>
</xsl:template>
</xsl:stylesheet>
...to have an XML output like this:
<?xml version="1.0" encoding="utf-8"?>
<mdmec:CoreMetadata xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:md="http://www.movielabs.com/schema/md/v2.6/md" xmlns:mdmec="http://www.movielabs.com/schema/mdmec/v2.6" xsi:schemaLocation="http://www.movielabs.com/schema/mdmec/v2.6/mdmec-v2.6.xsd">
<mdmec:Basic ContentID="md:cid:org:abs_cbn:StarCinema-BeautyAndTheBestie2015">
<md:LocalizedInfo language="en-US">
<md:TitleDisplayUnlimited>Beauty and The Bestie</md:TitleDisplayUnlimited>
</md:LocalizedInfo>
<!-- Where "People" Metadata should be appearing -->
<md:People>
<md:Job>
<md:JobFunction>Actor</md:JobFunction>
<md:BillingBlockOrder>1</md:BillingBlockOrder>
</md:Job>
<md:Name>
<md:DisplayName language="en-US">Vice Ganda</md:DisplayName>
</md:Name>
</md:People>
</mdmec:Basic>
<md:ReleaseYear><xsl:value-of select="$releaseYear"/></md:ReleaseYear>
<md:ReleaseDate><xsl:value-of select="$releaseDate"/></md:ReleaseDate>
</mdmec:CoreMetadata>
Basically, the XML string I wanted to insert is in between the <mdmec:basic> code, and defining an xslt template in-between the root is not allowed. How can I go through this?
Thanks for all your help in advance!
EDIT: I tried to reproduce the sample from this thread [https://stackoverflow.com/questions/54535142/xml-string-to-xml-by-xslt] by re-creating the XML string I have:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Root>
<md:People>
<md:Job>
<md:JobFunction>Actor</md:JobFunction>
<md:BillingBlockOrder>1</md:BillingBlockOrder>
</md:Job>
<md:Name>
<md:DisplayName language="en-US">Vice Ganda</md:DisplayName>
</md:Name>
</md:People>
</Root>
...and inserted into the XSLT I have:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:soa="urn:telestream.net:soa:core" exclude-result-prefixes='soa' version="1.0">
<xsl:variable name="basicContentID"><xsl:value-of select="/soa:Label/soa:Parameter[#name='basicContentID']/text()"/></xsl:variable>
<xsl:variable name="movieTitle"><xsl:value-of select="/soa:Label/soa:Parameter[#name='movieTitle']/text()"/></xsl:variable>
<xsl:variable name="releaseYear"><xsl:value-of select="/soa:Label/soa:Parameter[#name='releaseYear']/text()"/></xsl:variable>
<xsl:variable name="releaseDate"><xsl:value-of select="/soa:Label/soa:Parameter[#name='releaseDate']/text()"/></xsl:variable>
<xsl:output omit-xml-declaration="yes" />
<xsl:template match="/">
<mdmec:CoreMetadata xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:md="http://www.movielabs.com/schema/md/v2.6/md" xmlns:mdmec="http://www.movielabs.com/schema/mdmec/v2.6" xsi:schemaLocation="http://www.movielabs.com/schema/mdmec/v2.6/mdmec-v2.6.xsd">
<mdmec:Basic ContentID="{/soa:Label/soa:Parameter[#name='basicContentID']/text()}">
<md:LocalizedInfo language="{/soa:Label/soa:Parameter[#name='metadataLanguage']/text()}">
<md:TitleDisplayUnlimited><xsl:value-of select="$movieTitle"/></md:TitleDisplayUnlimited>
</md:LocalizedInfo>
<xsl:template match="/Root">
<xsl:value-of select="normalize-space(.)" disable-output-escaping="yes" />
</xsl:template>
</mdmec:Basic>
<md:ReleaseYear><xsl:value-of select="$releaseYear"/></md:ReleaseYear>
<md:ReleaseDate><xsl:value-of select="$releaseDate"/></md:ReleaseDate>
</mdmec:CoreMetadata>
</xsl:template>
</xsl:stylesheet>
It appears to have an error: 'xsl:template' cannot be a child of the 'mdmec:Basic' element
If you manage to pass the XML string into your XSL transformation as a parameter, you can output it with escaping disabled:
<xsl:stylesheet ...>
<xsl:param name="xmlstring"/>
...
</md:LocalizedInfo>
<!-- INSERT "People" Metadata XML STRING HERE -->
<xsl:value-of select="$xmlstring" disable-output-escaping="yes"/>
</mdmec:Basic>
...

Is there a way to replace the for-each with apply-templates in an XSLT Transform?

Environment: XSLT 1.0
The transform will take each element in partOne section and lookup #field attribute in partTwo section using #find attribute and then output #value attribute.
I'm using a for-each loop and was wondering if apply-templates could work?
xml
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="file.xslt"?>
<xml>
<partOne>
<target field="hello"/>
<target field="world"/>
</partOne>
<partTwo>
<number input="2" find="hello" value="valone" />
<number input="2" find="world" value="valtwo" />
<number input="2" find="hello" value="valthree" />
<number input="2" find="world" value="valfour" />
</partTwo>
</xml>
xsl
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="/xml/partOne/target">
,<xsl:value-of select="#field"/>
<xsl:for-each select="/xml/partTwo/number[#find=current()/#field]">
,<xsl:value-of select="#value"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Output:
,hello
,valone
,valthree
,world
,valtwo
,valfour
Well, it seems straight-forward to change
<xsl:for-each select="/xml/partTwo/number[#find=current()/#field]">
,<xsl:value-of select="#value"/>
</xsl:for-each>
to
<xsl:apply-templates select="/xml/partTwo/number[#find=current()/#field]"/>
with a template
<xsl:template match="partTwo/number">
,<xsl:value-of select="#value"/>
</xsl:template>
As your root template so far processes all elements you need to change it to
<xsl:template match="/">
<xsl:apply-templates select="xml/partOne"/>
</xsl:template>
to avoid processing the partTwo element(s) twice.
For the cross-reference you might want to use a key in both versions:
<xsl:key name="ref" match="partTwo/number" use="#find"/>
and then select="key('ref', #field)" instead of select="/xml/partTwo/number[#find=current()/#field]" for the apply-templates or for-each.

XSL query inner xml

Say this is my xml :
<History>
<University>TSU</University>
<Payload>
<Attrib Order="0">OVERSEA</Attrib>
<Attrib Order="1">GRADE2</Attrib>
<Attrib Order="2"><Person><ID>TQR344</ID></Person></Attrib>
<Attrib Order="3">3566644</Attrib>
</Payload>
</History>
And I want to query the inner XML inside Order=2 tag and read ID of the person.
I have created this so far :
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="xml" encoding="UTF-8" indent="yes" omit-xml-declaration="no" />
<xsl:template match="/History">
<xsl:apply-templates select="/History" />
</xsl:template>
<xsl:template name="Person" match="//History">
<Event>
<Uni><xsl:value-of select="University" /></Uni>
<ID><xsl:value-of select="Payload/Attrib[#Order='2']/Person/ID" disable-output-escaping="yes" /></ID>
</Event>
</xsl:template>
</xsl:stylesheet>
But as you can see it is not working.
Also I assigned the inner XML into a variable and tried to query that variable and It didn't work too.
Is it possible to do that via xsl ?
Limitations : I cannot change xml format. But maybe I was able to move from xsl ver 1 to new versions.
I want to query the inner XML inside Order=2 tag
The tag in question does not contain any XML; its content is a string and needs to be manipulated using string functions. Try:
XSLT 1.0
<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="/History">
<Event>
<Uni>
<xsl:value-of select="University" />
</Uni>
<ID>
<xsl:value-of select="substring-before(substring-after(Payload/Attrib[#Order='2'], '<ID>'),'</ID><')"/>
</ID>
</Event>
</xsl:template>
</xsl:stylesheet>
Note:
1. This:
<xsl:template match="/History">
<xsl:apply-templates select="/History" />
</xsl:template>
creates an infinite loop and will crash your processor.
2. Alternatively, you could serialize the string back into XML and process the result as XML; in XSLT 1.0, this can be done only by outputting the string with the escaping disabled, saving the result as a new document, then processing the new document with another XSLT stylesheet. Using XSLT 3.0 (or a processor that supports serializing as an extension) this can be all done during the same transformation.

Coying an entire xml in a Variable using xslt

How can i copy an entire xml as is in an Variable?
Below is the sample xml:
<?xml version="1.0" encoding="UTF-8"?>
<products author="Jesper">
<product id="p1">
<name>Delta</name>
<price>800</price>
<stock>4</stock>
</product>
</products>
I have tried below xslt but it is not working.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:variable name="reqMsg">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:variable>
<xsl:copy-of select="$reqMsg"/>
</xsl:template>
</xsl:stylesheet>
Regards,
Rahul
Your transformation fails because at a certain point, it tries to create a variable (result tree fragment) containing an attribute node. This is not allowed.
It's not really clear what you mean by "copying an entire XML to a variable". But you probably want to simply use the select attribute on the root node:
<xsl:variable name="reqMsg" select="/"/>
This will actually create variable with a node-set containing the root node of the document. Using this variable with xsl:copy-of will output the whole document.
<xsl:copy-of select="document('path/to/file.xml')" />
Or if you need it more than once, to avoid repeating the doc name:
<xsl:variable name="filepath" select="'path/to/file.xml'" />
…
<xsl:copy-of select="document($filepath)" />
The result of document() should be cached IIRC, so don't worry about calling it repeatedly.

XSLT transform error

I have the following xml:
<RootNode xmlns="http://someurl/path/path/path">
<Child1>
<GrandChild1>Value</GrandChild1>
<!-- Lots more elements in here-->
</Child1>
</RootNode>
I have the following xslt:
<xsl:stylesheet version="1.0" xmlns="http://someurl/path/path/path" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<NewRootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NewChild1>
<xsl:for-each select="RootNode/Child1">
<NewNodeNameHere>
<xsl:value-of select="GrandChild1"/>
</NewNodeNameHere>
<!-- lots of value-of tags in here -->
</xsl:for-each>
</NewChild1>
</NewRootNode >
</xsl:template>
</xsl:stylesheet>
The problem: this is the my result:
<?xml version="1.0" encoding="utf-8"?>
<NewRootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NewChild1 />
</NewRootNode>
I am expecting to see:
<?xml version="1.0" encoding="utf-8"?>
<NewRootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NewChild1>
<NewNodeNameHere>Value</NewNodeNameHere>
<!-- Other new elements with values from the xml file -->
</NewChild1>
</NewRootNode>
I am missing of the information inside of NewChild1 that should be there.
I think my for-each select is correct, so the only thing I can think of is that there is a problem with the namespace in the Xml and the namespace in the xslt. Can anybody see what I'm doing wrong?
The problem is caused by the namespaces.
Since the xml defines xmlns="http://someurl/path/path/path", it is not in the default namespace anymore.
You can define that namespace with an name like xmlns:ns="http://someurl/path/path/path" in the xsl, and then use that name in the XPath expression.
The following works for me:
<xsl:stylesheet version="1.0" xmlns:ns="http://someurl/path/path/path" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<NewRootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NewChild1>
<xsl:for-each select="ns:RootNode/ns:Child1">
<NewNodeNameHere>
<xsl:value-of select="ns:GrandChild1"/>
</NewNodeNameHere>
<!-- lots of value-of tags in here -->
</xsl:for-each>
</NewChild1>
</NewRootNode >
</xsl:template>
</xsl:stylesheet>
The stylesheet namespace should be http://www.w3.org/1999/XSL/Transform instead of http://someurl/path/path/path.
Also, since the input XML uses a namespace all your XPath expressions should be namespace-qualified:
<xsl:template match="/" xmlns:ns1="http://someurl/path/path/path">
<NewRootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NewChild1>
<xsl:for-each select="ns1:RootNode/ns1:Child1">
<NewNodeNameHere>
<xsl:value-of select="ns1:GrandChild1"/>
</NewNodeNameHere>
<!-- lots of value-of tags in here -->
</xsl:for-each>
</NewChild1>
</NewRootNode>
</xsl:template>