i am a newbie when it comes to XSLT translations, and have been searching with this question for a while, but did not find any answher to it. I have an XML file looking like this:
<item>
<code>I001</code>
<description>DEF</description>
<properties>
<line1>
<key>key 1</key>
<value>value 1</value>
</line1>
<line2>
<key>key 2</key>
<value>value 2</value>
</line2>
</properties>
</item>
The label-software that i need to process this data with does not cope with this kind of leveled xml, so i have to transform it to something like this
<?xml version="1.0" encoding="utf-8"?>
<item>
<code>I001</code>
<description>DEF</description>
<P1_key>key 1</P1_key>
<P1_value>value 1</P1_value>
<P2_key>key 1</P2_key>
<P2_value>value</P2_value>
</item>
So far i have come up with an xsl-file that looks like this:
<?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"/>
<!-- Identity rule -->
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<!-- special rules ... -->
<xsl:template match="properties/line1/*">
<xsl:element name="P1_{local-name()}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="properties/line2/*">
<xsl:element name="P2_{local-name()}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
This does only partly what i need (renaming the elements), wit a result looking like this:
<?xml version="1.0" encoding="utf-8"?>
<item>
<code>I001</code>
<description>DEF</description>
<properties>
<line1>
<P1_key>key 1</P1_key>
<P1_value>value 1</P1_value>
</line1>
<line2>
<P2_key>key 1</P2_key>
<P2_value>value</P2_value>
</line2>
</properties>
</item>
What i now would like to now is how to move the renamed elements (P1_key, P1_value, P2_key, P2_value) to the level.
Any hints would be appreciated.
Thanks
How about:
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="/item">
<xsl:copy>
<xsl:copy-of select="code | description"/>
<xsl:for-each select="properties/*/key">
<xsl:element name="P{position()}_key">
<xsl:value-of select="."/>
</xsl:element>
<xsl:element name="P{position()}_value">
<xsl:value-of select="following-sibling::value[1]"/>
</xsl:element>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Or perhaps:
<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="/item">
<xsl:copy>
<xsl:copy-of select="code | description"/>
<xsl:for-each select="properties/*/key">
<xsl:variable name="line-num" select="substring-after(name(..), 'line')" />
<xsl:element name="P{$line-num}_key">
<xsl:value-of select="."/>
</xsl:element>
<xsl:element name="P{$line-num}_value">
<xsl:value-of select="following-sibling::value[1]"/>
</xsl:element>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Of course, if there always will be exactly two lines, you could just spell it out:
<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="/item">
<xsl:copy>
<xsl:copy-of select="code | description"/>
<P1_key>
<xsl:value-of select="properties/line1/key" />
</P1_key>
<P1_value>
<xsl:value-of select="properties/line1/value" />
</P1_value>
<P2_key>
<xsl:value-of select="properties/line2/key" />
</P2_key>
<P2_value>
<xsl:value-of select="properties/line2/value" />
</P2_value>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
BTW, your method could work too, if you add another template:
<xsl:template match="properties | line1 | line2">
<xsl:apply-templates />
</xsl:template>
Related
Here's my input XML:
<?xml version="1.0" encoding="UTF-8"?>
<Sync
xmlns="http://schema.infor.com/InforOAGIS/2" languageCode="en-US" versionID="2.8.0">
<Data>
<ID>0001</ID>
<Text>ABCD</Text>
</Data>
</Sync>
And here's my expected outcome:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Sync xmlns:ns0="http://schema.infor.com/InforOAGIS/2"
languageCode="en-US"
versionID="2.8.0">
<DataArea xmlns:dns="http://schema.infor.com/InforOAGIS/2" xmlns="">
<ID>0001</ID>
<Text>ABCD</Text>
</DataArea>
</ns0:Sync>
My current XSLT as below (https://xsltfiddle.liberty-development.net/nbiE19N).
There are 2 problems:
I have the extra xmlns="" in DataArea element. I only want to add the dns namespace.
I cannot add the ns0 prefix for my namespace
<xsl:stylesheet version="2.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="/*:Sync">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*:Sync/*:Data">
<DataArea>
<xsl:namespace name="dns" select="'http://schema.infor.com/InforOAGIS/2'"/>
<ID>
<xsl:value-of select="/*:Sync/*:Data/*:ID"/>
</ID>
<Text>
<xsl:value-of select="/*:Sync/*:Data/*:Text"/>
</Text>
</DataArea>
</xsl:template>
</xsl:stylesheet>
Any suggestion is appreciated!
Does this return the expected result:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="http://schema.infor.com/InforOAGIS/2">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/Sync">
<ns0:Sync xmlns:ns0="http://schema.infor.com/InforOAGIS/2">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</ns0:Sync>
</xsl:template>
<xsl:template match="Data">
<DataArea xmlns:dns="http://schema.infor.com/InforOAGIS/2">
<xsl:apply-templates/>
</DataArea>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
P.S. I am not sure why you need the xmlns:dns="http://schema.infor.com/InforOAGIS/2" declaration; it's not being used anywhere.
I have some XML and having a difficult time transforming it.
Example XML:
<?xml version="1.0" encoding="utf-8"?>
<Cars xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Car> ... </Car>
</Cars>
I would like to change it to:
<?xml version="1.0" encoding="utf-8"?>
<Depot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Cars>
<Car> ... </Car>
</Cars>
</Depot>
Sounds simple enough but the problem is some data is already in the expected format, in which case I don't want to apply the transform. How do I achieve this?
EDIT
Some starting XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" mlns:xsi="http://www.w3.org/2001/XMLSchema-
instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="Cars">
<Depot>
<Cars>
<xsl:apply-templates select="*"/>
</Cars>
</Depot>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I think you only want to match Cars if it is the root element, so instead of your template matching "Cars", change it to match "/Cars"
<xsl:template match="/Cars">
Try this XSLT (which I have slightly amended to get the first template to call the identity template)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
<xsl:output method="xml" indent="yes" />
<xsl:template match="/Cars">
<Depot xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsl:call-template name="identity" />
</Depot>
</xsl:template>
<xsl:template match="#*|node()" name="identity">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I think that it is just necessary to use a choose in the root template to test if the node Depot exists, if not create it:
<xsl:template match="/">
<xsl:choose>
<xsl:when test="Depot">
<xsl:apply-templates/>
</xsl:when>
<xsl:otherwise>
<Depot>
<xsl:apply-templates/>
</Depot>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()" />
</xsl:copy>
</xsl:template>
This also gives same output.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
<xsl:template match="Cars">
<Depot>
<xsl:copy-of select="."/>
</Depot>
</xsl:template>
</xsl:stylesheet>
I wanted to extract leaf nodes and have them sorted.
My XSL gives unexpected results. How can I solve this?
Input
<root>
<b>
<b33 zzz="2" fff="3"></b33>
<b11></b11>
<b22></b22>
</b>
<a>
<a27></a27>
<a65 fff="0" eee="2" zzz="10"></a65>
<a11></a11>
</a>
</root>
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" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<root>
<xsl:call-template name="leafnodes"/>
</root>
</xsl:template>
<xsl:template match="*[not(*)]|#*" name="leafnodes">
<xsl:copy>
<xsl:apply-templates select="node()">
<xsl:sort select="name()"/>
</xsl:apply-templates>
<xsl:apply-templates select="#*">
<xsl:sort select="name()"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Output (I would expected it to be sorted, it is not)
<root>
<b33 fff="3" zzz="2" />
<b11 />
<b22 />
<a27 />
<a65 eee="2" fff="0" zzz="10" />
<a11 />
</root>
I would expect the nodes in the order a11, a27, a65, b11, b22, b33.
If I leave out the '[not(*)]', the xsl takes all nodes and sorts them properly.
How can this be solved?
To output all element which have no child sorted by name and the attributes also sorted by name. Try this;
<?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" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<root>
<xsl:apply-templates select="//*[not(*)]">
<xsl:sort select="name()"/>
</xsl:apply-templates>
</root>
</xsl:template>
<xsl:template match="*|#*">
<xsl:copy>
<xsl:apply-templates select="#*" >
<xsl:sort select="name()"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Which will generate following output:
<root>
<a11/>
<a27/>
<a65 eee="2" fff="0" zzz="10"/>
<b11/>
<b22/>
<b33 fff="3" zzz="2"/>
</root>
Input file:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:root xmlns:ns0="http://xyz.com/separate">
<ns0:root1>
<ns3:Detail xmlns:ns3="http://POProject/Details">
<DetailLines>
<ItemID>
<ItemDescription/>
</DetailLines>
</ns3:Detail>
</ns0:root1>
</ns0:root>
Output file:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:Detail xmlns:ns0="http://POProject/Details">
<DetailLines>
<ItemID>
<ItemDescription/>
</DetailLines>
</ns0:Detail>
Question: I have to remove the root1 and root nodes and need to do small
changes in Detail node. How to write a xslt code to achieve this?
This...
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://xyz.com/separate"
xmlns:ns3="http://POProject/Details">
<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="/">
<xsl:apply-templates select="*/*/ns3:Detail" />
</xsl:template>
<xsl:template match="ns3:Detail">
<xsl:apply-templates select="." mode="copy-sans-namespace" />
</xsl:template>
<xsl:template match="*" mode="copy-sans-namespace">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="copy-sans-namespace" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
...will yield this...
<?xml version="1.0" encoding="utf-8"?>
<ns3:Detail xmlns:ns3="http://POProject/Details">
<DetailLines>
<ItemID />
<ItemDescription />
</DetailLines>
</ns3:Detail>
I'm not sure it is possible to control the prefix. The XDM data model does not consider it to be significant information.
UDPATE
To get the prefix rename, I thought you would have to go to an XML 1.1 supporting XSLT processor (allowing prefix undefine), but I found a way to do it with XML 1.0 . Try this ...
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://xyz.com/separate">
<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:ns3="http://POProject/Details">
<xsl:apply-templates select="*/*/ns3:Detail" />
</xsl:template>
<xsl:template match="ns0:Detail" xmlns:ns0="http://POProject/Details">
<ns0:Detail xmlns:ns0="http://POProject/Details">
<xsl:apply-templates select="*" mode="copy-sans-namespace" />
</ns0:Detail>
</xsl:template>
<xsl:template match="*" mode="copy-sans-namespace">
<xsl:element name="{name()}" namespace="{namespace-uri()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="copy-sans-namespace" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
I have following XML. I was able to remove all namespaces but not able to remove xsi:type using XSL.
XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<StudentResult xmlns='http://ns.xyz.org/2004-08-02' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:ns1='http://ns.xyz.org/2004-08-02' xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:type='ns1:StudentResult'>
<StudentId idOwner='xyz'><IdValue name='ClientId'>9103-XML</IdValue></StudentId>
<ClientOrderId idOwner='Cloud'><IdValue name='OrderNumber'>272454</IdValue></ClientOrderId>
<Results>false</Results>
</StudentResult>
Desired Output:
<?xml version="1.0" encoding="utf-8"?>
<StudentResult>
<StudentId idOwner="xyz"><IdValue name="ClientId">9103-XML</IdValue></StudentId>
<ClientOrderId idOwner="Cloud"><IdValue name="OrderNumber">272454</IdValue></ClientOrderId>
<Results>false</Results>
</StudentResult>
This is the xslt I used but it did not help.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml" encoding="utf-8" />
<xsl:template match="/|comment()|processing-instruction()">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<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="/">
<xsl:apply-templates select="#*|node()|processing-instruction()"/>
</xsl:template>
</xsl:stylesheet>
Add a template
<xsl:template match="#xsi:type"/>
plus
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
exclude-result-prefixes="xsi">
on the stylesheet's root element.
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()[not(self::*)]">
<xsl:copy/>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="#*[not(name()='xsi:type')]|node()"/>
</xsl:element>
</xsl:template>
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<StudentResult
xmlns='http://ns.xyz.org/2004-08-02'
xmlns:xsd='http://www.w3.org/2001/XMLSchema'
xmlns:ns1='http://ns.xyz.org/2004-08-02'
xmlns:soapenv='http://schemas.xmlsoap.org/soap/envelope/'
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xsi:type='ns1:StudentResult'>
<StudentId idOwner='xyz'>
<IdValue name='ClientId'>9103-XML</IdValue>
</StudentId>
<ClientOrderId idOwner='Cloud'>
<IdValue name='OrderNumber'>272454</IdValue>
</ClientOrderId>
<Results>false</Results>
</StudentResult>
produces the wanted, correct result:
<StudentResult>
<StudentId idOwner="xyz">
<IdValue name="ClientId">9103-XML</IdValue>
</StudentId>
<ClientOrderId idOwner="Cloud">
<IdValue name="OrderNumber">272454</IdValue>
</ClientOrderId>
<Results>false</Results>
</StudentResult>