Copy/paste value of preceding element using XLST - xslt

I have the following XML:
<?xml version="1.0"?>
<mysqldump xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<database name="ims_prod">
<table_data name="wp_trp_dictionary_en_us_de_de">
<row>
<field name="id">1</field>
<field name="original">Random text 1</field>
<field name="translated"></field>
<field name="status">0</field>
<field name="block_type">0</field>
</row>
<row>
<field name="id">2</field>
<field name="original">Random text 2</field>
<field name="translated"></field>
<field name="status">0</field>
<field name="block_type">0</field>
</row>
</table_data>
</database>
</mysqldump>
I need to copy the value of every node that has "original" attribute and paste it into the following sibling (sibling that has "translated" attribute.
Expected:
<?xml version="1.0"?>
<mysqldump xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<database name="ims_prod">
<table_data name="wp_trp_dictionary_en_us_de_de">
<row>
<field name="id">1</field>
<field name="original">Random text 1</field>
<field name="translated">**Random text 1**</field>
<field name="status">0</field>
<field name="block_type">0</field>
</row>
<row>
<field name="id">2</field>
<field name="original">Random text 2</field>
<field name="translated">**Random text 2**</field>
<field name="status">0</field>
<field name="block_type">0</field>
</row>
</table_data>
</database>
</mysqldump>
I've tried the following XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:com="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//table_data/row/field[#name='original']/text()">
<xsl:value-of select="//table_data/row/field[#name='original']/following-sibling::field[#name='translated']"/>
</xsl:template>
</xsl:stylesheet>
But it blanks out all nodes with "original" attributes instead.
What am I doing wrong? Thank you for your help!

You want to change the value of the translated field, so you need to make your template match that. And if the field is empty, then matching its text node will not work.
Try instead:
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="field[#name='translated']">
<field name="translated">
<xsl:value-of select="preceding-sibling::field[#name='original']"/>
</field>
</xsl:template>
</xsl:stylesheet>
P.S. Note the use of relative path in select="preceding-sibling::field[#name='original']". Your version would always select the first row's original.

You can use these templates to get your desired outcome:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:com="http://www.w3.org/2001/XMLSchema-instance">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="table_data/row/field[#name='translated']">
<xsl:copy>
<xsl:copy-of select="#*" />
<xsl:value-of select="concat('**',preceding-sibling::field[1]/text(),'**')"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The output is as expected.

Related

Copy direct children and their attributes only in XSLT 1.0?

Input XML:
<root>
<recordList>
<record priref="1">
<Group attr="val">
<Field1>Value X</Field1>
<Field2>
<value lang="en-US">Foo</value>
<value lang="de-DE">Bar</value>
</Field2>
</Group>
<Field3 attr="val">Value Y</Field3>
</record>
<record priref="2">
<Field3 attr="val">Value Z</Field3>
</record>
</recordList>
</root>
Desired output (kind of a "shallow copy" with only the immediate child elements and attributes):
<root>
<record priref="1">
<Group attr="val" />
<Field3 attr="val">Value Y</Field3>
</record>
<record priref="2">
<Field3 attr="val">Value Z</Field3>
</record>
</root>
Is there another way (e.g. without for-each) to achieve this?
<xsl:template match="/">
<root>
<xsl:apply-templates select="root/recordList/record" />
</root>
</xsl:template>
<xsl:template match="record">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:for-each select="*">
<xsl:copy>
<xsl:copy-of select="#* | text()"/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
edit: the text nodes "Value Y" and "Value Z" are actually supposed to be in the result. "Foo" and "Bar" are still not desired anywhere in the result.
How about simply not apply templates to nodes you don't want?
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="recordList">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="record/*">
<xsl:copy>
<xsl:apply-templates select="#*|text()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
One way is to not process any grandchildren:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="recordList">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="record/*/node()"/>
</xsl:transform>
A very short template uses the parent::-axis:
<xsl:template match="root|record|*[parent::record]|*[parent::record]/text()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="text()" />
It's output is
<?xml version="1.0"?>
<root>
<record priref="1">
<Group attr="val">
</Group>
<Field3 attr="val">Value Y</Field3></record>
<record priref="2">
<Field3 attr="val">Value Z</Field3>
</record>
</root>

XSL group by node value

I have a case of XSL key grouping. The goal is to replace a value based on an ID match.
Input:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<message-in>
<actions>
<stop>
<action id="33632">
<text>DefaultComment</text>
<planning-status>finished</planning-status>
<realised-times>
<starttime>2017-03-13T12:43:54</starttime>
<finishtime>2017-03-13T13:15:21</finishtime>
</realised-times>
</action>
</stop>
<stop>
<action id="33635">
<planning-status>started</planning-status>
<realised-times>
<starttime>2017-03-13T13:15:21</starttime>
</realised-times>
</action>
</stop>
</actions>
</message-in>
<output_getquerydata>
<queries>
<query name="fat">
<parameters>
<parameter name="id">33632</parameter>
</parameters>
<queryErrors/>
<queryResults>
<record id="1">
<column name="interfaceAction_externalId">33633OREA</column>
</record>
</queryResults>
</query>
</queries>
</output_getquerydata>
<output_getquerydata>
<queries>
<query name="fat">
<parameters>
<parameter name="id">33635</parameter>
</parameters>
<queryErrors/>
<queryResults>
<record id="1">
<column name="interfaceAction_externalId">536313OREA</column>
</record>
</queryResults>
</query>
</queries>
</output_getquerydata>
</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" indent="yes" omit-xml-declaration="no" encoding="utf-8"/>
<xsl:strip-space elements="*"/>
<xsl:key name="actionKey" match="stop" use="action/#id"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<root>
<xsl:copy-of select="//message-in"/>
</root>
</xsl:template>
<xsl:template match="action/#id">
<xsl:attribute name="id"><xsl:value-of select="substring-before(//output_getquerydata/queries/query/parameters/parameter[key('actionKey', #id)]/queryResults/record/column[#name='interfaceAction_externalId'],'OREA')"/></xsl:attribute>
</xsl:template>
</xsl:stylesheet>
I have multiple "action" nodes that have matching "query" nodes.
The goal is that for every action ID we need to replace the ID value from the "action" tag with the corresponding "interfaceAction_externalId" value.
So for action ID 33632 we'll copy and replace with the value of "33633" (because we have a match in parameneter/name/#id 33632 = action/#id ).
The copy works well I get all the information I need, but it seems that action/#id doesn't get replaced.
I thought that I'll use a key to save the values of action/#id then use that in the template match to replace with the value from interfaceAction_externalId, but it doesn't work and I'm not sure what I'm doing wrong.
Thanks for your help!
The whole approach only makes sense if you use <xsl:apply-templates select="//message-in"/> instead of <xsl:copy-of select="//message-in"/>.
As for using a key, I think you want
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="no" encoding="utf-8"/>
<xsl:strip-space elements="*"/>
<xsl:key name="ref-query"
match="output_getquerydata/queries/query/queryResults/record/column[#name='interfaceAction_externalId']"
use="ancestor::query/parameters/parameter[#name = 'id']"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<root>
<xsl:apply-templates select="//message-in"/>
</root>
</xsl:template>
<xsl:template match="action/#id">
<xsl:attribute name="id"><xsl:value-of select="substring-before(key('ref-query', .),'OREA')"/></xsl:attribute>
</xsl:template>
</xsl:stylesheet>

XSLT - Remove empty tag after transformation

I am trying to remove all empty elements after I have finished the transformation however I am just not coming right.
I have the following XML
<root>
<record name='1'>
<Child1>value1</Child1>
<Child2>value2</Child2>
</record>
<record name='2'>
<Child1>value1</Child1>
<Child2>value2</Child2>
</record>
<record name='3'>
<Child1>value1</Child1>
<Child2>value2</Child2>
</record>
</root>
and I want the output to be
<root>
<record name="1">
<Element>1</Element>
</record>
</root>
however I keep getting all the empty record elements as well and I can't figure out how to get rid of them.
<root>
<record>
<Element>1</Element>
</record>
<record/>
<record/>
</root>
This is my stylesheet
<xsl:stylesheet version="2.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="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//record">
<xsl:copy>
<xsl:call-template name="SimpleNode"/>
</xsl:copy>
</xsl:template>
<xsl:template name="SimpleNode">
<xsl:if test="#name = '1'">
<Element><xsl:value-of select="#name"/></Element>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
I would rewrite your XSLT a little to match record elements differently depending on the value of their #name attribute.
The following XSLT stylesheet:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- Only produce output for record elements where the name attribute is 1. -->
<xsl:template match="record[#name='1']">
<xsl:copy>
<element>
<xsl:value-of select="#name"/>
</element>
</xsl:copy>
</xsl:template>
<!-- For every other record attribute, output nothing. -->
<xsl:template match="record"/>
</xsl:stylesheet>
produces the following output when applied to your example input XML:
<root>
<record>
<element>1</element>
</record>
</root>

how to remove unwanted soap envelop attribute tag value using XSLT transform

I have soap envelop containing xml. I want to remove that tag value. I am using saxon9 parser. my input xml is:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org /soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body><ns1:getDocumentByKeyResponse soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:ns1="http://www.taleo.com/ws/integration/toolkit/2005/07">
<Document xmlns="http://www.taleo.com/ws/integration/toolkit/2005/07">
<Attributes><Attribute name="duration">0:00:13.629</Attribute><Attribute name="count">121</Attribute><Attribute name="entity">Requisition</Attribute>
<Attribute name="mode">XML</Attribute>
<Attribute name="version">http://www.taleo.com/ws/tee800/2009/01</Attribute>
</Attributes><Content>
<ExportXML xmlns="http://www.taleo.com/ws/integration/toolkit/2005/07">
<record>
<field name="JobAction">2</field>
<field name="JobType">false</field>
<field name="JobPositionPostingID">000065</field>
<field name="JobFunctionCode">ADMINISTRATION</field>
</record>
</ExportXML></Content></Document></ns1:getDocumentByKeyResponse>
</soapenv:Body> </soapenv:Envelope>
my xsl file is like this
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" encoding="utf-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="*:field">
<xsl:element name="{lower-case(#name)}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="*:ExportXML">
<JobPositionPostings>
<xsl:apply-templates/>
</JobPositionPostings>
</xsl:template>
<xsl:template match="*:record">
<JobPositionPosting>
<xsl:apply-templates select="*:field[starts-with(#name,'JobAction')]"/>
<HiringOrg>
<xsl:apply-templates select="*:field[starts-with(#name,'JobType')]"/>
<Industry>
<xsl:apply-templates select="*:field[starts-with(#name,'JobPositionPostingID')]"/>
</Industry>
</HiringOrg>
</JobPositionPosting>
<xsl:apply-templates select="*:field[starts-with(#name,'JobFeedResponseEmail')]"/>
</xsl:template>
<xsl:template match="*:field[#name='TypeName']"/>
<xsl:template match="*:field[#name='TypeName']" mode="title">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
I am getting output like this
<?xml version="1.0" encoding="utf-8"?>**0:00:13.629121RequisitionXMLhttp://www.taleo.com/ws/tee800/2009/01**<JobPositionPostings xmlns:soap="http://www.taleo.com/ws/integration/toolkit/2005/07">
<JobPositionPosting>
<jobaction>2</jobaction>
<jobtype>false</jobtype>
<jobpositionpostingid>000065</jobpositionpostingid>
<HiringOrg>
after xml tag it is picking all attribute tag value which I don't want.
If you add a empty template like this one
<xsl:template match="*:Attributes"/>
(Notice the slash at end)
All Attributes elements will be ignored.
Since you don't seem to care about anything outside the <Content> you could add an extra template to jump straight to that and ignore the rest:
<xsl:template match="/">
<xsl:apply-templates select="descendant::*:Content[1]" />
</xsl:template>

xpath get matches in sublist

I've got the following xml:
<vo class="GroupEntry" buildByAlias="true">
<objectClass name="groupOfNames"/>
<field name="commonName" nameLDAP="cn" type="String"/>
<field name="descriptione" nameLDAP="description" type="String"/>
<field name="member" nameLDAP="member" type="String[]"/>
</vo>
<update method="addMember" modificationMode="ADD_ATTRIBUTE">
<input>
<field name="member"/>
<field name="description"/>
</input>
</update>
I'm using XSLT to transform it, and I'm need, for each update, to get the fields in the vo that correspond to the field defined in the input. It would be something like this:
<xsl:variable name="fields" select="vo/field" />
<xsl:for-each select="update">
<xsl:variable name='fieldsForInput' select = "$fields[#name]=input/fields[#name]"/>
<xsl:for-each select="$fieldsForInput">
<xsl:value-of select="#type"/> <xsl:value-of select="#name"/>
<xsl:for-each>
</xsl:for-each>
But it doesn't found anything. Any ideas?
Thanks
JL
From the shown fragments it's difficult helping you and understadning what you want. However your case seems perfect for using xsl:key.
For example, if you create a key at the beginning of the transform like this:
<xsl:key name="fields" match="vo/field" use="#name"/>
You can use it inside your matching template as follows:
<xsl:for-each select="update/input">
<xsl:copy-of select="key('fields',current()/field/#name)"/>
</xsl:for-each>
I would not use a xsl:foreach anyway. But it's hard to give you a complete solution if you provide only fragments. Also is not clear if you want just match or replace field.
Example showing how to get the field name/type for each update/input/field.
XSLT 1.0 tested with Saxon 6.5.5
<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:key name="fields" match="vo/field" use="#name"/>
<xsl:template match="/root">
<xsl:apply-templates select="update"/>
</xsl:template>
<xsl:template match="update">
<xsl:value-of select="concat('-',#method,'
')"/>
<xsl:apply-templates select="input/field"/>
</xsl:template>
<xsl:template match="input/field">
<xsl:value-of select="concat('--',#name,' ',key('fields',#name)/#type,'
')"/>
</xsl:template>
</xsl:stylesheet>
Applied on:
<root>
<vo class="GroupEntry" buildByAlias="true">
<objectClass name="groupOfNames"/>
<field name="commonName" nameLDAP="cn" type="String"/>
<field name="description" nameLDAP="description" type="String"/>
<field name="member" nameLDAP="member" type="String[]"/>
</vo>
<update method="addMember" modificationMode="ADD_ATTRIBUTE">
<input>
<field name="member"/>
<field name="description"/>
</input>
</update>
<update method="deleteMember" modificationMode="DELETE_ATTRIBUTE">
<input>
<field name="member"/>
<field name="description"/>
</input>
</update>
</root>
Produces:
-addMember
--member String[]
--description String
-deleteMember
--member String[]
--description String
Two solutions:
Solution1 (no keys):
<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=
"vo/field[#name=../../update/*/*/#name]">
<xsl:value-of select="concat(#name,' ',#type,'
')"/>
</xsl:template>
</xsl:stylesheet>
When applied on the provided XML document (corrected to be made well-formed):
<t>
<vo class="GroupEntry" buildByAlias="true">
<objectClass name="groupOfNames"/>
<field name="commonName" nameLDAP="cn" type="String"/>
<field name="description" nameLDAP="description" type="String"/>
<field name="member" nameLDAP="member" type="String[]"/>
</vo>
<update method="addMember" modificationMode="ADD_ATTRIBUTE">
<input>
<field name="member"/>
<field name="description"/>
</input>
</update>
</t>
the wanted, correct result is produced:
description String
member String[]
Solution2 (using a key):
<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:key name="kFieldByName" match="vo/field"
use="#name"/>
<xsl:template match="/*">
<xsl:apply-templates mode="selected" select=
"key('kFieldByName', update/*/*/#name)"/>
</xsl:template>
<xsl:template match="vo/field" mode="selected">
<xsl:value-of select="concat(#name,' ',#type,'
')"/>
</xsl:template>
</xsl:stylesheet>
when applied on the same XML document (above), the same correct result is produced:
description String
member String[]