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>
Related
I've got an XSLT that is translating from XML A to XML B and the 2 are 95% the same except for the fact XML B has fewer attributes. So, for example, I need to translate:
<PaymentDetail PaymentType="CC" ItemNbr="6" CcardNbr="9999999999999999" CcardExp="0523" CcardVend="AA" PayRmrk="" Description="Advance Payment" Total="999.99" />
to
<PaymentDetail PaymentType="CC" ItemNbr="6" CcardNbr="9999999999999999" CcardExp="0523" CcardVend="AA" Total="999.99" />
I've tried copy with an attribute list, but that seems to be long-winded, the same as just doing element = PaymentDetail, attribute name=..... & basically "hard-coding" the whole thing.
I'm using version 1
The standard solution uses template rules:
<!-- Default template: copy nodes unchanged -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Drop selected attributes -->
<xsl:template match="#PayRmrk | #Description"/>
You could exclude the attributes you don't need in your Xpath statement :
<xsl:template match="PaymentDetail">
<xsl:copy>
<xsl:copy-of select="#* except (#PayRmrk, #Description)"/>
</xsl:copy>
</xsl:template>
<!-- Identity template -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
See it working here : http://xsltfiddle.liberty-development.net/6pS26mv
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.
Is it possible to keep comments in an XML when applying an XSLT to it?
Example (source):
<rootNode>
<!-- My comment --><childElement>5</childElement>
</rootNode>
The sample result after the transformation shall be:
<newRoot>
<!-- My comment --><newChildElement>5</newChildElement>
</newRoot>
How would you write the stylesheet?
That is not proper XML comment syntax in your sample but you can keep all nodes with
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
then you add templates for the nodes to be transformed
<xsl:template match="childElement">
<newChildElement>
<xsl:apply-templates select="#* | node()"/>
</newChildElement>
</xsl:template>
My suggestions assumes the sample as
<rootNode>
<!-- My comment--><childElement>5</childElement>
</rootNode>
and the result as
<rootNode>
<!-- My comment--><newChildElement>5</newChildElement>
</rootNode>
If the childElement is inside the comment then it is more difficult.
Pretty straightforward, if I understand the question correctly:
<xsl:template match="comment()">
<xsl:copy/>
</xsl:template>
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" />
Forking from a previous question I asked, how do I ensure that example/one/field and example/three/field are enclosed in CDATA whilst example/two/field is not?
Input:
<?xml version="1.0"?>
<example>
<one>
<field>CDATA required here</field>
</one>
<two>
<field>No CDATA thanks</field>
</two>
<three>
<field>More CDATA please</field>
</three>
</example>
Required output:
<?xml version="1.0"?>
<example>
<one>
<field><![CDATA[CDATA required here]]></field>
</one>
<two>
<field>No CDATA thanks</field>
</two>
<three>
<field><![CDATA[More CDATA please]]></field>
</three>
</example>
I could specify <xsl:output cdata-section-elements="field"/> but this will affect example/two/field as well. I have tried putting in a path like <xsl:output cdata-section-elements="example/one/field example/three/field"/> but this produces an error (Error XTSE0280: Invalid element name. Invalid QName {example/one/field}). Where am I going wrong?
With your current markup I don't think there is a clean way with XSLT. You would need to use different element names or different namespaces at least to allow you and the XSLT processor's serializer to distinguish which elements to output as CDATA sections and which not.
Or you would need to consider to use disable-output-escaping e.g.
<xsl:template match="one/field | three/field">
<xsl:copy>
<xsl:text disable-output-escaping="yes"><![CDATA[<![CDATA[]]></xsl:text>
<xsl:value-of select="."/>
<xsl:text disable-output-escaping="yes">]]></xsl:text>
</xsl:copy>
</xsl:template>
[edit]
Here is a complete sample stylesheet:
<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="one/field | three/field">
<xsl:copy>
<xsl:text disable-output-escaping="yes"><![CDATA[<![CDATA[]]></xsl:text>
<xsl:value-of select="."/>
<xsl:text disable-output-escaping="yes">]]></xsl:text>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Note however that disable-output-escaping is an optional serialization feature that is not supported by all XSLT processors.