XSL query inner xml - xslt

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.

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>
...

Transform to output xml based on the value of an element in the input xml

I am trying to learn the basics of XSLT, but am stuck on a particular use case. What I want to achieve is to transform one xml file into another xml (I am using XSLT 2.0), but a condition is that the grouping of elements in the output xml is decided by the value of one particular element in the input xml.
I will try to exemplify my question through a made-up example.
Lets say this is an input xml:
<products>
<shoes>
<shoe>
<name>Ecco City</name>
<category>Urban</category>
</shoe>
<shoe>
<name>Timberland Forest</name>
<category>Wildlife</category>
</shoe>
<shoe>
<name>Asics Gel-Kayano</name>
<category>Running</category>
</shoe>
</shoes>
<clothes>
<shorts>
<name>North Face</name>
<category>Wildlife</category>
</shorts>
<shorts>
<name>Adidas Running Shorts</name>
<category>Running</category>
</shorts>
</clothes>
Based on the value of the category element I want to, for each product, list similar products, that is, other products having the same category in the input xml, like this:
<output>
<forSale>
<item>Asics Gel-Kayano</item>
<similarItem>Adidas Running Shorts</similarItem>
</forSale>
</output>
This doesn't seem to be a grouping problem as such. If I understand correctly, you want to do something like:
XSLT 2.0
<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="*"/>
<xsl:key name="product-by-category" match="*" use="category" />
<xsl:template match="/products">
<output>
<xsl:for-each select="*/*">
<forSale>
<item>
<xsl:value-of select="name" />
</item>
<xsl:for-each select="key('product-by-category', category) except .">
<similarItem>
<xsl:value-of select="name" />
</similarItem>
</xsl:for-each>
</forSale>
</xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet>
Applied to your input example, the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<output>
<forSale>
<item>Ecco City</item>
</forSale>
<forSale>
<item>Timberland Forest</item>
<similarItem>North Face</similarItem>
</forSale>
<forSale>
<item>Asics Gel-Kayano</item>
<similarItem>Adidas Running Shorts</similarItem>
</forSale>
<forSale>
<item>North Face</item>
<similarItem>Timberland Forest</similarItem>
</forSale>
<forSale>
<item>Adidas Running Shorts</item>
<similarItem>Asics Gel-Kayano</similarItem>
</forSale>
</output>

Transforming xml with namespaces using 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>....

Find node containing the most recent date when date needs converted to valid date format

I need an xPath to be used in a global variable which will select the 'Policy' node with the most recent dateTime (2014-12-02-04:00). Unfortanately the Time delimeter is a dash instead of 'T' so I can't use max() straight away. If I try to use substring or translate to remove the dashes and colon to simply compare numbers I get the error which states that there cannot be more that one sequence in those functions.
Is there a way to evaluate PolicyEffectiveDate from the root node when it is in 2014-12-02-04:00 format?
/Policies/PolicySummary/Policy[2]/PolicyEffectiveDate
XSLT 2.0 is OK. Also, note that I don't have control over the XML format. Thanks.
Given sample XML of:
<?xml version="1.0" encoding="UTF-8"?>
<Policies>
<PolicySummary>
<Policy>
<PolicyNumber>123</PolicyNumber>
<PolicyEffectiveDate>2014-06-01-04:00</PolicyEffectiveDate>
</Policy>
<Policy>
<PolicyNumber>1234</PolicyNumber>
<PolicyEffectiveDate>2014-12-02-04:00</PolicyEffectiveDate>
</Policy>
<Policy>
<PolicyNumber>12345</PolicyNumber>
<PolicyEffectiveDate>2014-08-02-04:00</PolicyEffectiveDate>
</Policy>
</PolicySummary>
</Policies>
You can simply sort the policies by their "dates" as text. For example:
<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:strip-space elements="*"/>
<xsl:template match="/">
<xsl:for-each select="Policies/PolicySummary/Policy">
<xsl:sort select="PolicyEffectiveDate" data-type="text" order="descending"/>
<xsl:if test="position()=1">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
will return:
<?xml version="1.0" encoding="utf-8"?>
<Policy>
<PolicyNumber>1234</PolicyNumber>
<PolicyEffectiveDate>2014-12-02-04:00</PolicyEffectiveDate>
</Policy>
in your example.

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.