I would like to select all descendant but "blog" nodes. For the example, only subtree should appear on output.
I'm trying this xsl code:
<xsl:template match="rdf:RDF">
<xsl:copy>
<xsl:copy-of select="descendant::*[not(descendant::blog)]"/>
</xsl:copy>
</xsl:template>
for this xml:
<rdf:RDF>
<profesor rdf:ID="profesor_39">
<nombre rdf:datatype="http://www.w3.org/2001/XMLSchema#string"
>Augusto</nombre>
</profesor>
<blog rdf:ID="blog_41">
<entradas>
<entrada_blog rdf:ID="entrada_blog_42">
<etiquetas>
<tecnologia rdf:ID="tecnologia_49">
<termino rdf:datatype="http://www.w3.org/2001/XMLSchema#string"
>Atom</termino>
</tecnologia>
</etiquetas>
<autor>
<alumno rdf:ID="alumno_38">
<nombre rdf:datatype="http://www.w3.org/2001/XMLSchema#string"
>Jesus</nombre>
</alumno>
</autor>
</entrada_blog>
</entradas>
<autores rdf:resource="#alumno_38"/>
<direccion rdf:datatype="http://www.w3.org/2001/XMLSchema#string"
>http://tfg1.unex.es/10comunidad/wordpress/</direccion>
</blog>
</rdf:RDF>
What am I missing? "blog" nodes are still printed on the output.
Thank you.
Here's a complete solution:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- By default, recursively copy all nodes unchanged -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<!-- But strip out <blog> -->
<xsl:template match="blog"/>
<!-- If you want to strip out just the <blog> start and end tags, use this instead:
<xsl:template match="blog">
<xsl:apply-templates/>
</xsl:template>
-->
</xsl:stylesheet>
To omit blog and all its children:
<xsl:template match="RDF">
<xsl:copy-of select="child::node()[name() != 'blog']"/>
</xsl:template>
To omit blog but still o/p its children:
<xsl:template match="RDF">
<xsl:copy-of select="descendant::node()[name() != 'blog']"/>
</xsl:template>
The condition not(descendant::blog) excludes any node that have a descendant named "blog".
So if you have:
<blog id="1">
<test id="1.1">
<blog id="1.1.1" />
</test>
</blog>
<blog id="2">
<test id="2.1" />
</blog>
It will exclude <blog id="1"> and <test id="1.1">, because these nodes both have <blog id="1.1.1"> as a descendant.
But it won't exclude <blog id="1.1.1"> or <blog id="2"> because they don't have any descendant named "blog"
Also note that your select of descendant::*[not(descendant::blog)] will output <test id="2.1"> twice: once within <blog id="2">, and once again by itself.
For a complete solution, the one suggested by Evan Lenz (identity transform with a blank overriding template for "blog" nodes) is probably the one that gives the result you wanted.
Related
Given this XML
<dmodule>
<content>
<warningsAndCautionsRef>
<warningRef id="w001" warningIdentNumber="warning-001">
</warningRef>
<warningRef id="w002" warningIdentNumber="warning-002">
</warningRef>
<cautionRef id="c001" cautionIdentNumber="caution-001">
</cautionRef>
<cautionRef id="c002" cautionIdentNumber="caution-002">
</cautionRef>
</warningsAndCautionsRef>
<faultReporting>
<preliminaryRqmts>
<reqSafety>
<safetyRqmts cautionRefs="c001 c002" warningRefs="w001 w002"/>
</reqSafety>
</preliminaryRqmts>
</faultReporting>
</content>
</dmodule>
I would like to tokenize the attributes #cautionRefs (and #warningRefs) and then find the cautionRef element that matches its #id to the tokenized value:
<xsl:template match="#cautionRefs">
<xsl:for-each select="tokenize(.,'\s')">
<xsl:apply-templates select="//*[#id=.]"/>
</xsl:for-each>
</xsl:template>
but the apply-templates fails: Fatal error during transformation Leading '/' selects nothing: the context item is not a node. It works if I don't tokenize and use string functions instead but that is not desirable.
Desired result:
Tokenize #cautionRefs="c001 c002" (which has multiple parent elements)
So each value is passed to the <cautionRef>template that will retrieve the caution and warning statements, to be displayed in a PDF:
<xsl:apply-templates select="//*[#id='c001']"/>
<xsl:apply-templates select="//*[#id='c002']"/>
I tried using <xsl:key name="id" match="*" use="#id"/> with
<xsl:for-each select="key('id',tokenize(.,'\s'))">
but the for-each is blank.
The above apply-templates will match with this <cautionRef> template, which retrieves the caution and warning statements correctly. I just need help with the context of the #cautionRefs template:
<xsl:template match="cautionRef">
<xsl:variable name="IdentNumber" select="#cautionIdentNumber"/>
<xsl:apply-templates select="//cautionSpec[cautionIdent/#cautionIdentNumber=$IdentNumber]"/>
</xsl:template>
You could use a variable and use that for context:
<xsl:template match="#cautionRefs|#warningRefs">
<xsl:variable name="ctx" select="/"/>
<xsl:for-each select="tokenize(.,'\s')">
<xsl:apply-templates select="$ctx//*[#id=.]"/>
</xsl:for-each>
</xsl:template>
but I would use a key like you hinted at (updated to include context based on comments)...
<xsl:key name="by_id" match="*[#id]" use="#id"/>
<xsl:variable name="root" select="/"/>
<xsl:template match="#cautionRefs|#warningRefs">
<xsl:for-each select="tokenize(.,'\s')">
<xsl:apply-templates select="key('by_id',.,$root)"/>
</xsl:for-each>
</xsl:template>
Here's a full working example. NB it's best to have this level of detail in the actual question; i.e. a sample input file, the XSLT, and output, along with an example of what you want the output to look like.
Input:
<test>
<safetyRqmts cautionRefs="c001 c002" warningRefs="w001"/>
<cautionRef id="c001" cautionIdentNumber="caution-001"/>
<cautionRef id="c002" cautionIdentNumber="caution-001"/>
</test>
Stylesheet:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:template match="*|#*">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:key name="by_id" match="*[#id]" use="#id"/>
<xsl:template match="#cautionRefs|#warningRefs">
<xsl:apply-templates select="key('by_id', tokenize(.))"/>
</xsl:template>
</xsl:stylesheet>
Output:
<test>
<safetyRqmts><cautionRef id="c001" cautionIdentNumber="caution-001"/><cautionRef id="c002" cautionIdentNumber="caution-001"/></safetyRqmts>
<cautionRef id="c001" cautionIdentNumber="caution-001"/>
<cautionRef id="c002" cautionIdentNumber="caution-001"/>
</test>
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>
In that, I want to display only the unique fruit entries in it. Here is the XML tag what I'm using for parsing
<main>
<local id="1" type="Primary">
-<summary Date="23-02-12">
-<fruit>apple</fruit>
-<fruit>Orange</fruit>
</summary>
</local>
<local id="2" type="Primary">
-<summary Date="23-02-12">
-<fruit>apple</fruit>
-<fruit>mango</fruit>
</summary>
</local>
</main>
The expected result should be in the below format
<fruit>apple</fruit>
<fruit>Orange</fruit>
<fruit>Mango</fruit>
Here are the code snippet what I'm trying to use
<xsl:for-each select="main/local">
<xsl:for-each select="symbol/fruit">
<xsl:copy-of select="."/>
<xsl:copy-of select="fruit[not(.=$fruit)]"/>
</xsl:for-each>
</xsl:for-each>
But I'm not getting any output display for this, Can you please help me how can I remove this duplicate redundancy from here.? Thank You in advance
To do this in XSLT1.0 you can make use of a technique called 'Meunchian' grouping. First you define a key to 'look-up' the fruit elements based on the value
<xsl:key name="fruit" match="fruit" use="." />
Then, to get the unique fruit names, you match fruit elements that happen to be the first fruit element in the key (and to check two nodes are the same the generate-id() method is used)
<xsl:apply-templates
select="//fruit[generate-id() = generate-id(key('fruit', .)[1])]" />
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="fruit" match="fruit" use="." />
<xsl:template match="/">
<xsl:apply-templates
select="//fruit[generate-id() = generate-id(key('fruit', .)[1])]" />
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When applied to your sample XML, the following is output:
<fruit>apple</fruit>
<fruit>Orange</fruit>
<fruit>mango</fruit>
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>
Is it possible to merge every sequence of nodes of the same specified type? ('aaa' in this case) (not just first occurrence of sequence)
Here is my XML input:
<block>
<aaa>text1</aaa>
<aaa>text2</aaa>
<aaa><xxx>text3</xxx></aaa>
<bbb>text4</bbb>
<aaa>text5</aaa>
<bbb><yyy>text6</yyy></bbb>
<bbb>text7</bbb>
<aaa>text8</aaa>
<aaa><zzz>text9</zzz></aaa>
<aaa>texta</aaa>
</block>
And I want following output:
<block>
<aaa>text1text2<xxx>text3</xxx></aaa>
<bbb>text4</bbb>
<aaa>text5</aaa>
<bbb><yyy>text6</yyy></bbb>
<bbb>text7</bbb>
<aaa>text8<zzz>text9</zzz>texta</aaa>
</block>
Any help appreciated
Here is another way to do this.
First, match on all the child nodes of the block element
<xsl:template match="block/child::*">
Next, check if the element's most direct sibling has a different name, indicating this is the first of one or more adjacent elements:
<xsl:if test="local-name(preceding-sibling::*[position()=1]) != $name">
If so, you can copy that node. Then, you need to copy of following siblings with the same name. I did this by recursively calling a template on each immediately following sibling with the same name
<xsl:apply-templates select="following-sibling::*[1][local-name()=$name]" mode="next"/>
Putting this all together gives
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<!-- Match children of the block element -->
<xsl:template match="block/child::*">
<xsl:variable name="name" select="local-name()"/>
<!-- Is this the first element in a sequence? -->
<xsl:if test="local-name(preceding-sibling::*[position()=1]) != $name">
<xsl:copy>
<xsl:apply-templates />
<!-- Match the next sibling if it has the same name -->
<xsl:apply-templates select="following-sibling::*[1][local-name()=$name]" mode="next"/>
</xsl:copy>
</xsl:if>
</xsl:template>
<!-- Recursive template used to match the next sibling if it has the same name -->
<xsl:template match="block/child::*" mode="next">
<xsl:variable name="name" select="local-name()"/>
<xsl:apply-templates />
<xsl:apply-templates select="following-sibling::*[1][local-name()=$name]" mode="next"/>
</xsl:template>
<!-- Template used to copy a generic node -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Assuming you only have one block, Muenchian method is the most optimized way to do this:
<!-- group nodes by name -->
<xsl:key name="block-children-by-name" match="block/*" use="name()"/>
<!-- for nodes that aren't first in their group, no output -->
<xsl:template match="block/*" />
<!-- for nodes that are first in their group, combine group children and output -->
<xsl:template match="block/*[generate-id() =
generate-id(key('block-children-by-name', name())[1])]">
<xsl:copy>
<xsl:copy-of select="key('block-children-by-name', name())/*"/>
</xsl:copy>
</xsl:template>
Note that this only merges the child nodes, and not e.g. any attributes that may occur on aaa and bbb themselves.
Here's another approach, without using recursive templates.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="aaa">
<xsl:if test="not(preceding-sibling::*[1]/self::aaa)">
<xsl:variable name="following"
select="following-sibling::aaa[
not(preceding-sibling::*[
not(self::aaa) and
not(following-sibling::aaa = current())
])
]"/>
<xsl:copy>
<xsl:apply-templates select="$following/#*"/>
<xsl:apply-templates select="#*"/>
<xsl:apply-templates select="node()"/>
<xsl:apply-templates select="$following/node()"/>
</xsl:copy>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
The rather convoluted XPath expression for selecting the following sibling aaa nodes which are merged with the current one:
following-sibling::aaa[ # following 'aaa' siblings
not(preceding-sibling::*[ # if they are not preceded by
not(self::aaa) and # a non-'aaa' node
not(following-sibling::aaa = current()) # after the current node
])
]