Excluding elements with xslt not working - xslt

I am trying to transform an xml file that contain a list of words and I am trying to exclude some elements from the resulting document, more concretely and
My List is as follows:
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="merge.xsl"?>
<dic:englishWords xmlns:dic = "dictionary">
<dic:words>
<dic:englishWords xmlns:dic = "dictionary">
<dic:title>
English Dictionary
</dic:title>
<dic:author>
<dic:authorsName>
Author:
<dic:name>
User
</dic:name>
<dic:lastName>
Name
</dic:lastName>
</dic:authorsName>
</dic:author>
<dic:words>
<dic:name>Water</dic:name><br/>
<dic:name>Room</dic:name><br/>
<dic:name>Computer</dic:name><br/>
<dic:name>Book</dic:name><br/>
<dic:name>Garage</dic:name><br/>
<dic:name>Car</dic:name><br/>
<dic:name>Ship</dic:name><br/>
<dic:name>Food</dic:name><br/>
<dic:name>Coffee</dic:name><br/>
<dic:name>Program</dic:name><br/>
</dic:words>
</dic:englishWords>
The path to the list of words is contained in an xml file as follows:
<dic:dictionary xmlns:dic = "dictionary">
<dic:Logo>Logo</dic:Logo>
<dic:Author>User Name</dic:Author>
<dic:EnglishWords>english</dic:EnglishWords>
<dic:SwedishTranslation>swedish</dic:SwedishTranslation>
<dic:SwedishWords>swedish</dic:SwedishWords>
<dic:EnglishTranslation>english</dic:EnglishTranslation>
</dic:dictionary>
my transformation is as follows
<!--Declare a parameter with the nodes to be removed-->
<xsl:param name="removeElementsNamed" select="'|dic:author|dic:title'"/>
<!--create a template and call it remove node-->
<xsl:template match="node()|#*" name="removeNode">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!--remove the actual nodes-->
<xsl:template match="*">
<xsl:if test="not(contains($removeElementsNamed, concat('|',name(),'|')))">
<xsl:call-template name="removeNode"/>
</xsl:if>
</xsl:template>
I am trying to follow an example which I have found here:
how to exclude elements
...but in my case it does not work.
Any help will be appreciated...
Bluetxxth

So it looks like you're trying to exclude elements based on whether |ELEMENTNAME| is present in $removeElementsNamed, but dic:author is the only item in that list that has pipes on both sides. It might almost work if you did this:
<xsl:param name="removeElementsNamed" select="'|dic:author|dic:title|'"/>
However this is a bit of a hack.
A better approach would to just do something like this:
<xsl:template match="dic:author | dic:title" />
This should exclude dic:author and dic:title from the output.
Another issue is that this template is misnamed:
<xsl:template match="node()|#*" name="removeNode">
What this template would actually do, if it worked, would be to include nodes that were sent its way, but a template can't both have a match attribute and a name attribute. I would suggest rewriting your XSLT to be like this and starting from here:
<!--Declare a parameter with the nodes to be removed-->
<xsl:template match="dic:author | dic:title" />
<!--create a template and call it remove node-->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>

You could match the element you don't want but not output anything.
<xsl:template match="nodeToMatch" />

Related

How do I match the elements generated by xsl-fo?

I have a .xsl file like this:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:exslt="http://exslt.org/common">
<xsl:template match="/>
<fo:root>
<fo:block>...</fo:block>
</fo:root>
</xsl:template>
</xsl:stylesheet>
How can I use templates to match and style the generated fo elements? For example, if I want to give my fo:table-cells red backgrounds, I'd like to be able to do
<xsl:template match="fo:table-cell">
<xsl:attribute name="background-color">red</xsl:attribute>
</xsl:template>
I found this and then tried something along the lines of
<xsl:template match="/>
<xsl:variable name="foRoot">
<fo:root>
<fo:block>...</fo:block>
</fo:root>
</xsl:variable>
<xsl:apply-templates select="exslt:node-set($foRoot)" />
</xsl:template>
but this results in a stack overflow due to endless recursion. When I try to avoid this, for example by doing
<xsl:apply-templates select="exslt:node-set($foRoot)/*" />
I get an empty document. When trying to fix that by adding
<xsl:copy-of select="$foRoot" />
right after, I don't get any errors but the table-cells still have a default white background.
If you really use an XSLT 2 processor then first of all you don't need exsl:node-set.
As for your template
<xsl:template match="fo:table-cell">
<xsl:attribute name="background-color">red</xsl:attribute>
</xsl:template>
that would match a FO table-cell but transform it into an attribute. So you rather want
<xsl:template match="fo:table-cell">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="background-color">red</xsl:attribute>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
as that adds the attribute to a shallow copy of the element and then keeps processing alive for child elements with apply-templates.
Of course you will also need to add the identity transformation template
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
to make sure the elements you don't want to change are copied through. It might be necessary to use modes to separate processing steps if the other templates you have interfere with the identity transformation.

Using XSLT to copy part of an XML as HTML

I'm using an XSL document to format an XML into HTML.
Part of the XML is an HTML fragment. For example:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="formatter.xsl"?>
<root>
<some-node>text</some-node>
<another-node>text</another-node>
<html-container>
<p>This is an HTML with links and <i>other stuff</i>.</p>
<p>And on and on it goes...</p>
</html-container>
</root>
My XSL does lots of manipulations on the XML, but the part inside <html-container> needs to be copied to the new XML as is - all HTML nodes and attributes.
I've tried using this template:
<xsl:template match="html-container/*">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
But this copies just the child nodes, no other descendants:
<p>This is an HTML with links and other stuff.</p>
<p>And on and on it goes...</p>
I've also tried:
<xsl:template match="html-container//*">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
But then the attributes are copied as text:
<p>This is an HTML with <a>http://www.google.com_blanklinks</a> and <i>other stuff</i>.</p>
<p>And on and on it goes...</p>
Obviously I'm missing something. Any help is appreciated!
If you are using xsl:apply-templates, then you should usually have templates that match the nodes and attributes you select. The one you tried, that matched html-container//* will only match elements, but you also need to match attributes as well.
<xsl:template match="html-container//*|html-container//#*">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
Note, this is very much like the XSLT identity template, which would look like this:
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
This would match elements outside html-container, which may or may not affect your current XSLT.
Alternatively, if the descendants of html-container need to be copies as-is, without any changes, just use xsl:copy-of instead
<xsl:template match="html-container/*">
<xsl:copy-of select="."/>
</xsl:template>

how to change a text of element in a parent tag using xslt

I want to replace the element tag name using xslt. I have an output like this:
<gl-cor:documentInfo>
<gl-cor:entriesType contextRef="journal_context">DocumentID</gl-cor:entriesType>
<gl-cor:uniqueID contextRef="journal_context">RevisionID</gl-cor:uniqueID>
</gl-cor:documentInfo>
<gl-cor:entityInformation>
<gl-cor:entityPhoneNumber>
<gl-cor:phoneNumber contextRef="journal_context">779633</gl-cor:phoneNumber>
</gl-cor:entityPhoneNumber>
<gl-cor:entityFaxNumberStructure>
<gl-cor:entityFaxNumbercontextRef="journal_context">1234-56-89</gl-cor:entityFaxNumber>
</gl-cor:entityFaxNumberStructure>
</gl-cor:entityInformation>
And, I want my output to be looks like this:
<gl-cor:documentInfo>
<gl-cor:entriesType contextRef="journal_context">DocumentID</gl-cor:entriesType>
<gl-bus:uniqueID contextRef="journal_context">RevisionID</gl-cor:uniqueID>
</gl-cor:documentInfo>
<gl-cor:entityInformation>
<gl-bus:entityPhoneNumber>
<gl-bus:phoneNumber contextRef="journal_context">779633</gl-bus:phoneNumber>
</gl-bus:entityPhoneNumber>
<gl-bus:entityFaxNumberStructure>
<gl-bus:entityFaxNumbercontextRef="journal_context">1234-56-89</gl-bus:entityFaxNumber>
</gl-bus:entityFaxNumberStructure>
</gl-cor:entityInformation>
All the children of <gl-cor:entityInformation> should replace instead of gl-cor, it should be gl-bus. Is it possible to do this?
I tried to create a sample xslt but it didn't work. The error occurs in the <gl-bus:phoneNumber>, because I think it is contain a special characters? like "-" and ":".
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="gl-cor:entityInformation/gl-cor:entityPhoneNumber/gl-cor:phoneNumber">
<gl-bus:phoneNumber>
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</gl-bus:phoneNumber>
</xsl:template>
Can someone help me solve this problem? Thanks alot.
First of all gl-cor and gl-bus are namespace prefixes. Namespace prefixes are written before an XML element and seperated from the XML element with a :. So your problem is not because of the characters - and :, these are all valid characters, please also read these articles/tutorials:
http://www.w3schools.com/xml/xml_namespaces.asp
http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references#Predefined_entities_in_XML
To answer your problem we need to know what the namspace URIs are for gl-cor and gl-bus, but it should look like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:gl-cor="http://example.org/gl-cor" xmlns:gl-bus="http://example.org/gl-bus">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="*[ancestor::gl-cor:entityInformation]">
<xsl:element name="gl-bus:{local-name()}">
<xsl:apply-templates select="#*|node()" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
The template *[ancestor::gl-cor:entityInformation] will match on all (grand)children of gl-cor:entityInformation.
NOTE
The namespaces in the XSLT should be updated and match to your input XML:
xmlns:gl-cor="http://example.org/gl-cor"
xmlns:gl-bus="http://example.org/gl-bus"

remove elements based on external file

I have an external setting file which has some nodes holiding attribute values of main xml document. I need to remove certian nodes from mian xml file if the attribute value is there in the setting file.
My setting file looks like this:
setting.xml
<xml>
<removenode titlename="abc" subtitlename="xyz"></removenode>
<removenode titlename="dvd" subtitlename="dvd"></removenode>
</xml>
Main.xml
<xml>
<title titlename="abc">
<subtitle subtitlename="xyz"></subtitle>
</title>
<title titlename="book">
<subtitle subtitlename="book sub title"></subtitle>
</title>
</xml>
Need a script which look for setting.xml file and remove the title element if titlename and subtitlename found in main.xml. The output should be
output.xml
<xml>
<title titlename="book">
<subtitle subtitlename="book sub title"></subtitle>
</title>
</xml>
I tried using document to read setting.xml file but not able to find how to do the match on main.xml file
<xsl:variable name="SuppressionSettings" select="document('Setting.xml')" />
<xsl:variable name="SuppressSetting" select="$SuppressionSettings/xml/removenode" />
.
Any hint how to implement it?
The key is to use an identity/copy pattern and, before each output, check the current (context) node isn't prohibited by the suppression rules nodeset.
<!-- get suppression settings -->
<xsl:variable name='suppression_settings' select="document('http://www.mitya.co.uk/xmlp/settings.xml')/xml/removenode" />
<!-- begin identity/copy -->
<xsl:template match="node()|#*">
<xsl:if test='not($suppression_settings[#titlename = current()/#titlename and #subtitlename = current()/subtitle/#subtitlename])'>
<xsl:copy>
<xsl:apply-templates select='node()|#*' />
</xsl:copy>
</xsl:if>
</xsl:template>
You can run it here (see output source - the 'abc' title node is omitted):
http://www.xmlplayground.com/9oCYKp
This XSLT indicated below works for the given document.
Note that I'm storing the contents of Setting.xml in a variable as you did, however, I'd then use that variable directly in my queries.
An important issue here is that in the match element of a template, variables cannot be used. Therefore, my template matches any <title> elements and then determines in an <xsl:choose> element whether the attributes match any values given in the settings file - if so, the <title> element will be omitted in the output.
As an explanation for why that test attribute in the <xsl:when> does what it should, imagine a comparison of someAttribute = someOtherAttribute not as a restriction that the attribute someAttribute must have the same value as the attribute someOtherAttribute, but rather as the condition that there must be any two attributes someAttribute and someOtherAttribute with the same value.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="SuppressionSettings" select="document('Setting.xml')" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//title">
<xsl:choose>
<xsl:when test="(#titlename = $SuppressionSettings/xml/removenode/#titlename) and (subtitle/#subtitlename = $SuppressionSettings/xml/removenode/#subtitlename)"/>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Here's a more generic answer where the names of the attributes are not hard coded into the XSLT. Like O. R. Mapper pointed out, in XSLT 1.0 you can't use variable references in the match, so I put the document() directly in the predicate. This may not be as efficient as using a variable and then testing the variable.
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[#* = document('setting.xml')/*/removenode/#*]"/>
</xsl:stylesheet>
XML Output (using your 2 xml files with main.xml as the input)
<xml>
<title titlename="book">
<subtitle subtitlename="book sub title"/>
</title>
</xml>

XSLT: add node inner text

This is a slightly version of other question posted here:
XSLT: change node inner text
Imagine i use XSLT to transform the document:
<a>
<b/>
<c/>
</a>
into this:
<a>
<b/>
<c/>
Hello world
</a>
In this case i can't use neither the
<xsl:strip-space elements="*"/>
element or the [normalize-space() != ''] predicate since there is no text in the place where i need to put new text. Any ideas? Thanks.
Here is what I would do:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<!-- identity template to copy everything unless otherwise noted -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- match the first text node directly following a <c> element -->
<xsl:template match="text()[preceding-sibling::node()[1][self::c]]">
<!-- ...and change its contents -->
<xsl:text>Hello world</xsl:text>
</xsl:template>
</xsl:stylesheet>
Note that text nodes contain "surrounding" whitespace - in the sample XML in the question the matched text node is whitespace only, which is why the above works. It will stop to work as soon as the input document looks like this:
<a><b/><c/></a>
because here is no text node following <c>. So if this is too brittle for your use case, an alternative would be:
<!-- <c> nodes get a new adjacent text node -->
<xsl:template match="c">
<xsl:copy-of select="." />
<xsl:text>Hello world</xsl:text>
</xsl:template>
<!-- make sure to remove the first text node directly following a <c> node-->
<xsl:template match="text()[preceding-sibling::node()[1][self::c]]" />
In any case, stuff like the above makes clear why intermixing of text nodes and element nodes is best avoided. This is not always possible (see XHTML). But when you have the chance and the XML is supposed to be purely a container for structural data, staying clear of mixed content makes your life easier.
This transformation inserts the desired text (for generality) after the element named a7:
<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="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="a7">
<xsl:call-template name="identity"/>
<xsl:text>Hello world</xsl:text>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<a>
<a1/>
<a2/>
.....
<a7/>
<a8/>
</a>
the desired result is produced:
<a>
<a1/>
<a2/>
.....
<a7/>Hello world
<a8/>
</a>
Do note:
The use of the identity rule for copying every node of the source XML document.
The overriding of the identity rule by a specific template that carries out the insertion of the new text.
How the identity rule is both applied (on every node) and called by name (for a specific need).
edit: fixed my fail to put proper syntax in.
<xsl:template match='a'>
<xsl:copy-of select="." />
<xsl:text>Hello World</xsl:text>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>