How to copy element after specific element in xslt - xslt

I need to copy via XSLT 1.0 g, h from //element/part[1] to //element/part[2], but I need to stick it and copy after a given element 'f'. I have this XML:
<root>
<element>
<part>
<a>1</a>
<b>2</b>
<c>3</c>
<f>4</f>
<g>5</g>
<h>6</h>
<z>50</z>
</part>
</element>
<element>
<part>
<a>1</a>
<b>2</b>
<c>3</c>
<f>4</f>
<z>55</z>
</part>
</element>
And I need to copy g, h from //element/part[1] to //element/part[2] - but I would need to copy it based on name elements, because XML is dynamic and could contain d, e before f so I can't stick it via index position. So how should I achieve this XML
<root>
<element>
<part>
<a>1</a>
<b>2</b>
<c>3</c>
<f>4</f>
<g>5</g>
<h>6</h>
<z>50</z>
</part>
</element>
<element>
<part>
<a>1</a>
<b>2</b>
<c>3</c>
<f>4</f>
<g>5</g>
<h>6</h>
<z>55</z>
</part>
</element>
I have this xsl file but it copies it at the end after 'z'
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:strip-space elements="*"/>
<xsl:output omit-xml-declaration="no" indent="yes"/>
<!-- copy everything into the output -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//root/element[2]/part">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
<xsl:if test="not(g)">
<xsl:copy-of select="../../element[1]/part/g"/>
</xsl:if>
<xsl:if test="not(h)">
<xsl:copy-of select="../../element[1]/part/h"/>
</xsl:if>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

If element f will alway be present in element[2]/part then you could do:
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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="element[2]/part/f">
<xsl:copy-of select="."/>
<xsl:if test="not(../g)">
<xsl:copy-of select="../../../element[1]/part/g"/>
</xsl:if>
<xsl:if test="not(../h)">
<xsl:copy-of select="../../../element[1]/part/h"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

Related

How to remove looping elements using XSLT Transformations where some tag have no values

<DETAILS>
<PUT>
<RECORD>ABC_PQRST0123456-001_1</RECORD>
<NUMBER>4</NUMBER>
<INST>1,2</INST>
</PUT>
<PUT>
<RECORD>ABC_PQRST0123456-001_2</RECORD>
<NUMBER>1</NUMBER>
</PUT>
</DETAILS>
How to remove the other loop elements from where INST don't have values.Can someone help me with xslt Transformation code to sort this
<PUT>
<RECORD>ABC_PQRST0123456-001_2</RECORD>
<NUMBER>1</NUMBER>
</PUT>
This does what you describe:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:apply-templates select="*" />
</xsl:template>
<xsl:template match="PUT[not(INST)]">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<!-- match but dont do anything -->
<xsl:template match="PUT">
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

xslt copy child note and add extra nodes

For the following xml:
<root>
<employees>
<employee>
<Name>ABC</Name>
<Dept>CS</Dept>
<Designation>sse</Designation>
</employee>
</employees>
</root>
I use this xslt:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<!-- Identity transform -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Name">
<xsl:copy-of select="."/>
<Age>34</Age>
</xsl:template>
<xsl:template match="Dept">
<xsl:copy-of select="."/>
<Domain>Insurance</Domain>
</xsl:template>
</xsl:stylesheet>
I would like to achieve the following output
<employee>
<Name>ABC</Name>
<Age>34</Age>
<Dept>CS</Dept>
<Domain>Insurance</Domain>
<Designation>sse</Designation>
</employee>
I don't know what xpath shall I use to limit the result only to employee node.
Well, processing starts at the document node (root node) denoted by / thus if you write a template
<xsl:template match="/">
<xsl:apply-templates select="//employee"/>
</xsl:template>
you only process any employee descendants of the document with the rest of the templates.
See https://xsltfiddle.liberty-development.net/pPqsHT6 for a working demo of above suggestion integrated into your stylesheet, the whole code is
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- Identity transform -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Name">
<xsl:copy-of select="."/>
<Age>34</Age>
</xsl:template>
<xsl:template match="Dept">
<xsl:copy-of select="."/>
<Domain>Insurance</Domain>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="//employee"/>
</xsl:template>
</xsl:stylesheet>

Remove xml namespace while transformation using xslt

I am transforming XML using XSLT and facing issue while namespace removal. If I remove xmlns it works fine.
Issue 1. It doesn't delete namespace from transformed XML
Issue 2. Doesn't implements other template I have in transformation.
My Input XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Catalog xmlns="http://example.com">
<Books>
<book1>Wise1 Otherwise</book1>
<book2>Great Expectations</book2>
</Books>
<library>
<Name> Forsyth </Name>
<city> Cumming </city>
</library>
</Catalog>
Expected Result
<?xml version="1.0" encoding="UTF-8"?>
<Import>
<Books>
<book1>Wise1 Otherwise</book1>
<book2>Great Expectations</book2>
</Books>
<elab>
<Name> Forsyth </Name>
<city> Cumming </city>
</elab>
</Import>
XSL
<?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" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#xmlns">
<xsl:element name="{local-name()}" namespace="http://example.com">
<xsl:apply-templates select="node() | #*"/>
</xsl:element>
</xsl:template>
<xsl:template match="library">
<elab>
<xsl:apply-templates />
</elab>
</xsl:template>
<xsl:template match="Catalog">
<Import>
<xsl:apply-templates />
</Import>
</xsl:template>
</xsl:stylesheet>
I think you are missing the namespace declaration in the XSLT templates to match elements:
This is my try:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns="http://example.com">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
<xsl:template match="ns:library">
<elab>
<xsl:apply-templates />
</elab>
</xsl:template>
<xsl:template match="ns:Catalog">
<Import>
<xsl:apply-templates />
</Import>
</xsl:template>
</xsl:stylesheet>

Adding a namespace to just one element

How do I only add a namespace to the root element?
My XML:
<Envelope>
<from>
<contents />
</from>
</Envelope>
My desired output:
<Envelope xmlns:tns="Foo">
<from>
<contents />
</from>
</Envelope>
I can only get "xmlns='Foo'" using this, not "xmlns:tns=..":
<xsl:element name="{local-name()}" namespace="Foo" >
<xsl:copy-of select="attribute::*"/>
<xsl:apply-templates />
</xsl:element>
Here is a complete transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:tns="Foo">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:element name="{name()}">
<xsl:copy-of select=
"document('')/*/namespace::*[name()='tns']"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<Envelope>
<from>
<contents />
</from>
</Envelope>
the wanted, correct result is produced:
<Envelope xmlns:tns="Foo">
<from>
<contents/>
</from>
</tns:Envelope>

How to prevent line-wrapping by XSLT?

I have some XML like this:
<root>
<do-not-sort>
<z/>
<y/>
</do-not-sort>
<sortable>
<e f="fog" h="bat" j="cat">
<n n="p"/>
<m n="p"/>
</e>
<d b="fop" c="bar" k="cab">
<m o="p"/>
<m n="p"/>
</d>
</sortable>
</root>
I want to sort the children of the "sortable" element by their textual representation, to end up with this:
<root>
<do-not-sort>
<z/>
<y/>
</do-not-sort>
<sortable>
<d b="fop" c="bar" k="cab">
<m n="p"/>
<m o="p"/>
</d>
<e f="fog" h="bat" j="cat">
<m n="p"/>
<n n="p"/>
</e>
</sortable>
</root>
I am currently doing this by applying the following XSLT template:
<?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" encoding="UTF-8" />
<xsl:template match="#* | /">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:value-of select="normalize-space(text()[1])" />
<xsl:apply-templates select="*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="sortable//*">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:value-of select="normalize-space(text()[1])" />
<xsl:apply-templates select="*">
<xsl:sort data-type="text" select="local-name()" />
<xsl:sort data-type="text" select="#*" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The sorting works correctly, but if the sorted elements have a lot of attributes, the later attributes each wrap onto a new line, for example:
<sortable>
<this is="an" element="with" a="lot" of="attributes"
and="the"
excess="ones"
each="wrap"
onto="their"
own="line"/>
How do I keep all these attributes on the same line, i.e.
<sortable>
<this is="an" element="with" a="lot" of="attributes" and="the" excess="ones" each="wrap" onto="their" own="line"/>
How do I keep all these attributes on
the same line
In your code, replace:
<xsl:output method="xml" indent="yes" encoding="UTF-8" />
with
<xsl:output method="xml" encoding="UTF-8" />
Of course, this will produce the complete output on a single line! At the moment of writing this XSLT 2.0 still doesn't have a finer grained control over the serialization of the output.
In case you need some elements indented and some not, then you will have to provide your own post-processing (and this post-processing may be easier to write with something different from XSLT).
Update:
Actually, using Saxon 9.1.07 and
<xsl:output method="html" encoding="UTF-8" />
where the complete transformation is:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="UTF-8" />
<xsl:template match="#* | /">
<xsl:copy>
<xsl:apply-templates select="#* | node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:value-of select="normalize-space(text()[1])" />
<xsl:apply-templates select="*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="sortable//*">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:value-of select="normalize-space(text()[1])" />
<xsl:apply-templates select="*">
<xsl:sort data-type="text" select="local-name()" />
<xsl:sort data-type="text" select="#*" />
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
and the source XML document is:
<root>
<do-not-sort>
<z></z>
<y></y>
</do-not-sort>
<sortable>
<this is="an" element="with" a="lot" of="attributes" and="the" excess="ones" each="wrap" onto="their" own="line"></this>
<e f="fog" h="bat" j="cat"></e>
<d b="fop" c="bar" k="cab"></d>
<d b="foo" c="baz" k="cap"></d>
</sortable>
</root>
I get the output with the wanted indentation:
<root>
<do-not-sort>
<z></z>
<y></y>
</do-not-sort>
<sortable>
<this is="an" element="with" a="lot" of="attributes" and="the" excess="ones" each="wrap" onto="their" own="line"></this>
<e f="fog" h="bat" j="cat"></e>
<d b="fop" c="bar" k="cab"></d>
<d b="foo" c="baz" k="cap"></d>
</sortable>
</root>
If you are using Saxon, and want to avoid everything being on one line, you can use the 'line-length' attribute, like this:
<xsl:output xmlns:saxon="http://saxon.sf.net/" indent="yes" saxon:line-length="2000"/
However, be aware that this only works on the Saxon PE (Professional) edition. See here for details:
http://www.saxonica.com/products/products.xml
If you are using the HE (Home) version, you will get an error like this:
Transformation failed: Requested feature (custom serialization {http://saxon.sf.net/}line-length) requires Saxon-PE