How to test for null string in node xslt - xslt

Original source of xml
<SubdivisionType>
<ID>null</ID>
<Name>null</Name>
<Parent i:nil="true"/>
</SubdivisionType>
String 'null' is generated by E4X. I have to check if SubdivisionType/ID contains another value then 'null' then skip all SubdivisionType tag.
Here is how i do
<xsl:if test="SubdivisionType/ID[text()!=null]" >
...
</xsl:if>
I can't understand how xlst(saxon) treats string 'null'.

I don't know saxon specifically, but as far as I'm aware, that isn't a "null string"... it's a string with the value "null".
I would try changing...
text()!=null
to...
text()!='null'
so that it reads...
<xsl:if test="SubdivisionType/ID[text()!='null']">

#Simar,
if we conside below as the xml
<?xml version="1.0" encoding="utf-8"?>
<xml>
<SubdivisionType>
<ID>null</ID>
<Name>null</Name>
<Parent nil="true"/>
</SubdivisionType>
<SubdivisionType>
<ID>asd</ID>
<Name>null</Name>
<Parent nil="true"/>
</SubdivisionType>
</xml>
XSL:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" `xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match = "SubdivisionType[ID = 'null']" />
</xsl:stylesheet>
OUTPUT :
<?xml version="1.0" encoding="UTF-8"?>
<xml>
<SubdivisionType>
<ID>asd</ID>
<Name>null</Name>
<Parent nil="true"/>
</SubdivisionType>
</xml>

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

XSLT - Select content between two special characters

I have a xml like this,
<doc>
<p>text1 <xml version="1.0" encoding="UTF-16"
standalone="yes"?> text2</p>
</doc>
I need to remove the text content between < and > form above text using XSLT. So expected output is,
<doc>
<p>text1 text2</p>
</doc>
I tried to use regex but I'm wondering how I can catch text between < and > form regex.
Any idea how I can do this using XSLT?
This Should Work.
(<(?:.?\n?)*>)
Then Replace with "" (empty)
Input:
<doc>
<p>text1 <xml version="1.0" encoding="UTF-16"
standalone="yes"?> text2</p>
</doc>
Output:
<doc>
<p>text1 text2</p>
</doc>
See: https://regex101.com/r/0o9hol/1
Using just XSLT-1.0 you can achieve this by applying the following template:
<?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" />
<xsl:template match="p">
<xsl:value-of select="concat(normalize-space(substring-before(text(), '<')),' ',normalize-space(substring-after(text(), '>')))" />
</xsl:template>
<!-- identity template -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This template just copies all nodes with the identity template and applies a special treatment to all <p> elements.
The special treatment of the <p> nodes extracts the text() nodes before < and after > while normalizing the space character occurrence(reducing their count to one) and concatenates the result.
That's all.

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

A sequence of more than one item is not allowed as the second argument of concat()

The below xsl works fine if I do not bring in the "other_location_postal_code" field, which is commented here.
This is because there are multiple records if I bring in that field.
How can I have this xsl evaluate each record so it would write this record twice, once for the one "other location postal code" and the other?
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:e="http://www.taleo.com/ws/tee800/2009/01" xmlns:fct="http://www.taleo.com/xsl_functions" exclude-result-prefixes="e fct">
<xsl:output method="xml" encoding="UTF-8" omit-xml-declaration="no"/>
<xsl:param name="OUTBOUND_FOLDER"/>
<xsl:template match="/">
<source>
<xsl:apply-templates select="//e:Requisition"/>
</source>
</xsl:template>
<xsl:template match="e:Requisition">
<xsl:variable name="job_id" select="e:ContestNumber"/>
<xsl:variable name="other_location_postal_code" select="e:JobInformation/e:JobInformation/e:OtherLocations/e:Location/e:NetworkLocation/e:NetworkLocation/e:ZipCode"/>
<job>
<job_id>
<xsl:value-of select="concat('<','![CDATA[',$job_id,']]','>')"/>
</job_id>
<other_location_postal_code>
<xsl:value-of select="concat('![CDATA[',$other_location_postal_code,']]')"/>
</other_location_postal_code>
</job>
</xsl:template>
</xsl:stylesheet>
I want it to come out like so:
<?xml version="1.0" encoding="UTF-8"?>
<source>
<job>
<job_id><![CDATA[15000005]]></job_id>
<other_location_postal_code><![CDATA[77382]]></other_location_postal_code>
</job>
<job>
<job_id><![CDATA[15000005]]></job_id>
<other_location_postal_code><![CDATA[37567]]></other_location_postal_code>
</job>
</source>
The initial XML looks like so:
<?xml version="1.0" encoding="UTF-8"?>
-<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
-<soapenv:Body>
-<ns1:getDocumentByKeyResponse xmlns:ns1="http://www.taleo.com/ws/integration/toolkit/2005/07" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
-<Document xmlns="http://www.taleo.com/ws/integration/toolkit/2005/07">
-<Attributes>
<Attribute name="count">1</Attribute>
<Attribute name="duration">0:00:00.088</Attribute>
<Attribute name="entity">Requisition</Attribute>
<Attribute name="mode">T-XML</Attribute>
<Attribute name="version">http://www.taleo.com/ws/tee800/2009/01</Attribute>
</Attributes>
-<Content>
-<ExportTXML xmlns="http://www.taleo.com/ws/integration/toolkit/2005/07" xmlns:e="http://www.taleo.com/ws/tee800/2009/01">
-<e:Requisition>
<e:ContestNumber>15000005</e:ContestNumber>
-<e:JobInformation>
-<e:JobInformation>
-<e:OtherLocations>
-<e:Location>
<e:ZipCode>77002</e:ZipCode>
</e:Location>
-<e:Location>
<e:ZipCode>77050</e:ZipCode>
</e:Location>
</e:OtherLocations>
</e:JobInformation>
</e:JobInformation>
</e:Requisition>
</ExportTXML>
</Content>
</Document>
</ns1:getDocumentByKeyResponse>
</soapenv:Body>
</soapenv:Envelope>
The error message simply says that an argument to the concat function is a sequence of more than one node. So some of your variable selects two or more nodes and concat expects as single node for each of its arguments. You will need to decide what you want to output, either only the first node with $var[1] or the concatenation with string-join($var, ' ').
Note that you don't need all those attempts to output CDATA section, you can tell the XSLT processor the cdata-section-elements on the xsl:output direction.
As you have now posted more details here is one suggestion to map each ZipCode element to job element:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:e="http://www.taleo.com/ws/tee800/2009/01"
exclude-result-prefixes="e">
<xsl:output indent="yes" cdata-section-elements="job_id other_location_postal_code"/>
<xsl:template match="/">
<source>
<xsl:apply-templates select="//e:OtherLocations/e:Location/e:ZipCode"/>
</source>
</xsl:template>
<xsl:template match="e:ZipCode">
<xsl:apply-templates select="ancestor::e:Requisition">
<xsl:with-param name="zc" select="current()"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="e:Requisition">
<xsl:param name="zc"/>
<job>
<job_id><xsl:value-of select="e:ContestNumber"/></job_id>
<other_location_postal_code><xsl:value-of select="$zc"/></other_location_postal_code>
</job>
</xsl:template>
</xsl:stylesheet>

exclude empty elements

in my sample xml file, i have this:
<AAA mandatory = "true"> good </AAA>
<BBB mandatory = "true"></BBB>
<CCC />
in the resulting xml, the result should be like this:
<AAA> good </AAA>
<BBB></BBB>
what should i put in my transformation file xslt to produce this xml?
currently, i have this:
<xsl:template match="node()[(#mandatory='true' or (following-sibling::*[#mandatory='true' and string-length(normalize-space(.)) > 0] or preceding-sibling::*[#mandatory='true' and string-length(normalize-space(.)) > 0])) or descendant-or-self::*[string-length(normalize-space(.)) > 0]]">
but this keeps displaying
<CCC />
When I run your XSLT on the input XML I do not get any output.
Your provided XML is not well formed and the XPATH in your "match" is too complicated I think.
I came up with a XSL 1.0 solution but I do not know if you can use that in XSL 2.0. I do not have experience with XSL 2.0.
This XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<list>
<xsl:apply-templates/>
</list>
</xsl:template>
<xsl:template match="*[#mandatory='true']">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
applied to this input XML:
<?xml version="1.0" encoding="UTF-8"?>
<list>
<AAA mandatory="true"> good </AAA>
<BBB mandatory="true"/>
<CCC/>
</list>
gives this output XML:
<?xml version="1.0" encoding="UTF-8"?>
<list>
<AAA> good </AAA>
<BBB/>
</list>
I am not sure if you also want to check on the text length of an element or only on the attribute mandatory. I only check on the attribute in my XSL.