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>
Related
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.
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>
Given the following XML document:
<dialog>
<speech speaker="Robert">
<line>"Once more into the breach", he said.</line>
I can't believe him. I just <emphasis>can't</emphasis> believe him!
</speech>
</dialog>
I'd like to try and capture eveything within speech that isn't in a line already and wrap it in a line, however I need to capture any other elements along with it (eg. the emphasis in the example above).
The result I'd like to achieve is:
<dialog>
<speech speaker="Robert">
<line>"Once more into the breach", he said.</line>
<line>I can't believe him. I just <emphasis>can't</emphasis> believe him!</line>
</speech>
</dialog>
I'm using libxslt and libxml, so I'm stuck with XSLT 1.0.
One way to approach that in XSLT 1.0 is through sibling recursion, as outlined in the following example:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml"/>
<xsl:template match="#* | node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="speech">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="node()[1]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="speech/line">
<xsl:call-template name="identity"/>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:template>
<xsl:template match="speech/node()[not(self::line)
and (not(preceding-sibling::node())
or
preceding-sibling::node()[1][self::line])]">
<xsl:if test="normalize-space() or following-sibling::node()[1][not(self::line)]">
<line>
<xsl:call-template name="identity"/>
<xsl:apply-templates select="following-sibling::node()[1][not(self::line)]"/>
</line>
</xsl:if>
<xsl:apply-templates select="following-sibling::line[1]"/>
</xsl:template>
<xsl:template match="speech/node()[not(self::line)
and preceding-sibling::node()[1][not(self::line)]]">
<xsl:call-template name="identity"/>
<xsl:apply-templates select="following-sibling::node()[1][not(self::line)]"/>
</xsl:template>
</xsl:stylesheet>
With that stylesheet an input sample like
<dialog>
<speech speaker="Robert">
<line>"Once more into the breach", he said.</line>
I can't believe him. I just <emphasis>can't</emphasis> believe him!
</speech>
<speech speaker="Foo">This is a test.
<line>This line is wrapped and should be copied unchanged.</line>
<em>This</em> needs to be <it>wrapped</it>.
</speech>
<speech speaker="Bar"> <em>This</em> should be wrapped.
<line>This line is wrapped and should be copied unchanged.</line>
<it>Test</it>
</speech>
</dialog>
is transformed into
<dialog>
<speech speaker="Robert"><line>"Once more into the breach", he said.</line><line>
I can't believe him. I just <emphasis>can't</emphasis> believe him!
</line></speech>
<speech speaker="Foo"><line>This is a test.
</line><line>This line is wrapped and should be copied unchanged.</line><line>
<em>This</em> needs to be <it>wrapped</it>.
</line></speech>
<speech speaker="Bar"><line> <em>This</em> should be wrapped.
</line><line>This line is wrapped and should be copied unchanged.</line><line>
<it>Test</it>
</line></speech>
</dialog>
The input xml structure is like this:
<Envelopes>
<env:Envelope>
<urn:MyFunction>
<parameter1 attr1='df'>fdad</parameter1>
<parameter2 attr2='ww'>dfsa</parameter2>
<productData>
<Id></Id>
<Description></Description>
<Price><Price>
</productData>
</urn:MyFunction>
</env:Envelope>
<env:Envelope>
<urn:MyFunction1>
<parameter1 attr1='df'>fdad</parameter1>
<parameter2 attr2='ww'>dfsa</parameter2>
<productData>
<Id></Id>
<Description></Description>
<Price><Price>
</productData>
</urn:MyFunction>
</env:Envelope>
<env:Envelope>
<urn:MyFunction1>
<parameter1 attr1='df'>fdad</parameter1>
<parameter2 attr2='ww'>dfsa</parameter2>
<productData>
<Id></Id>
<Description></Description>
<Price><Price>
</productData>
</urn:MyFunction>
</env:Envelope>
<Envelopes>
In my xsl I am doing the below:
<xsl:template match="/">
<NewEnvelopes>
<xsl:for-each select="//productData">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</NewEnvelopes>
</xsl:template>
<xsl:template match="productData/Description">
<Description>new Description</Description>
</xsl:template>
I intend to keep the rest of the productData elements and attributes same, but modify some of them. But the resulting xml gives the description element with the new value, but only the text nodes for the rest of the elements. How can I get all the rest of the nodes of productData?
You need an identity template that will copy the input content. Try adding this to your XSLT:
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
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.