XSLT copy, copying content
1. | value-of, copy and copy-of. What's the difference? |
xsl:value-of always returns the _string_ value of the node supplied in its select attribute. (ie, teh concatenation of all the characers in its descendants). If you supply a node set of more than one node, all but the first node in document order will be discarded. xsl:copy-of does a "deep copy" of its input, you can supply a value, or a set of nodes, in its select attribute and all teh nodes/values will be copied. xsl:copy does a "shallow copy" it doesn't have a select attribute, and for nodes without children (atributes/comments/processing instructions) it does the same as copy-of, but for element nodes it just generates an element node with the same name, without copying all the child nodes. Typically you then use xsl:apply-templaes to generate some new content that isn't a copy. so if <x> <a>asd <b>ljh g</b> jhtg </a> <y>knv <b>asjfgq</b> oiacb </y> </x> and the current node is x then <xsl:value-of select="a|y"/> is "asd ljh g jhtg " (characters in a, y is discarded) <xsl:copy-of select="a|y"/> is <a>asd <b>ljh g</b> jhtg </a> <y>knv <b>asjfgq</b> oiacb </y> copy of those two elements <xsl:copy> generates a (currently empty) node <x> ... </x> in the result tree. | |
2. | How can I copy all tag with attributes |
See the XSLT spec, section 7.5 for the "identity transformation".identity <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> | |
3. | Output all children of a given element |
Q expansion. > In my XML document, I have the following: > <runmode name="debug"> > <any_element> > <sub_element/> > <sub_element/> > </any_element> > </runmode> > > I would like my XSL stylesheet to output the entire text of > the runmode > element, so it may look like the following: > <xsl:template match="runmode[@name='debug']"> > start: > <!-- This is where I would like to output the text > elements --> > end: > </xsl:template> > > and my output document would contain: > > start: > <any_element> > <sub_element/> > <sub_element/> > </any_element> > end: Try: <xsl:template match="runmode[@name='debug']"> start: <xsl:copy-of select="node()"/> end: </xsl:template> The description of xsl:copy-of is notoriously misplaced in the spec, I think it was originally designed to do a much more specialised job and then generalised later, so it's not surprising you failed to spot it. | |
4. | Copying tags with attributes |
> <form name="queryForm" action="test.xml" method="post" > target="results"> > <tag1/> > <tag2/> > </form> > > is there is easy way to preserve the <form> tag with > attributes but process all tags inside it? You didn't say what XSL processor you are using. If it is one that implements recent versions of the spec, this should work: <!-- template for handling source tree elements named 'form' --> <xsl:template match="form"> <!-- put a form element in the result tree --> <form> <!-- add the attributes --> <xsl:for-each select="@*"> <xsl:attribute name="name()"><xsl:value-of select="."/></xsl:attribute> </xsl:for-each> <!-- process the children of form element in source tree --> <xsl:apply-templates/> </form> </xsl:template> Ken Holman adds The following is a short way of saying "copy the element and all attributes for a bunch of element types named "form", "this", "that" and "other": <xsl:template match="form|this|that|other"> <xsl:copy><!--copy node being visited--> <xsl:copy-of select="@*"/><!--copy of all attributes--> <xsl:apply-templates/><!--process the children--> </xsl:copy> </xsl:template> | |
5. | Copy source to output with minor changes |
>can someone give me a minimal script that will take a file of >XML and replicate it unchanged as output. i guess you need a template to >override each default rule for how each input element is handled (including >comments, processing instructions etc.) Not quite: you can override all default rules together. An identity transform is given in the XSL spec, section 7.5: <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> >secondly, can you modify this to show a test for the presence of a named >child element which inserts it if it is not present Rather than modify it, supplement it with a template specifically for that element where you want the test and possible new child: <xsl:template match="yourelement"> <xsl:copy> <xsl:apply-templates select="@*"/> <xsl:if test="not(child)"> <child><!-- whatever you like --></child> </xsl:if> <xsl:apply-templates select="node()"/> </xsl:copy> </xsl:template> >and finally, can you modify this to show testing the presence of an >attribute and inserting a default if it is not present <xsl:template match="yourelement"> <xsl:copy> <xsl:if test="not(@yourattribute)"> <xsl:attribute name="yourattribute"> <xsl:text>yourdefaultvalue</xsl:text> </xsl:attribute> </xsl:if> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> Note that I haven't tested this stuff, but I think it'll do what you want. | |
6. | How to copy elements with XSLT |
> The problem is that I do not know how apply templates to my elements > only and copy XHTML tags into result file without changes. Write a default template that copies elements: <xsl:template match="*"> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:apply-templates/> </xsl:copy> </xsl:template> And then specific template rules for your own elements that do something different. | |
7. | How to copy HTML elements |
Here is the basics of what I do: <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"> <xsl:template match="/"> <xsl:apply-templates/> </xsl:template> <xsl:template match=" a | applet | b | big | body | br | caption | cite | code | col | colgroup | dd | div | dl | dt | em | font | form | frame | frameset | head | h1 | h2 | h3 | h4 | h5 | h6 | hr | html | i | iframe | img | link | li | map | meta | noframes | ol | p | param | pre | s | script | small | span | strong | style | sub | sup | td | th | title | tr | tt | ul | var | table "> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:apply-templates/> </xsl:copy> </xsl:template> <!-- ADD YOUR template here --> </xsl:output> </xsl:stylesheet> While this doesn't look too elegant, it copies the html and it does something else with the other stuff. | |
8. | Copying node but changing attributes |
The only thing to do is fill in <!-- DO SOMETHING WITH ATTR --> (and take out xsl:copy) For example <xsl:template match="node"> <xsl:copy> <xsl:copy-of"@*[not(name()='x' or name()='y')]"> <xsl:attribute name="xxx"> <xsl:value-of select="@y"/> </xsl:attribute> <xsl:attribute name="yyy"> <!-- DO SOMETHING WITH ATTR --> </xsl:attribute> <xsl:apply-templates/> </xsl:copy> </xsl:template> copies any attribute not called x or y, makes a new attribute xxx with the data from the old y attribute, makes a new attribute yyy and doesn't use the old x attribute at all. or perhaps <xsl:template match="node"> <xsl:copy> <xsl:for-each select="@*"> <xsl:attribute name="new-{name()}"> <xsl:text>modified from </xsl:text> <xsl:value-of select="."/> </xsl:attribute> </xsl:for-each> <xsl:apply-templates/> </xsl:copy> </xsl:template> which changes <node a="1" b="2"> to <node new-a="modified from 1" new-b="modified from 2"> | |
9. | Ommitting value of a copied attribute |
I want to copy a node with all it's attributes and the values of the attributes except the id-attribute. I want also copy the id-attribute, but ommit the value of it. <xsl:template match="element"> <xsl:copy> <xsl:copy-of select="@*[not(name()='attr')]"/> <xsl:attribute name="attr"> <.... whatever you want ....> </xsl:attribute> <.... whatever you want ....> </xsl:copy> </xsl:template> Mike Kay offers Several solutions were offered recently, The one I liked best was: <xsl:copy> <xsl:variable name="excl" select="generate-id(@id)"/> <xsl:copy-of select="@*[generate-id()!=$excl]" <xsl:attribute name="id">... new value ...</xsl:attribute> </xsl:copy> | |
10. | How to copy all child elements except specific one |
> I assume > > <xsl:copy-of select="" /> > > will do what I need assuming I can come up with the correct select > expression. How do I build an expression that will choose all child elements > except for a specific one? that's an alternative to the method I suggested. (ie change the template of the parent rather than the element you are trying to change. <xsl:copy-of select="*[not(self::thisElement)]"/> does what you ask, but this method only works if you don't mind changing the order of your child nodes. This will process all the "other" elements together. If you have the "specific" element you want to change interspersed with other elements, and you want the generated fragments to go into the same place using the identity transform method I suggested earlier is probably simpler. Dimitre Novatchev adds Use: <xsl:copy-of select="*[not(count(. | $thisChild) = 1)]" /> This can be generalised so that if $thisChild is a node-set containing a subset of children, then this subset will be excluded from copying: <xsl:copy-of select="*[not(count(. | $theseChildren) = count($theseChildren) )]" /> In fact, this is an almost obvious application of the general XPath expression for set difference (ns1 \ ns2): ns1[not(count(. | ns2) = count(ns2))] Jeni answsers a follow on question re attributes > I tried the same for attributes, but this doesn't work - it copies all > attributes: > > <xsl:copy-of select="@*[not(attribute::DoNotCopyAttribute)]" /> In this XPath, you are saying: get a list of all the attributes on the current node, and filter it to include only those that do not have an attribute called DoNotCopyAttribute Now, attributes can't have attributes, so the predicate always returns true, and you get all the attributes. The self:: axis that you use to test the name of elements doesn't work because self::* only matches elements, so you also can't do: <xsl:copy-of select="@*[not(self::DoNotCopyAttribute)]" /> This will say: get a list of all the attributes on the current node, and filter it to include only those that are not themselves DoNotCopyAttribute elements Attributes can never be elements, so the predicate always returns true, and you get all the attributes. The best way around it that I've seen is to locate the relevant attribute and test whether the current attribute is the same node. If it is, then you don't want it; if it isn't, then you do. So: <xsl:copy-of select="@*[generate-id(.) != generate-id(../@DoNotCopyAttribute)]" /> or: <xsl:copy-of select="@*[count(.|../@DoNotCopyAttribute) != count(../@DoNotCopyAttribute)]" /> whichever you find easiest to read or is quicker with your processor. She later adds: Actually, the vast majority of attributes are in the null namespace because they don't have prefixes. If you know that the attribute you want to filter out is unprefixed, then you can use local-name() and test not(namespace-uri()): <xsl:copy-of select="attribute::*[not(local-name()='DoNotCopyAttribute' and not(namespace-uri())]" /> Or you can use name() directly: <xsl:copy-of select="attribute::*[name() != 'DoNotCopyAttribute']" /> It's just that this is a bad habit to get into because when you come up against prefixed attributes, like xlink:href, which *are* in a particular namespace, then using the same pattern can cause Bad Things to happen. Namely, if I write this in my stylesheet: <xsl:copy-of select="attribute::*[name() != 'xlink:href']" /> and then someone writes their XML source with 'xl' as a prefix instead: xl:href="..." Then because 'xl:href' isn't the same as 'xlink:href', the xl:href attribute isn't filtered out. Using local-name() and namespace-uri() gets around that but writing out the URI for a namespace is long and tedious (I wish the XML Schema writers recognised this!) so it's easier to use the prefix that you've defined in your stylesheet to make the match, hence the generate-id() and Kaysian intersection samples. | |
11. | copy-of question, push or pull? |
> The default rules gives the unmatched elements back as > unformattet text I guess. Could you give me an example to extract > only an element out of a huge xml? Well, you can either approach it from a pull direction or a push direction. If you want to pull, then you should find an XPath that identifies the single node that you want to copy from the root node of the tree, perhaps through id() or by stepping down to it. I don't know what your XML looks like, unfortunately, so I can only say that: //TABLE will work to point at the TABLE element in your XML. There may be a much better path - this one is far from optimal because it uses the descendant-or-self:: axis, which is very inefficient. Then you should have a single template that operates very near the top of the tree (often the root node), and that copies only this one node: <xsl:template match="/"> <xsl:copy-of select="//TABLE" /> </xsl:template> The push method would involve changing the default templates. To get no output by default, you only need to change the template that matches text() nodes, making it do nothing rather than giving the value of the text. So just add the template: <xsl:template match="text()" /> to your current stylesheet. Which one is most efficient depends on the structure of your XML, the processor you're using and whether you can come up with a better XPath to access the node you're interested in. Generally I'd go for pull in this situation. | |
12. | Correct way to handle nested tags |
<xsl:template match="text"> <xsl:copy-of select="node()"/> </xsl:template> | |
13. | Copying |
xsl:copy-of select="@*" is the standard way of copying all attributes. However You can simplify <xsl:template match="PERSNAME"> to either <xsl:template match="PERSNAME"> or <xsl:template match="PERSNAME"> and you can simplify <xsl:copy-of select="@*"/> to <xsl:copy-of select="@*|node()"/> actually if that was exactly what you wanted, you are just copying an entire element, so you could further simplify to <xsl:template match="PERSNAME"> | |
14. | Change namespace with copy-of? |
No. xsl:copy-of can only be used to create an exact copy. If you want to change anything, you need to process each node individually using a template rule ("or otherwise", as they say in maths exams). In XSLT 2.0 there is an <xsl:namespace> instruction designed to fill this gap. In XSLT 1.0 the usual circumvention is to create an RTF containing the required namespace node, and then copy it (which requires xx:node-set): <xsl:variable name="dummy"> <xsl:element name="{$prefix}:xxx" namespace="{$uri}"/> </xsl:variable> ... <xsl:copy-of select="xx:node-set($dummy)/*/namespace::*[name()=$prefix]"/> Copying of namespace nodes is defined by an erratum to XSLT 1.0. | |
15. | copy only elements with content |
Whenever you want to copy-with-amendments, your best starting point is the identity template, which performs a deep copy by recursing down the tree: <xsl:template match="node() | @*"> <xsl:copy> <xsl:apply-templates select="@* | node()" /> </xsl:copy> </xsl:template> Then you can add templates that match the things that you want to remove from the tree and do nothing with them. In your case, a template that matches the empty elements: <xsl:template match="*[not(node())]" /> Note that "empty" means different things to different people. The above will filter out elements that are truly empty, but won't filter out elements that contain only comments, processing-instructions or whitespace-only text. It also won't filter out elements that only contain other elements that are empty. So you might want: <xsl:template match="*[not(normalize-space())]" /> instead, which will filter out all elements that don't contain any textual content. | |
16. | value-of or copy-of? |
And ... today's little XPath lesson...: [0] "." is short for "self::node()" [1a] select="node()" is short for select="child::node()" (select all child nodes of the context node) [1b] select="./node()" is short for select="self::node()/child::node()" (select all child nodes of the context node itself) These are functionally equivalent, which makes this case very different from another apparently similar pairing: [2a] select="//node()" is short for select="/descendant-or-self::node()/child::node()" [2b] select=".//node()" is short for select="self::node()/descendant-or-self::node()/child::node()" That is, [2a] is the same as "/descendant::node()" (node descendants of the root), while [2b] is the same as "self::node()/descendant::node()" (node descendants of the context node) -- not the same thing at all. Back to the thread ... if you want to copy the current node (with all descendants), not just its children (with theirs), that's <xsl:copy-of select="."/> (expands as in [0]) ... or perhaps you really want to copy the node's children (with all descendants) but not the node itself: you can do this with copy-of select="node()" [1a]. I say "with all descendants" because that's what copy-of does. For the OP, that's how the <a> node in the source gets included when its parent's template says to copy node children. | |
17. | copy, with an exclusion |
You can write @*[name() != 'foo']. In XSLT 2.0 you can write (@* except @foo). But a better way is perhaps to declare a template rule for @foo that does nothing: <xsl:template match="@foo" priority="3"/> | |
18. | Recording the changes made |
This is really like a table of contents, so the canonical way of proceeding is to process the document twice, with two modes so <xsl:template match="/"> <xsl:apply-templates/> <xsl:result-document ....> <xsl:apply-templates mode="log"/> </xsl:result document> </xsl:template> You then just duplicate every <xsl:template match="something"> that's doing something otherthan an identity and have <xsl:template match="something" mode=log"> I did something </xsl:template> If reprocessing the document twice and duplicating the templates doesn't feel right then an alternative plan (which isn't really following the one-true-path to functional programming purity is to stick an <xsl:message>I did something </xsl:message> into any template that does anything, then use your command line or API to redirect the xsl:message output to your log file. Note however that the first way is guaranteed to produce the log in a logical order, the second way will most likely produce the log in the order that the system actually evaluated the templates, which if you have some (perhaps mythical) highly parralelised and optimised xslt engine mightbe in any order at all. | |
19. | Copy, remove duplicates |
The variable-based grouping method can be used in XSLT 1.0 across multiple documents. Input files: input.xml <elements> <a/> <b/> <link location="extern1.xml"/> <c/> <a/> <link location="extern2.xml"/> <b/> <e/> </elements> extern1.xml <elements> <a/> <f/> </elements> extern2.xml <elements> <g/> <f/> </elements> Stylesheet henning.xsl <?xml version="1.0" encoding="iso-8859-1"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:output indent="yes"/> <xsl:template match="/"> <elements> <!--collect all items to be sorted into a variable--> <xsl:variable name="items" select="/elements/*[not(self::link)] | document(/elements/link/@location)/elements/*"/> <!--loop through all, acting only on the first of each unique value--> <xsl:for-each select="$items"> <xsl:if test="generate-id(.)= generate-id($items[name(.)=name(current())])"> <xsl:copy-of select="."/> </xsl:if> </xsl:for-each> </elements> </xsl:template> </xsl:stylesheet> And the output <?xml version="1.0" encoding="utf-8"?> <elements> <a/> <b/> <c/> <e/> <f/> <g/> </elements> | |
20. | Copy input XML to escaped output XML |
Below is a version of verbid.xsl that demonstrates the use of a repeatable-id feature. By this I mean repeatable across xslt processors and across runs, assuming the same xsl source. It may be over-engineered for schematron in that it handles things like attributes, comments, processing instructions and text which are not normally the context of schematron rules. It may be under-engineered as a general xpath function in that I coded but haven't yet tested the support for namespace nodes. <!-- Copyright 1999-2008 David Carlisle NAG Ltd davidc@nag.co.uk Free use granted under GPL or MPL. Render XML in an HTML pre element. USAGE ===== include this stylesheet into your main stylesheet via <xsl:import href="verb.xsl"/> Then a typical usage might be to render an <example> element twice, first as literal XML, then secondly styled as normal. One would use, in your main stylesheet: <xsl:template match="example"> <pre> <xsl:apply-templates mode="verb"/> </pre> <xsl:apply-templates/> </xsl:template> This would put the contents of the example element (but not `<example>') in the HTML pre element. If you want the verbatim mode to include the current, `example' element, modify the above to say <xsl:apply-templates mode="verb" select="."/> --> <!-- verb mode --> <!-- Does not really give verbatim copy of the file as that information not present in the parsed document, but should give something that renders in HTML as a well formed XML document that would parse to give same XML tree as the original --> <!-- non empty elements and other nodes. --> <?xml version='1.0'?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template mode="verb" match="*[*]|*[text()]|*[comment()]|*[processing-instruction()]"> <xsl:variable name="repeatable-id"> <xsl:apply-templates mode="schematron-get-full-path" select="."/> </xsl:variable> <a name="{$repeatable-id}"/> <xsl:value-of select="concat('<',name(.))"/> <xsl:apply-templates mode="verb" select="@*"/> <xsl:text>></xsl:text> <xsl:apply-templates mode="verb"/> <xsl:value-of select="concat('</',name(.),'>')"/> </xsl:template> <!-- empty elements --> <xsl:template mode="verb" match="*"> <xsl:variable name="repeatable-id"> <xsl:apply-templates mode="schematron-get-full-path" select="."/> </xsl:variable> <a name="{$repeatable-id}"/> <xsl:value-of select="concat('<',name(.))"/> <xsl:apply-templates mode="verb" select="@*"/> <xsl:text>/></xsl:text> </xsl:template> <!-- attributes Output always surrounds attribute value by " so we need to make sure no literal " appear in the value --> <xsl:template mode="verb" match="@*"> <xsl:variable name="repeatable-id"> <xsl:apply-templates mode="schematron-get-full-path" select="."/> </xsl:variable> <a name="{$repeatable-id}"/> <xsl:value-of select="concat(' ',name(.),'=')"/> <xsl:text>"</xsl:text> <xsl:call-template name="string-replace"> <xsl:with-param name="from" select="'"'"/> <xsl:with-param name="to" select="'&quot;'"/> <xsl:with-param name="string" select="."/> </xsl:call-template> <xsl:text>"</xsl:text> </xsl:template> <!-- pis --> <xsl:template mode="verb" match="processing-instruction()"> <xsl:variable name="repeatable-id"> <xsl:apply-templates mode="schematron-get-full-path" select="."/> </xsl:variable> <a name="{$repeatable-id}"/> <xsl:value-of select="concat('<?',name(.),' ',.,'?>')"/> </xsl:template> <!-- only works if parser passes on comment nodes --> <xsl:template mode="verb" match="comment()"> <xsl:variable name="repeatable-id"> <xsl:apply-templates mode="schematron-get-full-path" select="."/> </xsl:variable> <a name="{$repeatable-id}"/> <xsl:value-of select="concat('<!--',.,'-->')"/> </xsl:template> <!-- text elements need to replace & and < by entity references do > as well, just for balance --> <xsl:template mode="verb" match="text()"> <xsl:variable name="repeatable-id"> <xsl:apply-templates mode="schematron-get-full-path" select="."/> </xsl:variable> <a name="{$repeatable-id}"/> <xsl:call-template name="string-replace"> <xsl:with-param name="to" select="'&gt;'"/> <xsl:with-param name="from" select="'>'"/> <xsl:with-param name="string"> <xsl:call-template name="string-replace"> <xsl:with-param name="to" select="'&lt;'"/> <xsl:with-param name="from" select="'<'"/> <xsl:with-param name="string"> <xsl:call-template name="string-replace"> <xsl:with-param name="to" select="'&amp;'"/> <xsl:with-param name="from" select="'&'"/> <xsl:with-param name="string" select="."/> </xsl:call-template> </xsl:with-param> </xsl:call-template> </xsl:with-param> </xsl:call-template> </xsl:template> <!-- end verb mode --> <!-- replace all occurences of the character(s) `from' by the string `to' in the string `string'.--> <xsl:template name="string-replace"> <xsl:param name="string"/> <xsl:param name="from"/> <xsl:param name="to"/> <xsl:choose> <xsl:when test="contains($string,$from)"> <xsl:value-of select="substring-before($string,$from)"/> <xsl:value-of select="$to"/> <xsl:call-template name="string-replace"> <xsl:with-param name="string" select="substring-after($string,$from)"/> <xsl:with-param name="from" select="$from"/> <xsl:with-param name="to" select="$to"/> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="$string"/> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- repeatable-id maker --> <xsl:template match="/" mode="schematron-get-full-path"/> <xsl:template match="text()" mode="schematron-get-full-path"> <xsl:apply-templates select="parent::*" mode="schematron-get-full-path"/> <xsl:value-of select="concat('+text()[', 1+ count(preceding-sibling::text()), ']')"/> </xsl:template> <xsl:template match="comment()" mode="schematron-get-full-path"> <xsl:apply-templates select="parent::*" mode="schematron-get-full-path"/> <xsl:value-of select="concat('+comment()[', 1+ count(preceding-sibling::comment()), ']')"/> </xsl:template> <xsl:template match="processing-instruction()" mode="schematron-get-full-path"> <xsl:apply-templates select="parent::*" mode="schematron-get-full-path"/> <xsl:value-of select="concat('+processing-instruction()[', 1+ count(preceding-sibling::processing-instruction()), ']')"/> </xsl:template> <xsl:template match="@*" mode="schematron-get-full-path"> <xsl:apply-templates select="parent::*" mode="schematron-get-full-path"/> <xsl:value-of select="concat('+@', name())"/> </xsl:template> <xsl:template match="*" mode="schematron-get-full-path" priority="-0.5"> <xsl:apply-templates select="parent::*" mode="schematron-get-full-path"/> <xsl:text>+</xsl:text> <xsl:choose> <xsl:when test="count(. | ../namespace::*) = count(../namespace::*)"> <xsl:value-of select="concat('+namespace::[',1+count(namespace::*),']')"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="concat('+',name(),'[',1+ count(preceding-sibling::*[name()=name(current())]),']')"/> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet> |