XSLT 1.0 Copy attribute value as text and drop the attribute - xslt

I want to copy the attiribute value and move it as text of element and drop the attribute for that element. Note: it has to match the element name as i dont want to drop the attribute for other elements.
Input:
<a name = "attr" value = "text"/>
Expected
<a name = "attr"> text </a>

Suppose this is a sample XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<a name="attr" value="text" />
</root>
Then this stylesheet will result in the desired output:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="a">
<xsl:copy>
<xsl:apply-templates select="#*[local-name() != 'value']"/>
<xsl:value-of select="#value" />
</xsl:copy>
</xsl:template>
</xsl:transform>
Output:
<?xml version="1.0" encoding="UTF-8"?><root>
<a name="attr">text</a>
</root>
The default template just copies recursively. The second template with match <a> elements. It first applies templates for the attributes that are not value, then copies the text of attribute value. It must be done like this because if attribute value appears before other attributes, the opening tag would already be ended to start its text content, and by then other attributes can't be copied anymore.

Related

How to check contain only characters + space and `p` using regex

I want to check to contain only characters + space and <p> nodes inside <used>.
Input:
<root>
<used><p>String 1</p></used>
<used>string 2<p>string 3</p></used>
<used>string 4</used>
<used><image>aaa.jpg</image>para</used>
The output should be:
<ans>
<abc>string 1</abc>
<abc>string 4</abc>
</ans>
Tried code:
<ans>
<abc>
<xsl:template match="root">
<xsl:choose>
<xsl: when test="getCode/matches(text(),'^[a-zA-Z0-9]+$')">
<xsl:text>text()</xsl:text>
</xsl:when>
</xsl:choose>
</xsl:template>
</abc>
</ans>
My tried code is not working as I am expecting. How can I fix this? Thank you. I am using XSLT 2.0
You can use the following XSLT-2.0 stylesheet to get the desired result:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl= "http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<!-- Handle the <root> element -->
<xsl:template match="/root">
<ans>
<xsl:apply-templates select="used" />
</ans>
</xsl:template>
<!-- Create <abc> elements for every matching element -->
<xsl:template match="used[not(*) and matches(text(),'^[\sa-zA-Z0-9]+$')] | used[not(text()) and matches(p/text(),'^[\sa-zA-Z0-9]+$')]/p">
<abc><xsl:copy-of select="text()" /></abc>
</xsl:template>
<!-- Remove all spurious text nodes -->
<xsl:template match="text()" />
</xsl:stylesheet>
Its result is
<?xml version="1.0" encoding="UTF-8"?>
<ans>
<abc>String 1</abc>
<abc>string 4</abc>
</ans>

Select preceding elements until I find an element to stop on (XSLT 1.0)

I want to select the a elements that precede bird until I hit dog, but not select the bird or dog. And, I don't know what the elements are. They could be different than in the sample XML. And, I would like to do it in the select of a variable.
Input XML:
<?xml version="1.0" encoding="utf-8"?>
<root>
<a>cat</a>
<a>dog</a>
<a>dog</a>
<a>cat</a>
<a>snake</a>
<a>cat</a>
<a>cat</a>
<a>bird</a>
<a>dog</a>
<a>cat</a>
</root>
Desired Output XML:
<?xml version="1.0" encoding="utf-8"?>
<root>
<a>cat</a>
<a>snake</a>
<a>cat</a>
<a>cat</a>
</root>
XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="nodes">
<xsl:copy-of select="//a"/>
</xsl:variable>
<xsl:variable name="nodeList" select="msxsl:node-set($nodes)"/>
<!-- I want to select the a elements that precede bird until I hit dog, but not select the bird or dog.
And, I don't know what the elements are. They could be different than in the sample XML.
And, I want to do it in the select of the variable below.
-->
<xsl:variable name="subsetOfNodeList" select="$nodeList/a[.='bird']/preceding-sibling::a[. >> $nodeList/a[.='bird']/preceding-sibling::a[.='dog'][1]]"/>
<xsl:template match="root">
<xsl:copy>
<xsl:copy-of select="$subsetOfNodeList"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Given XSLT 1 I think you can select the preceding-siblings of the bird and intersect (remember intersection of node-sets n1 and n2 is done with $n1[count(. | $n2) = count($n2)]) them with anything following the dog, here is an example that additionally uses a key to identify what follows the dog:
<xsl:key name="fol" match="a[not(. = 'dog')]" use="generate-id(preceding-sibling::a[. = 'dog'][1])"/>
<xsl:template match="root">
<xsl:variable name="bird" select="a[. = 'bird']"/>
<xsl:variable name="prec-dog" select="$bird/preceding-sibling::a[. = 'dog'][1]"/>
<xsl:variable name="fol-dog" select="key('fol', generate-id($prec-dog))"/>
<xsl:variable name="prec-siblings" select="$bird/preceding-sibling::a[not(. = 'dog')]"/>
<xsl:variable name="intersect" select="$prec-siblings[count((. | $fol-dog)) = count($fol-dog)]"/>
<xsl:copy>
<xsl:copy-of select="$intersect"/>
</xsl:copy>
</xsl:template>
At https://xsltfiddle.liberty-development.net/eiQZDbr/2 for the input
<root>
<a id="c1">cat</a>
<a id="d1">dog</a>
<a id="d2">dog</a>
<a id="c3">cat</a>
<a id="s1">snake</a>
<a id="c4">cat</a>
<a id="c5">cat</a>
<a>bird</a>
<a>dog</a>
<a>cat</a>
</root>
I get the result
<root>
<a id="c3">cat</a>
<a id="s1">snake</a>
<a id="c4">cat</a>
<a id="c5">cat</a>
</root>
One of possible solutions is to make the "initial" call of a dedicated
template (let's call it print), passing it the last element to be
printed, i.e. the first preceding sibling of bird element (I assume
such a bird element is only one).
This template:
Checks whether the argument passed has content other than
dog (actually a condition to do anything). If this is the case then:
Make a recursive call to itself, passing it the first preceding
sibling of the argument element.
Print the element passed as the argument.
So the whole script can look like below:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template name="print">
<xsl:param name="xx"/>
<xsl:if test="$xx/text() != 'dog'">
<xsl:call-template name="print">
<xsl:with-param name="xx" select="$xx/preceding-sibling::*[1]"/>
</xsl:call-template>
<xsl:copy-of select="$xx"/>
</xsl:if>
</xsl:template>
<xsl:template match="root">
<xsl:copy>
<xsl:call-template name="print">
<xsl:with-param name="xx" select="a[text() = 'bird']/preceding-sibling::*[1]"/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
</xsl:transform>
For a working example see http://xsltfiddle.liberty-development.net/gWcDMet

xslt: move all siblings inside the first one

I've searched through similar questions, but couldn't make any of the suggestions to work. I have the following xml I need to modify it
<XDB>
<ROOT>
<KEY><ID>12345</ID><DATE>5/10/2011</DATE></KEY>
<PERSONAL><ID>1</ID><INFO><LASTNAME>Smith</LASTNAME>...</INFO></PERSONAL>
<CONTACT><ID>1</ID><EMAIL>asmith#yahoo.com</EMAIL>...</CONTACT>
</ROOT>
<ROOT>
<KEY><ID>98765</ID><DATE>5/10/2013</DATE></KEY>
<CONTACT><ID>2</ID><EMAIL>psmithton#yahoo.com</EMAIL>...</CONTACT>
</ROOT>
...
</XDB>
And it needs to look like this:
<XDB>
<ROOT>
<KEY><ID>12345</ID><DATE>5/10/2011</DATE>
<PERSONAL><ID>1</ID><INFO><LASTNAME>Smith</LASTNAME>...</INFO></PERSONAL>
<CONTACT><ID>1</ID><EMAIL>asmith#yahoo.com</EMAIL>...</CONTACT>
</KEY>
</ROOT>
<ROOT>
<KEY><ID>98765</ID><DATE>5/10/2013</DATE>
<CONTACT><ID>2</ID><EMAIL>psmithton#yahoo.com</EMAIL>...</CONTACT>
</KEY>
</ROOT>
...
</XDB>
I need to make 2...n siblings as children of the first 'key' sibling. Essentially, i need to remove the closing < /KEY> and put it before the closing < /ROOT>. I would appreciate your help.
Thanks.
Following xslt based on Identity transform could make this job
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<!-- Copy everything you find... -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<!-- ... but if you find first element inside ROOT ... -->
<xsl:template match="ROOT/node()[1]">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
<!-- ... copy its sibling into it ... -->
<xsl:copy-of select="following-sibling::*" />
</xsl:copy>
</xsl:template>
<!-- ignore other elements inside ROOT element since they are copied in template matching first element -->
<xsl:template match="ROOT/node()[position() > 1]" />
</xsl:stylesheet>

remove elements based on external file

I have an external setting file which has some nodes holiding attribute values of main xml document. I need to remove certian nodes from mian xml file if the attribute value is there in the setting file.
My setting file looks like this:
setting.xml
<xml>
<removenode titlename="abc" subtitlename="xyz"></removenode>
<removenode titlename="dvd" subtitlename="dvd"></removenode>
</xml>
Main.xml
<xml>
<title titlename="abc">
<subtitle subtitlename="xyz"></subtitle>
</title>
<title titlename="book">
<subtitle subtitlename="book sub title"></subtitle>
</title>
</xml>
Need a script which look for setting.xml file and remove the title element if titlename and subtitlename found in main.xml. The output should be
output.xml
<xml>
<title titlename="book">
<subtitle subtitlename="book sub title"></subtitle>
</title>
</xml>
I tried using document to read setting.xml file but not able to find how to do the match on main.xml file
<xsl:variable name="SuppressionSettings" select="document('Setting.xml')" />
<xsl:variable name="SuppressSetting" select="$SuppressionSettings/xml/removenode" />
.
Any hint how to implement it?
The key is to use an identity/copy pattern and, before each output, check the current (context) node isn't prohibited by the suppression rules nodeset.
<!-- get suppression settings -->
<xsl:variable name='suppression_settings' select="document('http://www.mitya.co.uk/xmlp/settings.xml')/xml/removenode" />
<!-- begin identity/copy -->
<xsl:template match="node()|#*">
<xsl:if test='not($suppression_settings[#titlename = current()/#titlename and #subtitlename = current()/subtitle/#subtitlename])'>
<xsl:copy>
<xsl:apply-templates select='node()|#*' />
</xsl:copy>
</xsl:if>
</xsl:template>
You can run it here (see output source - the 'abc' title node is omitted):
http://www.xmlplayground.com/9oCYKp
This XSLT indicated below works for the given document.
Note that I'm storing the contents of Setting.xml in a variable as you did, however, I'd then use that variable directly in my queries.
An important issue here is that in the match element of a template, variables cannot be used. Therefore, my template matches any <title> elements and then determines in an <xsl:choose> element whether the attributes match any values given in the settings file - if so, the <title> element will be omitted in the output.
As an explanation for why that test attribute in the <xsl:when> does what it should, imagine a comparison of someAttribute = someOtherAttribute not as a restriction that the attribute someAttribute must have the same value as the attribute someOtherAttribute, but rather as the condition that there must be any two attributes someAttribute and someOtherAttribute with the same value.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="SuppressionSettings" select="document('Setting.xml')" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//title">
<xsl:choose>
<xsl:when test="(#titlename = $SuppressionSettings/xml/removenode/#titlename) and (subtitle/#subtitlename = $SuppressionSettings/xml/removenode/#subtitlename)"/>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Here's a more generic answer where the names of the attributes are not hard coded into the XSLT. Like O. R. Mapper pointed out, in XSLT 1.0 you can't use variable references in the match, so I put the document() directly in the predicate. This may not be as efficient as using a variable and then testing the variable.
XSLT 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[#* = document('setting.xml')/*/removenode/#*]"/>
</xsl:stylesheet>
XML Output (using your 2 xml files with main.xml as the input)
<xml>
<title titlename="book">
<subtitle subtitlename="book sub title"/>
</title>
</xml>

Add attribute to tag with XSLT

I have a few svg documents with a 1-n Path elements now i wanted to change the color of those path elements.
i have haven't found a way to do this
Svg example document:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" height="45" width="45" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
<g transform="matrix(1.25,0,0,-1.25,0,45)">
<path d="m9 18h18v-3h-18v3"/>
</g>
</svg>
XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' version='1.0'>
<xsl:template match='path'>
<xsl:copy>
<xsl:attribute name='fill'>red</xsl:attribute>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
What do i need to change to make it add/change the fill attribute to red?
I think you misunderstand how XSLT works. It takes an input XML tree and produces a new tree by interpreting your stylesheet. In other words, your stylesheet defines how a completely new tree is produced from scratch, based on the input XML tree.
It's important to understand that you are not modifying the original XML tree. It's like a difference between a purely functional and imperative language. Bottom line: you can't change the fill attribute to red, you can produce a copy of your original document where the fill attribute is set to red.
That said, this is more or less how you would do it:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:svg="http://www.w3.org/2000/svg" version='1.0'>
<!-- this template is applied by default to all nodes and attributes -->
<xsl:template match="#*|node()">
<!-- just copy all my attributes and child nodes, except if there's a better template for some of them -->
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- this template is applied to an existing fill attribute -->
<xsl:template match="svg:path/#fill">
<!-- produce a fill attribute with content "red" -->
<xsl:attribute name="fill">red</xsl:attribute>
</xsl:template>
<!-- this template is applied to a path node that doesn't have a fill attribute -->
<xsl:template match="svg:path[not(#fill)]">
<!-- copy me and my attributes and my subnodes, applying templates as necessary, and add a fill attribute set to red -->
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:attribute name="fill">red</xsl:attribute>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>