Copy & rename element using XSL - xslt

I have an XML that goes like this:
<tu>
<prop type="x-idiom-source-ipath">DOMAIN/SUBDOMAIN/FILENAME</prop>
<tuv xml:lang="IT-IT">
<seg>Prova</seg>
</tuv>
<tuv xml:lang="EN-GB">
<seg>Test</seg>
</tuv>
</tu>
I'd like to copy the prop element twice and rename it:
<tu>
<prop type="domain">DOMAIN/SUBDOMAIN/FILENAME</prop>
<prop type="subdomain">DOMAIN/SUBDOMAIN/FILENAME</prop>
<prop type="filename">DOMAIN/SUBDOMAIN/FILENAME</prop>
<tuv xml:lang="IT-IT">
<seg>Prova</seg>
</tuv>
<tuv xml:lang="EN-GB">
<seg>Test</seg>
</tuv>
</tu>
How would I do that? I don't understand how to copy and duplicate the element, I'm not competent enough.
Thank you

Well, you could do simply:
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="prop">
<prop type="domain">
<xsl:value-of select="." />
</prop>
<prop type="subdomain">
<xsl:value-of select="." />
</prop>
<prop type="filename">
<xsl:value-of select="." />
</prop>
</xsl:template>
</xsl:stylesheet>

Related

Insert list of elements inside XML tags

I am trying to obtain a list of all the elements with values that aren't in the (Line 1, Line2), and then insert them into the tags similar to the test.
Right now I can retrieve all the elements, but I'm having trouble restricting this to just my desired values. And then I'm unsure how to match and do a for each on elements outside my match criteria. Any advice would be greatly appreciated!
Given the Following XML:
<?xml version="1.0" encoding="UTF-8"?>
<Request>
<Header>
<Line1>Element1</Line1>
<Line2>Element2</Line2>
</Header>
<ElementControl>
<Update>
<Element>test</Element>
</Update>
</ElementControl>
<Member>
<Identifier>123456789</Identifier>
<Contact>
<Person>
<Gender>MALE</Gender>
<Title>Mr</Title>
<Name>JOHN DOE</Name>
</Person>
<HomePhone/>
<eMailAddress/>
<ContactAddresses>
<Address>
<AddressType>POS</AddressType>
<Line1>100 Fake Street</Line1>
<Line2/>
<Line3/>
<Line4/>
<Suburb>Jupiter</Suburb>
<State>OTH</State>
<PostCode>9999</PostCode>
<Country>AUS</Country>
</Address>
</ContactAddresses>
</Contact>
</Member>
</Request>
Current XSL for getting elements
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()">
<xsl:for-each select="node()[text() != '']">
<xsl:value-of select="local-name()"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
<xsl:apply-templates select="node()"/>
</xsl:template>
</xsl:stylesheet>
My WIP xml for inserting the result xml tags is below. I'm unsure how to insert the results of the above xsl into this,
<xsl:template match="Element">
<xsl:copy-of select="."/>
<Element>Value1</Element>
</xsl:template>
And ultimate desired output:
<?xml version="1.0" encoding="UTF-8"?>
<Request>
<Header>
<Line1>Element1</Line1>
<Line2>Element2</Line2>
</Header>
<ElementControl>
<Update>
<Element>Identifier</Element>
<Element>Gender</Element>
<Element>Title</Element>
<Element>Name</Element>
<Element>AddressType</Element>
<Element>Line1</Element>
<Element>Suburb</Element>
<Element>State</Element>
<Element>PostCode</Element>
<Element>Country</Element>
</Update>
</ElementControl>
<Member>
<Identifier>123456789</Identifier>
<Contact>
<Person>
<Gender>MALE</Gender>
<Title>Mr</Title>
<Name>JOHN DOE</Name>
</Person>
<HomePhone/>
<eMailAddress/>
<ContactAddresses>
<Address>
<AddressType>POS</AddressType>
<Line1>100 Fake Street</Line1>
<Line2/>
<Line3/>
<Line4/>
<Suburb>Jupiter</Suburb>
<State>OTH</State>
<PostCode>9999</PostCode>
<Country>AUS</Country>
</Address>
</ContactAddresses>
</Contact>
</Member>
</Request>
I would change the current template to use mode attribute, so it is only used in specific cases, rather than matching all elements. You should also change it to output elements, not text, like so:
<xsl:template match="node()" mode="copy">
<xsl:for-each select=".//node()[text() != '']">
<Element>
<xsl:value-of select="local-name()"/>
</Element>
</xsl:for-each>
</xsl:template>
Then you can call it like this....
<xsl:template match="ElementControl/Update">
<xsl:apply-templates select="../../Member" mode="copy" />
</xsl:template>
Try this XSLT. Note the use of the identity template to copy all other existing elements unchanged
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()" mode="copy">
<xsl:for-each select=".//node()[text() != '']">
<Element>
<xsl:value-of select="local-name()"/>
</Element>
</xsl:for-each>
</xsl:template>
<xsl:template match="ElementControl/Update">
<xsl:apply-templates select="../../Member" mode="copy" />
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Numbering attributes of elements embedded in free text

I have the following XML, and would like to remove all ele tags, before numbering the x attribute of the ph elements that reside within some of these ele tags:
<?xml version="1.0" encoding="UTF-8"?>
<tmx version="1.4">
<body>
<tu>
<prop type="x-Context">-2050338055591740051, -2050338055591740051</prop>
<prop type="x-Origin">TM</prop>
<prop type="x-ConfirmationLevel">Translated</prop>
<tuv>
<seg>
<ele>The text </ele>
<ele>
<ph x="0" type="QIAsymphony"/>
</ele>
<ele> goes </ele>
<ele>
<ph x="0" type="470"/>
</ele>
<ele> here </ele>
<ele>
<ph x="0" type="471"/>
</ele>
<ele>.</ele>
</seg>
</tuv>
<tuv>
<seg>
<ele>El texto </ele>
<ele>
<ph x="0" type="QIAsymphony"/>
</ele>
<ele> se mete </ele>
<ele>
<ph x="0" type="471"/>
</ele>
<ele> aquí </ele>
<ele>
<ph x="0" type="470"/>
</ele>
<ele>.</ele>
</seg>
</tuv>
</tu>
</body>
</tmx>
I have the following XSLT, that performs the numbering operation, although I'm not sure what changes to make to replace the missing ele tags, once this element has been deleted from the XML:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes" />
<xsl:key name="ph" match="tuv[1]/seg/ele/ph" use="#type" />
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="tuv/seg/ele/ph/#x">
<xsl:attribute name="x">
<xsl:value-of select="count(key('ph', ../#type)/../preceding-sibling::ele[ph]) + 1" />
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Desired output:
<tuv>
<seg>The text
<ph x="1" type="QIAsymphony"/>
goes
<ph x="3" type="471"/>
here
<ph x="2" type="470"/>
.
</seg>
</tuv>
In the comments you say you want to remove the <ele> elements, but not their contents.
Because your XSLT is already based on the identity transform, that's easily done by adding a very simple template:
<xsl:template match="ele">
<xsl:apply-templates select="#*|node()" />
</xsl:template>
This template simply reads as: "When encountering <ele>, do not output anything but process its children". In effect <ele> is removed from the output, its children remain.
In context:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes" />
<xsl:key name="ph" match="tuv[1]/seg/ele/ph" use="#type" />
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="ele">
<xsl:apply-templates select="#*|node()" />
</xsl:template>
<xsl:template match="tuv/seg/ele/ph/#x">
<xsl:attribute name="x">
<xsl:value-of select="count(key('ph', ../#type)/../preceding-sibling::ele[ph]) + 1" />
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Result:
<tmx version="1.4">
<body>
<tu>
<prop type="x-Context">-2050338055591740051, -2050338055591740051</prop>
<prop type="x-Origin">TM</prop>
<prop type="x-ConfirmationLevel">Translated</prop>
<tuv>
<seg>The text <ph x="1" type="QIAsymphony"/> goes <ph x="2" type="470"/> here <ph x="3" type="471"/>.</seg>
</tuv>
<tuv>
<seg>El texto <ph x="1" type="QIAsymphony"/> se mete <ph x="3" type="471"/> aquí <ph x="2" type="470"/>.</seg>
</tuv>
</tu>
</body>
</tmx>

Convert string value as XML tag name

Below is my requirement. Can we do this using XSLT? I want to convert value of AttributeName as tag under policy and corresponding AttributeValue as value.
Input :
<Policy>
<Attributes>
<AttributeName>is_policy_loan</AttributeName>
<AttributeValue>Yes</AttributeValue>
</Attributes>
<Attributes>
<AttributeName>is_policy_owners</AttributeName>
<AttributeValue>Yes</AttributeValue>
</Attributes>
<Attributes>
<AttributeName>is_policy_twoyears</AttributeName>
<AttributeValue>Yes</AttributeValue>
</Attributes>
</Policy>
Output :
<Policy>
<is_policy_loan>Yes</is_policy_loan>
<is_policy_owners>Yes</is_policy_owners>
<is_policy_twoyears>Yes</is_policy_twoyears>
</Policy>
The following xsl file will do the job:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- create the <AttributeName>AttributeValue</..> nodes -->
<xsl:template match="//Attributes">
<xsl:variable name="name" select="AttributeName" />
<xsl:element name="{$name}">
<xsl:value-of select="AttributeValue" />
</xsl:element>
</xsl:template>
<!-- wrap nodes in a `Policy` node -->
<xsl:template match="/">
<Policy>
<xsl:apply-templates/>
</Policy>
</xsl:template>
</xsl:stylesheet>
The way i would do,
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" indent="yes" encoding="UTF-8" omit-xml-declaration="yes" />
<xsl:template match="Policy">
<xsl:element name="Policy">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="Attributes">
<xsl:variable name="name" select="AttributeName" />
<xsl:element name="{$name}">
<xsl:value-of select="AttributeValue" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
output will be,
<Policy>
<is_policy_loan>Yes</is_policy_loan>
<is_policy_owners>Yes</is_policy_owners>
<is_policy_twoyears>Yes</is_policy_twoyears>
</Policy>

how to put condition on xsl:result-document output according to attribute value

i am transforming XML using Saxon parser.i have to generate same output with two place that i am able to do that.i want to put the condition like if count attribute value is 0 then it should not generate any output file. my input file is
<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:getDocumentByKeyResponsexmlns:ns1="http://www.taleo.com/ws/integration/toolkit/2005/07" soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<Document xmlns="http://www.taleo.com/ws/integration/toolkit/2005/07">
<Attributes>
<Attribute name="duration">0:00:00.789</Attribute>
<Attribute name="count">0</Attribute>
<Attribute name="entity">SourcingRequest</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"/>
</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:param name="pDest" select="'file:///c:/temp/'"/>
<xsl:param name="pDest1" select="'file:///c:/'"/>
<xsl:template match="*:field">
<xsl:element name="{lower-case(#name)}">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<xsl:template match="/">
<xsl:result-document
href="{$pDest}requsition_{format-date(current-date(),'[D01]_[M01]')}.xml">
<JobPositionPostings>
<xsl:apply-templates select="descendant::*:Content[1]" />
</JobPositionPostings>
</xsl:result-document>
<xsl:result-document
href="{$pDest1}requsition_{format-date(current-date(),'[D01]_[M01]')}.xml">
<JobPositionPostings>
<xsl:apply-templates select="descendant::*:Content[1]" />
</JobPositionPostings>
</xsl:result-document>
</xsl:template>
<xsl:template match="*:record">
<!--doing rest operation if attribute count not equal to 0 -->
</xsl:template>
</xsl:stylesheet>
I think you want to replace the template
<xsl:template match="/">
<xsl:result-document
href="{$pDest}requsition_{format-date(current-date(),'[D01]_[M01]')}.xml">
<JobPositionPostings>
<xsl:apply-templates select="descendant::*:Content[1]" />
</JobPositionPostings>
</xsl:result-document>
<xsl:result-document
href="{$pDest1}requsition_{format-date(current-date(),'[D01]_[M01]')}.xml">
<JobPositionPostings>
<xsl:apply-templates select="descendant::*:Content[1]" />
</JobPositionPostings>
</xsl:result-document>
</xsl:template>
somehow with
<xsl:template match="/">
<xsl:if test="soapenv:Envelope/soapenv:Body/ns1:getDocumentByKeyResponse/df:Document/df:Attributes/df:Attribute[#name = 'count'] != 0">
<xsl:result-document
href="{$pDest}requsition_{format-date(current-date(),'[D01]_[M01]')}.xml">
<JobPositionPostings>
<xsl:apply-templates select="descendant::*:Content[1]" />
</JobPositionPostings>
</xsl:result-document>
<xsl:result-document
href="{$pDest1}requsition_{format-date(current-date(),'[D01]_[M01]')}.xml">
<JobPositionPostings>
<xsl:apply-templates select="descendant::*:Content[1]" />
</JobPositionPostings>
</xsl:result-document>
</xsl:if>
</xsl:template>
where you additionally declare
<xsl:stylesheet xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://www.taleo.com/ws/integration/toolkit/2005/07"
xmlns:df="http://www.taleo.com/ws/integration/toolkit/2005/07"
exclude-result-prefixes="soapenv ns1 df"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
That way the two xsl:result-document instructions you have are only executed if the condition is true.

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>