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
Related
I have requirements to copy whole node including schemaversion and namespaces. (Below is part of XML)
<ns0:Body>
<std:test com:schemaVersion = "4.1" xmlns:com = "http://www.test.com/DI/D2P/Broker/Schemas/CommonTypes" xmlns:std = "http://www.test1.com/DI/D2P/Broker/Schemas/test" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance">
<com:SystemMessageHeader>
...
...
I am using below xslt to do this task. (i have mentioned only needed portion of XSLT.) With this XSLT, it does copy of whole node including namespaces but it does not copy com:schemaVersion = "4.1". Please help me how can i fix it ?
<nos:Body>
<xsl:for-each select ="nos:GRRCall/nos:Response/ns0:Body/*">
<xsl:copy>
<xsl:variable name="v1" select="position()"/>
<xsl:variable name="v2" select="count(../../../../*)"/>
<xsl:apply-templates />
</xsl:copy>
</xsl:for-each>
</nos:Body>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:apply-templates select="com:SystemMessageHeader"/>
</xsl:copy>
</xsl:template>
When you perform an apply-templates without specifying a select expression -- as in your stylesheet fragment -- the attributes of the context node are not selected for transformation. That's why the canonical identity transform explicitly selects attributes:
<!-- Identity transform -->
<xsl:template match="node()|#*">
<xsl:copy>
<!-- HERE: -->
<xsl:apply-templates select="node()|#* />
</xsl:copy>
</xsl:template>
Your first alternative, then, would be to follow the model of the identity transform when you apply templates to the children of <ns0:Body> elements.
Your second alternative would be to explicitly transform the wanted attribute by adding ...
<xsl:apply-templates select="#com:schemaVersion" />
... which will not affect other attributes, and will do nothing if the source element has no such attribute.
A third alternative would be to ensure that the transformed elements have the wanted attribute, even if the source elements didn't, by inserting code to create one into the template:
<xsl:attribute name="com:schemaVersion">
<xsl:value-of select="#com:schemaVersion" />
</xsl:attribute>
That particular version will provide an empty string as the attribute value when the source node has no such attribute; you could modify it to provide a default value instead if desired.
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>
The identity template looks like this:
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
Does <xsl:apply-templates select="#*|node()" /> select more than <xsl:apply-templates />, or could the identity template have been like this?
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
What exactly is selected when I do the following?
<xsl:apply-templates />
Does <xsl:apply-templates select="#*|node()" /> select more than
<xsl:apply-templates />, or could the identity template have been
like this?
<xsl:apply-templates/>
is equivalent to:
<xsl:apply-templates select="node()"/>
and this is a shorter former of:
<xsl:apply-templates select="child::node()"/>
and this is a equivalent to:
<xsl:apply-templates select="* | text() | comment() | processing-instruction()"/>
As we see from the last instruction, the xsl:apply-templates instruction you are asking about, doesn't select any attributes, therefore it cannot be used as a shorthand for:
<xsl:apply-templates select="#*|node()"/>
The default select for <xsl:apply-templates/> is just "node()", it doesn't include attributes.
The default selection of apply-templates is node(), which is shorthand for child::node(). This XPath expressions is evaluated as follows:
First, all nodes from the "child" axis are taken. This are all the direct children of the current element, i.e. other elements, text, and comments, but not the attributes.
Then this node set is filtered with the node test "node()". In this case, no elements are filtered because that test matches everything.
So with <xsl:apply-templates />, templates for the child elements are applied but not for the attributes. In case of the copy template this would mean that the attributes are not copied.
Question for Copy
input:
<Rel>
<IRel UID1="3a4d1d2909d0" UID2="35fe61082294" DefUID="AssetSupplier" />
<IObject UID="3a4d1d2909d0.AssetSupplier.35fe61082294" />
<SPXSupplier>
<ISPFOrganization />
<ISPFAdminItem />
<IObject UID="b73ebb87-cd36-4c25-b9ed-35fe61082294"
Description="local supplier made in form (10C)"
Name="CASTROL1200" />
<ISupplierOrganization />
</SPXSupplier>
</Rel>
Output:
I only want to skip SPXSupplier and its child node in my output
<Rel>
<IRel UID1="3a4d1d2909d0" UID2="35fe61082294" DefUID="AssetSupplier" />
<IObject UID="3a4d1d2909d0.AssetSupplier.35fe61082294" />
</Rel>
currently I am using this copy which copy all the things including the child,
<xsl:copy-of select="self::node()"/>
I only want <Rel>, <IRel> and <IObject> tags. excluding other stuff.
Here's a refinement of Alex's answer.
<xsl:template match="SPXSupplier"/>
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
The empty template for SPXSupplier means that when you hit one of these elements, the subtree below that element is not processed. I've also used a version of the identity template that copies attributes unconditionally, which is more efficient.
xsl:copy-of copies the whole subree. To exclude an SPXSupplier element you can use the following approach:
<xsl:template match="//Rel">
<xsl:copy>
<xsl:apply-templates select="#*|IRel|IObject"/>
</xsl:copy>
</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
])
]