xslt to add an attribute - xslt

I need to add a namespace, and to add an attribute to certain nodes. With this input:
<root>
<Node1>test</Node1>
<DateTo />
</root>
I want this output:
<my:root xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2010-07-28T07:33:11">
<my:Node1>test</my:Node1>
<my:DateTo xsi:nil="true"/>
</my:root>
The DateTo node needs to have this attribute set.
I successfully added the namespace with this transform, but cannot get the attribute added.
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
<xsl:template match='*'>
<xsl:element name='my:{local-name()}' namespace='http://schemas.microsoft.com/office/infopath/2003/myXSD/2010-07-28T07:33:11' >
<xsl:apply-templates />
</xsl:element>
</xsl:template>
</xsl:stylesheet>"
I get this error "Attribute and namespace nodes cannot be added to the parent element after a text, comment, pi, or sub-element node has already been added." Be grateful for any help here.

You can try the following to insert the additional attribute:
<?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='*'>
<xsl:element name='my:{local-name()}'
xmlns:my='http://schemas.microsoft.com/office/infopath/2003/myXSD/2010-07-28T07:33:11'>
<xsl:if test="not(*) and not(normalize-space())">
<xsl:attribute name="xsi:nil">
<xsl:value-of select="true()"/>
</xsl:attribute>
</xsl:if>
<xsl:apply-templates />
</xsl:element>
</xsl:template>
</xsl:stylesheet>

Related

Rename the root node in xml using xslt

I am new to xslt and was trying to find a solution to my problem below.
I have a xml, and using xslt I need to do the following
just rename the root node to something else (without copying the entire structure of root node into another tag)
Get rid of the namespaces information in nodes/elements/attributes
Below is my XML
<lbl:ABCD>
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:lbl="http://abc.xyz.com/abc/xsd/sdf/aaaa"
xsi:schemaLocation="http://abc.ayx.com/sdf/xsd/label/efr efr.xsd">
<lbl:Title>title</lbl:Title>
<lbl:date>01/Aug/2018</lbl:date>
<lbl:id>12345</lbl:id>
<lbl:location>0362</lbl:location>
<lbl:Status>aaaa</lbl:status>
<lbl:hashnum>11801792113759009</lbl:hashnum>
</lbl:ABCD>
The output I want to achieve is
<shp>
<Title>title</Title>
<date>01/Aug/2018</date>
<id>12345</id>
<location>0362</location>
<Status>aaaa</status>
<hashnum>11801792113759009</hashnum>
</shp
>
But so far I have been able to manage below (which is not how I need it to be)
<?xml version="1.0" encoding="utf-16"?>
<shp xmlns:lbl="http://abc.xyz.com/abc/xsd/sdf/aaaa">
<lbl:ICNLabel
xsi:schemaLocation="http://abc.ayx.com/sdf/xsd/label/efr efr.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<lbl:Title>title</lbl:Title>
<lbl:date>01/Aug/2018</lbl:date>
<lbl:id>12345</lbl:id>
<lbl:location>0362</lbl:location>
<lbl:Status>aaaa</lbl:Status>
<lbl:hashnum>11801792113759009</lbl:hashnum>
</lbl:ICNLabel>
</shp>
Can someone guide me to resolve this?
XML: (namespaces needs to address at root element)
<lbl:ABCD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:lbl="http://abc.xyz.com/abc/xsd/sdf/aaaa"
xsi:schemaLocation="http://abc.ayx.com/sdf/xsd/label/efr efr.xsd">
<lbl:Title>title</lbl:Title>
<lbl:date>01/Aug/2018</lbl:date>
<lbl:id>12345</lbl:id>
<lbl:location>0362</lbl:location>
<lbl:Status>aaaa</lbl:Status>
<lbl:hashnum>11801792113759009</lbl:hashnum>
</lbl:ABCD>
XSLT:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:lbl="http://abc.xyz.com/abc/xsd/sdf/aaaa">
<xsl:template match="*">
<xsl:element name="{local-name()}"><!--avoiding namespaces-->
<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="comment()">
<xsl:comment>
<xsl:value-of select="."/>
</xsl:comment>
</xsl:template>
<xsl:template match="/*">
<xsl:element name="shp">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
OutPut:
<?xml version="1.0" encoding="UTF-8"?><shp>
<Title>title</Title>
<date>01/Aug/2018</date>
<id>12345</id>
<location>0362</location>
<Status>aaaa</Status>
<hashnum>11801792113759009</hashnum>
</shp>

xslt attributes are not getting copied

This is my xml, I have simple XSLT to update just one element and copy everything else same.
<?xml version='1.0' encoding='UTF-8'?>
<sip xmlns="urn:x-emc:ia:schema:sip:1.0">
<dss>
<holding>message</holding>
<id>message-2018-06-12-gthlZYrWZjJuQ</id>
<pdi_schema>urn:bhp:documentum:message.1.0</pdi_schema>
<production_date>2018-06-12T21:23:04.752+08:00</production_date>
<base_retention_date>2018-06-12T21:23:04.752+08:00</base_retention_date>
<producer>IA_Samples</producer>
<entity>IA</entity>
<priority>0</priority>
<application>IA</application>
</dss>
<production_date>2018-06-12T21:23:04.752+08:00</production_date>
<seqno>1</seqno>
<is_last>true</is_last>
<aiu_count>10</aiu_count>
<page_count>0</page_count>
<pdi_hash algorithm="SHA-256" encoding="base64">iLrzH22nT7Nr258E/oBve+dFDFFyUaMHGz2v9BoBSr0=</pdi_hash>
</sip>
Below is my XSLT
<?xml version="1.0"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="urn:x-emc:ia:schema:sip:1.0" xmlns:exsl="http://exslt.org/common"
xmlns:emc="urn:x-emc:ia:schema:sip:1.0"
extension-element-prefixes="exsl" xmlns:f="Functions" exclude-result-prefixes="emc xs xsl f">
<!-- template to copy elements -->
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:apply-templates select="node()" />
</xsl:element>
</xsl:template>
<!-- template to copy attributes -->
<xsl:template match="#*">
<xsl:attribute name="{local-name()}">
<xsl:value-of select="." />
</xsl:attribute>
</xsl:template>
<xsl:template match="emc:base_retention_date">
<base_retention_date>2016-06-30</base_retention_date>
</xsl:template>
</xsl:stylesheet>
In the output xml the attributes of below elements are not getting copied
<pdi_hash algorithm="SHA-256" encoding="base64">iLrzH22nT7Nr258E/oBve+dFDFFyUaMHGz2v9BoBSr0=</pdi_hash>
What am I missing?
The attributes aren't being copied because you aren't copying them.
If you want to copy attributes using a template rule with match="#*" then you need to ensure that the template is actually invoked, by doing <xsl:apply-templates select="#*"/>.

How to iterate each xml tag having different names and values ? How to start iterating from a particular tag ? in xslt 2.0

I have the below xml text,
<SUBSCRIBER>
<Anumber>639081000000</Anumber>
<FirstCallDate>20130430104419</FirstCallDate>
<SetyCode>TNT</SetyCode>
<Status>ACT</Status>
<RoamIndicator/>
<PreloadCode>P1</PreloadCode>
<CreationDate>20130116100037</CreationDate>
<PreActiveEndDate/>
<ActiveEndDate>20130804210541</ActiveEndDate>
<GraceEndDate>20140502210541</GraceEndDate>
<RetailerIndicator/>
<OnPeakAccountID>9100</OnPeakAccountID>
<OnPeakSmsExpDate>20130804210504</OnPeakSmsExpDate>
<UnivWalletAcc/>
<UnliSmsOnCtl>20130606211359</UnliSmsOnCtl>
<UnliSmsTriCtl/>
<UnliSmsGblCtl/>
<UnliMocOnCtl>20130606211359</UnliMocOnCtl>
<UnliMocTriCtl/>
<UnliMocGblCtl/>
<UnliSurfFbcCtl>20130606212353</UnliSurfFbcCtl>
</SUBSCRIBER>
How can I iterate/parse over each xml tag name and get the value ( I need the name of the tag and value in a different variables) ? And also, How can I start iterating from particular tag name ? Ex: I would like to start iterating UnivWalletAcc
Please advise.
So far, I have tried the following,
<xsl:template match="SUBSCRIBER">
<xsl:variable name="tagName">
<xsl:value-of select="/*/*/name()"/>
</xsl:variable>
<xsl:variable name="tagValue">
<xsl:value-of select="/*/*/text()"/>
</xsl:variable>
<xsl:value-of select="$tagName"/>
<xsl:value-of select="$tagValue"/>
</xsl:template>
As an alternative to Veenstra's solution, instead of the xsl:if in the SUBSCRIBER/* template, you can control the iteration in the apply-templates:
<xsl:template match="SUBSCRIBER">
<data>
<xsl:apply-templates select="UnivWalletAcc,
UnivWalletAcc/following-sibling::*" />
</data>
</xsl:template>
With the following XSLT you can loop through all childs of the node SUBSCRIBER:
<?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" version="1.0" encoding="UTF-8" indent="yes"/>
<!-- Identity template that will loop over all nodes and attributes -->
<xsl:template match="#*|node()">
<xsl:apply-templates select="#*|node()" />
</xsl:template>
<!-- Template to match the root and create new root -->
<xsl:template match="SUBSCRIBER">
<data>
<xsl:apply-templates select="#*|node()" />
</data>
</xsl:template>
<!-- Template to loop over all childs of SUBSCRIBER node -->
<xsl:template match="SUBSCRIBER/*">
<!-- This will test if we have seen the UnivWalletAcc node already, if so, output something, otherwise, output nothing -->
<xsl:if test="preceding-sibling::UnivWalletAcc or self::UnivWalletAcc">
<tag>
<tagName><xsl:value-of select="name()" /></tagName>
<tagValue><xsl:value-of select="." /></tagValue>
</tag>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

Selectively copy and update xml nodes using XSLT

I'm working with an xml that I need to copy and update to pass on for further processing. The issue I'm having is that I have not figured out an efficient method to do this. Essentially, I want to update some data, conditionally, then copy all the nodes that were not updated. Why this is challenging is due to the volume and variance in the number and name of nodes to be copied. I also want to NOT copy nodes that have no text value. Here is an example:
INPUT XML
<root>
<PersonProfile xmlns:'namespace'>
<ID>0001</ID>
<Name>
<FirstName>Jonathan</FirstName>
<PreferredName>John</PreferredName>
<MiddleName>A</MiddleName>
<LastName>Doe</LastName>
</Name>
<Country>US</Country>
<Biirthdate>01-01-1980</Birthdate>
<BirthPlace>
<City>Townsville</City>
<State>OR</State>
<Country>US</Country>
</Birthplace>
<Gender>Male</Gender>
<HomeState>OR</HomeState>
...
<nodeN>text</nodeN>
</PersonProfile>
</root>
The "PersonProfile" node is just one of several node sets within the "root" element, each with their own subset of data. Such as mailing address, emergency contact info, etc. What I am attempting to do is update nodes if the variable has a new value for them then copy all the nodes that were not updated.
Here is my current XSLT
<xsl:stylesheet version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:variable name='updateData' select='document("report")'/>
<!-- Identity Transform -->
<xsl:template match='#* | node()'>
<xsl:if test'. != ""'>
<xsl:copy>
<xsl:apply-templates select='#* | node()'/>
</xsl:copy>
</xsl:if>
</xsl:template>
<!-- Template to update Person Profile -->
<xsl:template match='PersonProfile'>
<xsl:copy>
<xsl:apply-templates select='*'/>
<xsl:element name='Name'>
<xsl:if test='exists($updateData/Preferred)'>
<xsl:element name='FirstName'>
<xsl:value-of select='$reportData/FirstName'/>
</xsl:element>
</xsl:if>
<xsl:if test='exists($updateData/Preferred)'>
<xsl:element name='PreferredName'>
<xsl:value-of select='$updateData/Preferred'/>
</xsl:element>
</xsl:if>
<xsl:if test='exists($updateData/Middle)'>
<xsl:element name='MiddleName'>
<xsl:value-of select='$updateData/Middle'/>
</xsl:element>
</xsl:if>
<xsl:if test='exists($updateData/LastName)'>
<xsl:element name='LastName'>
<xsl:value-of select='$updateData/wd:LastName'/>
</xsl:element>
</xsl:if>
</xsl:element>
<xsl:if test='exists($updateData/Country)'>
<xsl:element name='Country'>
<xsl:value-of select='$updateData/Country'/>
</xsl:element>
</xsl:if>
....
<!-- follows same structure until end of template -->
</xsl:copy>
</xsl:template>
<!-- More Templates to Update other Node sets -->
</xsl:stylesheet>
What's happening right now, is that it's copying ALL the nodes and then adding the updates values. Using Saxon-PE 9.3.0.5, I'll get an output similar to this:
Sample Output
<root>
<PersonProfile xmlns:'namespace'>
<ID>0001</ID>
<Name>
<FirstName>Jonathan</FirstName>
<PreferredName>John</PreferredName>
<MiddleName>A</MiddleName>
<LastName>Doe</LastName>
</Name>
<Country>US</Country>
<Biirthdate>01-01-1980</Birthdate>
<BirthPlace>
<City>Townsville</City>
<State>OR</State>
<Country>US</Country>
</Birthplace>
<Gender>Male</Gender>
<HomeState>OR</HomeState>
...
<nodeN>text</nodeN>
<PreferredName>Jonathan</PreferredName>
<HomeState>WA</HomeState>
</PersonProfile>
</root>
I realize this is happening because I am applying the templates to all the nodes in PersonProfile and that I could specify which nodes to exclude, but I feel like this is a very poor solution as the volume of nodes could be upwards of 30 or more and that would require a written value for each one. I trust XML has a more elegant solution than to explicitly list each of these nodes. I would like to have an out like this:
Desired Output
<root>
<PersonProfile xmlns:'namespace'>
<ID>0001</ID>
<Name>
<FirstName>Jonathan</FirstName>
<PreferredName>Jonathan</PreferredName>
<MiddleName>A</MiddleName>
<LastName>Doe</LastName>
</Name>
<Country>US</Country>
<Biirthdate>01-01-1980</Birthdate>
<BirthPlace>
<City>Townsville</City>
<State>OR</State>
<Country>US</Country>
</Birthplace>
<Gender>Male</Gender>
<HomeState>WA</HomeState>
...
<nodeN>text</nodeN>
</PersonProfile>
</root>
If anyone could help me create a template structure that would work for the xml structure, I would GREATLY appreciate it. It would need to be "reusable" as there are similar node structures like Person Profile I would have to apply it to, but with different node names and number of elements, etc.
Thanks in advance for any help!
J
This should work for your original question:
<xsl:stylesheet version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:variable name='updateData' select='document("report")'/>
<!-- Identity Transform -->
<xsl:template match='#* | node()' name='copy'>
<xsl:copy>
<xsl:apply-templates select='#* | node()'/>
</xsl:copy>
</xsl:template>
<xsl:template match='*[not(*)]'>
<xsl:variable name='matchingValue'
select='$updateData/*[name() = name(current())]'/>
<xsl:choose>
<xsl:when test='$matchingValue'>
<xsl:copy>
<xsl:apply-templates select='#*' />
<xsl:value-of select='$matchingValue'/>
</xsl:copy>
</xsl:when>
<xsl:when test='normalize-space()'>
<xsl:call-template name='copy' />
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
As far as inserting new elements that are not present in the source XML, that's trickier. Could you open a separate question for that? I may have some ideas for how to approach that.

Setting disable-output-escaping="yes" for every xsl:text tag in the xml

say I have the following xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/*">
<display>
<xsl:for-each select="logline_t">
<xsl:text disable-output-escaping="yes"><</xsl:text> <xsl:value-of select="./line_1" <xsl:text disable-output-escaping="yes">></xsl:text>
<xsl:text disable-output-escaping="yes"><</xsl:text> <xsl:value-of select="./line_2" <xsl:text disable-output-escaping="yes">></xsl:text>
<xsl:text disable-output-escaping="yes"><</xsl:text> <xsl:value-of select="./line_3" <xsl:text disable-output-escaping="yes">></xsl:text>
</xsl:for-each>
</display>
</xsl:template>
</xsl:stylesheet>
Is there a way to set disable-output-escaping="yes" to all of the xsl:text that appear in the document?
I know there is an option to put
< xsl:output method="text"/ >
and every time something like
& lt;
appears, a < will appear, but the thing is that sometimes in the values of line_1, line_2 or line_3, there is a "$lt;" that I don't want changed (this is, I only need whatever is between to be changed)
This is what I'm trying to accomplish. I have this xml:
<readlog_l>
<logline_t>
<hora>16:01:09</hora>
<texto>Call-ID: 663903<hola>396#127.0.0.1</texto>
</logline_t>
</readlog_l>
And this translation:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/*">
<display>
<screen name="<xsl:value-of select="name(.)"/>">
<xsl:for-each select="logline_t">
< field name="<xsl:for-each select="*"><xsl:value-of select="."/></xsl:for-each>" value="" type="label"/>
</xsl:for-each>
</screen>
</display>
</xsl:template>
</xsl:stylesheet>
I want this to be the output:
<?xml version="1.0"?>
<display>
<screen name="readlog_l">
<field name="16:01:09 Call-ID: 663903<hola>396#127.0.0.1 " value="" type="label">
</screen>
</display>
Note that I need the "<" inside the field name not to be escaped, this is why I can't use output method text.
Also, note that this is an example and the translations are much bigger, so this is why I'm trying to find out how not to write disable-output-escaping for every '<' or '>' I need.
Thanks!
Thanks for clarifying the question. In this case, I'm fairly sure there's no need to disable output escaping. XSLT was designed to accomplish what you're doing:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/*">
<display>
<screen name="{name(.)}">
<xsl:for-each select="logline_t">
<xsl:variable name="nameContent">
<xsl:for-each select="*">
<xsl:if test="position() > 1"><xsl:text> </xsl:text></xsl:if>
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:variable>
<field name="{$nameContent}" value="" type="label" />
</xsl:for-each>
</screen>
</display>
</xsl:template>
</xsl:stylesheet>
I'm a bit unclear on this point:
Note that I need the "<" inside the field name not to be escaped, this is why I can't use output method text.
Which < are you referring to? Is it the < and > around "hola"? If you left those unescaped you would wind up with invalid XML. It also looks like the name attribute in your sample output have a lot of values that aren't in the input XML. Where did those come from?
Given your expected output you don't need d-o-e at all for this. Here is a possible solution that doesn't use d-o-e, and is based on templates rather than for-each:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/*">
<display>
<screen name="{name(.)}">
<xsl:apply-templates select="logline_t"/>
</screen>
</display>
</xsl:template>
<xsl:template match="logline_t">
<field value="" type="label">
<xsl:attribute name="name">
<xsl:apply-templates select="*" mode="fieldvalue"/>
</xsl:attribute>
</field>
</xsl:template>
<xsl:template match="*[last()]" mode="fieldvalue">
<xsl:value-of select="." />
</xsl:template>
<xsl:template match="*" mode="fieldvalue">
<xsl:value-of select="." />
<xsl:text> </xsl:text>
</xsl:template>
</xsl:stylesheet>
If you want to set d-o-e on everything, that suggests you are trying to generate markup "by hand". I don't think that's a particularly good idea (in fact, I think it's a lousy idea), but if it's what you want to do, I would suggest using the text output method instead of the xml output method. That way, no escaping of special characters takes place, and therefore it doesn't need to be disabled.