XSLT renaming element with attribute value? - xslt

I have this XML:
<PARTY>
<PARTY_ID type="buyer_specific">XXXXXX</PARTY_ID>
<PARTY_ID type="gln">YYYYYYYYYYYY</PARTY_ID>
<PARTY_ROLE>buyer</PARTY_ROLE>
</PARTY>
And I need the elements PARTY_ID to be named different, preferably set to the same as type
So preferred result would be:
<PARTY>
<buyer_specific>XXXXXX</buyer_specific>
<gln>YYYYYYYYYYYY</gln>
<PARTY_ROLE>buyer</PARTY_ROLE>
</PARTY>
I know this is probably a easy fix, but I am really new at XSLT..... thx for your patience and help!

I have managed to get something that is ok for the task at hand working:
<?xml version="1.0"?>
<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="PARTY_ID[#type]">
<xsl:element name="{#type}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}" namespace="http://www.opentrans.org/XMLSchema/2.1">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Not ideal, but it works...
Thank you for reading!

As simple as this:
<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()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="PARTY_ID[#type]">
<xsl:element name="{#type}"><xsl:apply-templates/></xsl:element>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<PARTY>
<PARTY_ID type="buyer_specific">XXXXXX</PARTY_ID>
<PARTY_ID type="gln">YYYYYYYYYYYY</PARTY_ID>
<PARTY_ROLE>buyer</PARTY_ROLE>
</PARTY>
The wanted, correct result is produced:
<PARTY>
<buyer_specific>XXXXXX</buyer_specific>
<gln>YYYYYYYYYYYY</gln>
<PARTY_ROLE>buyer</PARTY_ROLE>
</PARTY>
Explanaion:
Using the identity rule to copy every node "as-is"
Overriding the identity rule with a more specific one matching any PARTY_ID element that has a type attribute
Using an <xsl:element> instruction with AVL (Attribute Value Template) for the name of the element -- to be constructed from the value of the type attribute of the current node.

Related

Require Help in XSLT coding

I am new to XSLT .Kindly help me with the below query :
My Source XML:
<?xml version="1.0" encoding="UTF-8"?>
<ns1:Header1 xmlns:ns1="urn:src:abc">
<Header2>
<Header3>
<field1>1.1.2017</field1>
<field2>12</field2>
<field3> </field3>
</Header3>
</Header2>
</ns1:Header1>
Target/Expected XML
<?xml version="1.0" encoding="UTF-8"?>
<ns2:Header1 xmlns:ns2="urn:tar:abc" xmlns:v1="def.v1">
<Header2>
<v1:Header3>
<field1>1.1.2017</field1>
<field2>12</field2>
<field3> </field3>
</v1:Header3>
</Header2>
</ns2:Header1>
And also i need to remove the space/blank between the tag filed3 ( Field3 value sometimes will be blank)
I am using the below code for Transformation
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs" version="2.0">
<xsl:template match="*">
<ns2:Header1 xmlns:ns2="urn:tar:abc" xmlns:v1="def.v1">
<Header2>
<xsl:copy-of select="//Header3"/>
</Header2>
</ns2:Header1>
</xsl:template>
</xsl:stylesheet>
I am not able to achieve my Target XML. Kindly help
Many thanks in advance
Regards,
Pavi
Your current template matches * which will match any element, but really you only want it to match the root element.
Additionally, as you want to change Header3 from being in no namespace, to being in the "def.v1" namespace, you should be making use of xsl:apply-templates, not xsl:copy-of
<xsl:template match="/*">
<ns2:Header1 xmlns:ns2="urn:tar:abc" xmlns:v1="def.v1">
<xsl:apply-templates />
</ns2:Header1>
</xsl:template>
You would then need a template matching Header3 to change the namespace
<xsl:template match="Header3" xmlns:v1="def.v1">
<v1:Header3>
<xsl:apply-templates />
</v1:Header3>
</xsl:template>
You would then just need the identity template to copy all other nodes without changes.
Try this XSLT
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs" version="2.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/*">
<ns2:Header1 xmlns:ns2="urn:tar:abc" xmlns:v1="def.v1">
<xsl:apply-templates />
</ns2:Header1>
</xsl:template>
<xsl:template match="Header3" xmlns:v1="def.v1">
<v1:Header3>
<xsl:apply-templates />
</v1:Header3>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy copy-namespaces="no">
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
See it in action at http://xsltransform.net/jz1PuQb

Using xslt to change an attribute value using arithmetic operations

I'm trying to bound the value of an xml attribute using xslt/xpath 1.0. In this example, it would be the id attribute on the m_m element.
<blart>
<m_data>
<m_m name="arggg" id="99999999" subs="asas"/>
</m_data>
<m_data>
<m_m name="arggg" id="99" subs="asas"/>
</m_data>
</blart>
If the id is greater then 20000 it gets set to 20000. I have the following xslt. I know it selects the correct node and attribute. It obviously is just outputing 20000. I realize I should have some sort of xpath logic in there but I'm having a hard time developing it. I have some big holes in my knowledge of xpath and xslt. If you can point me in the right direction in helping me understand on what I should be doing I would really appreciate it.
<?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:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match ="m_data/m_m/#id[.> 20000]">20000 </xsl:template>
</xsl:stylesheet>
The expected output would be
<blart>
<m_data>
<m_m name="arggg" id="20000" subs="asas"/>
</m_data>
<m_data>
<m_m name="arggg" id="99" subs="asas"/>
</m_data>
</blart>
You can use the following XSLT that gives flexibility to the attribute you want to change, and keeps everything else as it is:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match ="m_data/m_m/#id[. > 20000]">
<xsl:attribute name="id">20000</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Why don't you try:
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 ="m_m/#id[. > 20000]">
<xsl:attribute name="id">20000</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
NOTE: Since I posted this, much better answers were contributed (see here and here). SO won't let me delete this one because it was accepted, but in all fairness and for the sake of quality, I should encourage you to upvote the two aforementioned answers, so that they stand out over this one.
How about 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" indent="yes"/>
<xsl:template match="m_m">
<m_m>
<xsl:copy-of select="#*" />
<xsl:if test="#id > 20000">
<xsl:attribute name="id">20000</xsl:attribute>
</xsl:if>
</m_m>
</xsl:template>
<xsl:template match="m_data">
<m_data>
<xsl:apply-templates select="m_m" />
</m_data>
</xsl:template>
<xsl:template match="/blart">
<blart>
<xsl:apply-templates select="m_data" />
</blart>
</xsl:template>
</xsl:stylesheet>

XSLT XPath, Need to change node value to based on number value of a sibling node

I am trying to change a node value to one of two different things by checking to see if the the following node contains an odd or even number.
This is the source XML.
<?xml version="1.0" encoding="UTF-8"?>
<FILE>
<CLAIM>
<PERSON>
<PROVIDER>
<HEADER>
<FLAG>PPON</FLAG>
<IDNO>11612</IDNO>
</HEADER>
</PROVIDER>
</PERSON>
</CLAIM>
</FILE>
And the XSLT I'm trying is:
<?xml version='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"
omit-xml-declaration="no"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="FLAG/node()">
<xsl:choose>
<xsl:when test="number(../IDNO) mod 2 = 0">EVEN</xsl:when>
<xsl:otherwise>ODD</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
And what I want to the output to be is
<?xml version="1.0" encoding="UTF-8"?>
<FILE>
<CLAIM>
<PERSON>
<PROVIDER>
<HEADER>
<FLAG>EVEN</FLAG>
<IDNO>11612</IDNO>
</HEADER>
</PROVIDER>
</PERSON>
</CLAIM>
</FILE>
I know I'm not getting the IDNO in my when test because my code gives me ODD all the time, also while debugging I tried putting the value of the when test into FLAG and I get NaN. But I can't come up with the correct syntax to get the IDNO into the test.
And yes, I'm new to XSLT, so maybe this is a dumb question, but I've tried many different things and searched this site and others for the answer with no luck.
Thanks in advance for your help.
DWL
You current template matches on a child of FLAG
<xsl:template match="FLAG/node()">
Now, when you use .. this is looking for the parent, so .. will match FLAG in this context, and so ../IDNO is looking for a child of FLAG called IDNO, which does not exist.
You need to go one more level up. Try this instead
<xsl:template match="FLAG/text()">
<xsl:choose>
<xsl:when test="number(../../IDNO) mod 2 = 0">EVEN</xsl:when>
<xsl:otherwise>ODD</xsl:otherwise>
</xsl:choose>
</xsl:template>
It might be easier to not check the axis of the node, but rather the node itself:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:a="http://www.mycompany.com/services/core/file" exclude-result-prefixes="a">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" omit-xml-declaration="no"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="IDNO">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:choose>
<xsl:when test="number(.) mod 2 = 0">EVEN</xsl:when>
<xsl:otherwise>ODD</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Attribute issue in X-Path expression

Below is my input XML
<ServiceIncident xmlns="http://b2b.ibm.com/schema/IS_B2B_CDM/R2_2">
<ServiceProvider>
<Person Role="AffectedUser">
<ContactID>ITELLA_BRIDGE_USER</ContactID>
<FullName>Chad Whaley</FullName>
</Person>
</ServiceProvider>
In the output in Person Role i need to get Role in place of AffectedUser in the above code Role is an attribute for person.Below is my XSLT
xmlns:r2="http://b2b.ibm.com/schema/IS_B2B_CDM/R2_2">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="r2:Person#Role">
<xsl:copy>Owner</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Your input is not valid XML. Hopefully you have valid XML. I'm assuming this:
<ServiceIncident xmlns="http://b2b.ibm.com/schema/IS_B2B_CDM/R2_2">
<ServiceProvider>
<Person Role="AffectedUser">
<ContactID>ITELLA_BRIDGE_USER</ContactID>
<FullName>Chad Whaley</FullName>
</Person>
</ServiceProvider>
</ServiceIncident>
Your XSLT is also not XSLT, the beginning is missing. I'm assuming that it starts with <xsl:stylesheet somewhere.
Under all the assumtions, the XSLT transformer gives me quite a clear error message:
'r2:Person#Role' is an invalid XPath expression.
This can be fixed to r2:Person/#Role.
Next, <xsl:copy> does not work for you. Maybe you want
<xsl:attribute name="Role">Owner</xsl:attribute>
So finally we have
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xml:space="default" exclude-result-prefixes="" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:r2="http://b2b.ibm.com/schema/IS_B2B_CDM/R2_2">
<xsl:output method="xml" indent="yes" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="r2:Person/#Role">
<xsl:attribute name="Role">Owner</xsl:attribute>
</xsl:template>
</xsl:stylesheet>

Add additional namespace with XSLT

I need to add an additional namespace to an already namespaced XML file but only if a particular element does not exist.
My XML doc looks like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<everyone xmlns="AAA" xmlns:ns2="BBB" xmlns:ns3="CCC" company="TestingCorp">
<common>Stuff Here</common>
<ns2:person id="123">
<ns3:firstname>Billy</ns3:firstname>
<ns2:lastname>Bobby</ns2:lastname>
</ns2:person>
</everyone>
... and if there is no ns3:firstname element in the person element, I'd like to add a new namespace and (e.g. xmlns:frog="FFF") and also an additional element within person as shown below:
Desired Output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<everyone xmlns="AAA" xmlns:ns2="BBB" xmlns:ns3="CCC" xmlns:frog="FFF" company="TestingCorp">
<common>Stuff Here</common>
<ns2:person id="123">
<frog:title>
<Master/>
</frog:title>
<ns2:lastname>Bobby</ns2:lastname>
</ns2:person>
</everyone>
My XSL doc currently is:
<?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" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- Copy Everything -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/*">
<xsl:element name="ns:{local-name()}">
<xsl:attribute name="frog">fff</xsl:attribute>
<xsl:apply-templates select="node()|#*" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
.... unfortunately this does not work.
I've tried lots of different things but can't seem to achieve this using XSLT v1.0. Any help would be greatly appreciated.
First you need to declare the various namespaces in your stylesheet, as well as the "AAA" default namespace
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="AAA"
xmlns:frog="FFF"
xmlns:ns2="BBB"
xmlns:ns3="CCC">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- Copy Everything -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- for a Person with no firstname, add a frog:title -->
<xsl:template match="ns2:person[not(ns3:firstname)]">
<xsl:copy>
<!-- must handle attributes before elements/text nodes -->
<xsl:apply-templates select="#*" />
<frog:title>
<Master/>
</frog:title>
<xsl:apply-templates select="node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This will produce
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<everyone xmlns="AAA" xmlns:ns2="BBB" xmlns:ns3="CCC" company="TestingCorp">
<common>Stuff Here</common>
<ns2:person id="123">
<frog:title xmlns:frog="FFF">
<Master/>
</frog:title>
<ns2:lastname>Bobby</ns2:lastname>
</ns2:person>
</everyone>
If the xmlns:frog absolutely must be on the everyone element rather than on each frog:title then you could add another template
<xsl:template match="/*">
<xsl:copy>
<xsl:copy-of select="document('')/xsl:stylesheet/namespace::frog" />
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
to copy the namespace declaration off the stylesheet element (though this would mean that every output document has an xmlns:frog declaration even if it doesn't involve any frog:* elements).
Edit: apparently Xalan doesn't like the copy-of namespaces from document(''), as an alternative, if you know that the document element will always have the same name then you can hard code that as a literal result element
<xsl:template match="/*">
<everyone xmlns:frog="FFF">
<xsl:copy-of select="namespace::*" />
<xsl:apply-templates select="#*|node()" />
</everyone>
</xsl:template>
(technically it will do what you want even without the explicit xmlns:frog in this template, since literal result elements always get the namespace declarations that are in scope at the point in the stylesheet where they are declared, but the intention is arguably clearer if you include it)
This mailing list post gives some possible insights into the reason for document('') not working as it should.