Convert all node's attributes into child nodes
Tested on Oxygen/XML using Saxon6.5:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@*">
<xsl:element name="{name()}"><xsl:value-of select="."/></xsl:element>
</xsl:template>
</xsl:stylesheet>
This is based on using an identity template for element nodes and a template that converts attributes to elements.
Merge all values of a node's child nodes
Re your Update 2
The given input:
XML
<p class="s3">
<span class="s4">
<span>Read</span>
</span>
<span class="s4">
<span> </span>
</span>
<span class="s4">
<span>the</span>
</span>
<span class="s4">
<span> information </span>
</span>
<span class="s4">
<span>sheet</span>
</span>
<span class="s4">
<span> </span>
</span>
<span class="s4">
<span>carefully</span>
</span>
</p>
poses conflicting requirements:
on the one hand, you want to strip the whitespace nodes surrounding
the innerspan
elements;on the other hand, you want to preserve the spaces contained within the inner
span
elements.
Since both the inner and the outer elements are named the same, this is quite impossible.
What you could do is pre-process the input XML using the following stylesheet:
1st XSLT 1.0
<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"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="span[not(@class)]">
<new-span>
<xsl:apply-templates select="@*|node()"/>
</new-span>
</xsl:template>
</xsl:stylesheet>
to produce:
2nd XML
<p class="s3">
<span class="s4">
<new-span>Read</new-span>
</span>
<span class="s4">
<new-span> </new-span>
</span>
<span class="s4">
<new-span>the</new-span>
</span>
<span class="s4">
<new-span> information </new-span>
</span>
<span class="s4">
<new-span>sheet</new-span>
</span>
<span class="s4">
<new-span> </new-span>
</span>
<span class="s4">
<new-span>carefully</new-span>
</span>
</p>
Then process the result with the next stylesheet:
2nd XSLT 1.0
<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"/>
<xsl:strip-space elements="*"/>
<xsl:preserve-space elements="new-span"/>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="p">
<xsl:copy>
<xsl:value-of select='.'/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
to produce the final result:
<?xml version="1.0" encoding="UTF-8"?>
<p>Read the information sheet carefully</p>
Transform all child siblings into parent-like node
I would suggest to approach it like this:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="line[*]">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="line/*">
<line>
<xsl:copy-of select="../@* | @*"/>
</line>
</xsl:template>
</xsl:stylesheet>
Converting a single XML attribute of element to child element
It might suffice to first process the attributes you want to copy as attributes with one apply-templates (or simply use copy-of for them) and to then apply-templates to the id attribute and any child nodes:
<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="@*[not(name()='id')]"/>
<xsl:apply-templates select="@id | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@id">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/bEzkntb/7
In XSLT, how to copy all attributes from all child nodes to root node of xml?
You could do quite simply:
XSLT 1.0
<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"/>
<xsl:template match="/root">
<xsl:copy>
<xsl:copy-of select="//@*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
However, an element cannot have two attributes with the same name. If your XML has more than one instance of the same attribute, they will overwrite each other and only the last one will be present in the output.
How to add attribute to all particular children of a node
First, let me point out that there is a difference in how this is done for just in-memory use versus updating the content of the database. For the latter, you could do:
for $add in $test//add
return
xdmp:node-insert-child(
$add,
attribute atta1 { 1 }
)
To change it in memory, which is what functx does, you'll be making a copy of the original, making changes in the copy as you build it. This is called recursive descent and is a pretty common pattern. I wrote a blog post a while ago that shows how to implement recursive descent, but essentially you'll do a typeswitch that, when it encounters an "add" element, creates the new attribute. You can use the functx function for that. Something along these lines (untested):
declare function local:change($node)
{
typeswitch($node)
case element(add) return
functx:add-attributes($node, xs:QName('att1'), 1)
case element() return
element { fn:node-name($node) } {
$node/@*,
$node/node() ! local:change(.)
}
default return $node
};
This code assumes that an add element won't have add elements inside of it; if you will, then you'd want to do something like the second case for the first.
Attaching ancestor attributes to child nodes
To get the desired output, you can create a template that uses 2 params in the input and prints the data in the required structure/format.
<xsl:template name="printValues">
<xsl:param name="val1" />
<xsl:param name="val2" />
<xsl:text>"</xsl:text>
<xsl:value-of select="$val1" />
<xsl:text>"</xsl:text>
<xsl:text>,</xsl:text>
<xsl:text>"</xsl:text>
<xsl:value-of select="$val2" />
<xsl:text>"</xsl:text>
<xsl:text>
</xsl:text>
</xsl:template>
This template can be called from the <xsl:for-each>
loop using the values that need to printed.
<xsl:template match="/">
<xsl:for-each select="//Chemical[@displayName != '' and @displayName != 'INDEX NAME NOT YET ASSIGNED']">
<xsl:call-template name="printValues">
<xsl:with-param name="val1" select="@id" />
<xsl:with-param name="val2" select="@displayName" />
</xsl:call-template>
<xsl:call-template name="printValues">
<xsl:with-param name="val1" select="@id" />
<xsl:with-param name="val2" select="normalize-space(NameList/SystematicName/text())" />
</xsl:call-template>
<xsl:call-template name="printValues">
<xsl:with-param name="val1" select="@id" />
<xsl:with-param name="val2" select="normalize-space(NameList/Synonyms/text())" />
</xsl:call-template>
</xsl:for-each>
</xsl:template>
This will give the desired output.
"000036884","Carotene"
"000036884","Carotenes and Carotenoids"
"000036884","Phytoxanthins"
Convert all XML elements without children to XML attributes of parent element
Below is a very generic solution to your problem. It makes only minimal assumptions about the nature of the input XML. What it does assume is only that elements that have child elements should include all of them as attributes.
Apart from that, any input XML will do. Tested with Saxon 6.5, try it online here. What it does, in plain English, is:
Match an element that has at least one child element. Copy this element to the
output. For each of its child element that does not itself have child
elements, add an attribute which is named after the "childless" child
element, and add its text content as the attribute value.
Stylesheet
EDIT: As a response to your comment. Now, the stylesheet gives the correct output for an arbitrary number of "levels".
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="*[*]">
<xsl:copy>
<xsl:for-each select="*[not(*)]">
<xsl:attribute name="{local-name(.)}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()"/>
</xsl:transform>
XML Output
<?xml version="1.0" encoding="UTF-8"?>
<EnvelopeType1 CustomerNumber="1" CustomerName="Me">
<Header User="user1" Password="password"/>
<Address Number="5" StreetName="High St" State="MI"/>
</EnvelopeType1>
Related Topics
Simplexml Soap Response Namespace Issues
Destroy Session on Window Close
Upload Doesn't Work Right When the File Is Too Big
How to Create Clean Url Using .Htaccess
How to Stop PHP Sleep() Affecting My Whole PHP Code
How to Generate an .Xlsx Using PHP
Pdo Lastinsertid() Always Return 0
How to Get User's Screen Resolution with PHP
PHP Dynamic Name for Object Property
Tcpdf Error :Unable to Get the Size of the Image
Simplexml Get Element Content Based on Attribute Value
Commenting Interpreted Code and Performance
Using Windows Authentication with PHP