I have below input, I have to update the <price> of <orderitem> with <name> = Desktop with the <price> of another <orderitem> (present anywhere in the xml) which has same value of <associationid> as its <objectid>.
Here for example, i have <associationid> as 2 for Desktop, now I look for <objectid> with value as 2 and get its price and update it here. Sample output below.
Kindly let me know the approach to solve such problems involving traversing, I am new to XSL and trying to refer the XSLT cook book and SO, but not getting proper reference. Thanks.
<listoforders>
<Orderitem>
<name>Desktop</name>
<place>NZ</place>
<price>120ass</price>
<associationid>2</associationid>
<Orderitem>
<name>Desktop2</name>
<place>NZ</place>
<price>130</price>
</Orderitem>
<Orderitem>
<name>Desktop3</name>
<place>NZ</place>
<price>130obj1</price>
<objectid>1</objectid>
<price>130</price>
</Orderitem>
</Orderitem>
<Orderitem>
<name>laptop</name>
<place>NZ</place>
<price>120</price>
<Orderitem>
<name>laptop2</name>
<place>NZ</place>
<price>130</price>
</Orderitem>
<Orderitem>
<name>laptop3</name>
<place>NZ</place>
<price>130obj2</price>
<objectid>2</objectid>
</Orderitem>
</Orderitem>
</listoforders>
Output
<listoforders>
<Orderitem>
<name>Desktop</name>
<place>NZ</place>
<price>130obj2</price>
<associationid>2</associationid>
<Orderitem>
<name>Desktop2</name>
<place>NZ</place>
<price>130</price>
</Orderitem>
<Orderitem>
<name>Desktop3</name>
<place>NZ</place>
<price>130obj1</price>
<objectid>1</objectid>
<price>130</price>
</Orderitem>
</Orderitem>
<Orderitem>
<name>laptop</name>
<place>NZ</place>
<price>120</price>
<Orderitem>
<name>laptop2</name>
<place>NZ</place>
<price>130</price>
</Orderitem>
<Orderitem>
<name>laptop3</name>
<place>NZ</place>
<price>130obj2</price>
<objectid>2</objectid>
</Orderitem>
</Orderitem>
</listoforders>
XSL:
<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="price[../name='Desktop']">
<xsl:copy-of select="price[//objectid=.//associationid]" />
</xsl:template>
</xsl:stylesheet>
It's always best to use a key to resolve cross-references:
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="*"/>
<xsl:key name="ord" match="Orderitem" use="objectid" />
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="price[../name='Desktop']">
<xsl:copy-of select="key('ord', ../associationid)/price"/>
</xsl:template>
</xsl:stylesheet>
you can use key for this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output indent="yes"/>
<xsl:key name="keyitem" match="price" use="../objectid"/>
<!-- Identity Transformation -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"></xsl:apply-templates>
</xsl:copy>
</xsl:template>
<xsl:template match="price[../name eq 'Desktop'][../associationid]">
<xsl:variable name="id" select="../associationid"/>
<xsl:copy>
<xsl:value-of select="key('keyitem',$id)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I noticed three things in your XSLT:
In the xsl:copy-of, you are currently not traversing through all the prices like you intended, for that you need to use //price.
In the condition, you want to test the element objectid that belongs to the price, so you should use ../objectid rather than //objectid.
Therefor, in the condition you implicitly "leave" the context of the desktop price element, so you no longer have direct access to .//associationid. However, you can use the current() function to refer to the object (Thanks michael-hor257k)
This XSLT produces the desired output:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<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="price[../name='Desktop']">
<xsl:copy-of select="//price[../objectid=current()/../associationid]"/>
</xsl:template>
</xsl:stylesheet>
Another possibility would be to use a variable to store the id for later comparison.
<xsl:template match="price[../name='Desktop']">
<xsl:variable name="var.associationid" select="../associationid"/>
<xsl:copy-of select="//price[../objectid=$var.associationid]"/>
</xsl:template>
Related
I am trying to create a counter to grouped elements.
Source:
<?xml version="1.0" encoding="UTF-8"?>
<root_com>
<root_por-out>
<is_globalprocessid>1370284</is_globalprocessid>
<is_processid>1370284</is_processid>
<partneridentcode>123456</partneridentcode>
<por-out>
<por_number>320060916</por_number>
<order_pos>10</order_pos>
<order_pos_partner>10</order_pos_partner>
</por-out>
<por-out>
<por_number>320060916</por_number>
<order_number_partner>875421</order_number_partner>
<order_pos>20</order_pos>
<order_pos_partner>20</order_pos_partner>
</por-out>
<por-out>
<por_number>320060916</por_number>
<order_pos>30</order_pos>
<order_pos_partner>10</order_pos_partner>
</por-out>
<por-out>
<por_number>320060916</por_number>
<order_pos>40</order_pos>
<order_pos_partner>30</order_pos_partner>
</por-out>
<por-out>
<por_number>320060916</por_number>
<order_pos>50</order_pos>
<order_pos_partner>10</order_pos_partner>
</por-out>
</root_por-out>
</root_com>
Desired output:
<Confirmation>
<Settings>
<DecimalSymbol>.</DecimalSymbol>
</Settings>
<Orders>
<Order>
<OrderIdSupplier>320060916</OrderIdSupplier>
<OrderItems>
<OrderItem>
<LineNumber>10</LineNumber>
<ItemSubNo>1</ItemSubNo>
</OrderItem>
<OrderItem>
<LineNumber>20</LineNumber>
<ItemSubNo>1</ItemSubNo>
</OrderItem>
<OrderItem>
<LineNumber>10</LineNumber>
<ItemSubNo>2</ItemSubNo>
</OrderItem>
<OrderItem>
<LineNumber>30</LineNumber>
<ItemSubNo>1</ItemSubNo>
</OrderItem>
<OrderItem>
<LineNumber>10</LineNumber>
<ItemSubNo>3</ItemSubNo>
</OrderItem>
</OrderItems>
</Order>
</Orders>
</Confirmation>
The Code i currently have:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:formatter="com.inubit.ibis.xsltext.Formatter" version="2.0" exclude-result-prefixes="formatter">
<xsl:output method="xml" encoding="UTF-8"/>
<xsl:template match="/"><xsl:for-each select="root_com/root_por-out"><Confirmation>
<Settings>
<DecimalSymbol>.</DecimalSymbol>
</Settings>
<Orders>
<Order>
<PurchaseNo><xsl:value-of select="por-out[1]/order_number_partner"/></PurchaseNo>
<SupplierId><xsl:value-of select="partneridentcode"/></SupplierId>
<OrderIdSupplier><xsl:value-of select="por-out[1]/por_number"/></OrderIdSupplier>
<OrderItems><xsl:for-each select="por-out/order_pos"><xsl:for-each-group select="../order_pos_partner" group-by="text()"><OrderItem>
<DeliveryDate><xsl:value-of select="delivery_date"/></DeliveryDate>
<LineNumber><xsl:value-of select="current-grouping-key()"/></LineNumber><ItemSubNo><xsl:value-of select="last()"/></ItemSubNo>
<ProductId><xsl:value-of select="article_partner"/></ProductId>
<Price><xsl:value-of select="price"/></Price>
<PriceFactor>1</PriceFactor>
<Quantity><xsl:value-of select="quantity"/></Quantity>
</OrderItem></xsl:for-each-group></xsl:for-each>
</OrderItems>
</Order>
</Orders>
</Confirmation></xsl:for-each></xsl:template>
</xsl:stylesheet>
Code reduced to the core issue:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:formatter="com.inubit.ibis.xsltext.Formatter" version="2.0" exclude-result-prefixes="formatter">
<xsl:output method="xml" encoding="UTF-8"/>
<xsl:template match="/"><xsl:for-each select="root_com/root_por-out"><Confirmation>
<Settings>
<DecimalSymbol>.</DecimalSymbol>
</Settings>
<Orders>
<Order>
<OrderItems><xsl:for-each select="por-out/order_pos"><xsl:for-each-group select="../order_pos_partner" group-by="text()"><OrderItem>
<LineNumber><xsl:value-of select="current-grouping-key()"/></LineNumber><ItemSubNo><xsl:value-of select="last()"/></ItemSubNo>
</OrderItem></xsl:for-each-group></xsl:for-each>
</OrderItems>
</Order>
</Orders>
</Confirmation></xsl:for-each></xsl:template>
</xsl:stylesheet>
What the point is:
The customer sends an order. We provide a response. Since high amounts are ordered, we need to delivere the ordered parts on different dates. We split original positions.
In our reply customer gets a reference to his original order position(order_pos_partner) and customer also needs a counter for amount of split positions(destination: "ItemSubNo")
how can I do that?
If output is sorted by positions, that is fine, but not needed.
Thank you
If you use
<xsl:template match="root_por-out">
<Confirmation>
<Orders>
<Order>
<OrderIdSupplier>{por-out[1]/por_number}</OrderIdSupplier>
<xsl:for-each-group select="por-out" group-by="order_pos_partner">
<xsl:apply-templates select="current-group()"/>
</xsl:for-each-group>
</Order>
</Orders>
</Confirmation>
</xsl:template>
<xsl:template match="por-out">
<OrderItem>
<LineNumber>{current-grouping-key()}</LineNumber>
<ItemSubNumber>{position()}</ItemSubNumber>
</OrderItem>
</xsl:template>
you will get
<OrderItem>
<LineNumber>10</LineNumber>
<ItemSubNumber>1</ItemSubNumber>
</OrderItem>
<OrderItem>
<LineNumber>10</LineNumber>
<ItemSubNumber>2</ItemSubNumber>
</OrderItem>
<OrderItem>
<LineNumber>10</LineNumber>
<ItemSubNumber>3</ItemSubNumber>
</OrderItem>
<OrderItem>
<LineNumber>20</LineNumber>
<ItemSubNumber>1</ItemSubNumber>
</OrderItem>
<OrderItem>
<LineNumber>30</LineNumber>
<ItemSubNumber>1</ItemSubNumber>
</OrderItem>
So I think that has the right numbers, although not the ordering you showed.
To preserve the original input order, you could use the grouping only to store the right sequences and them map then later on:
<xsl:template match="root_por-out">
<Confirmation>
<Orders>
<Order>
<OrderIdSupplier>{por-out[1]/por_number}</OrderIdSupplier>
<xsl:variable name="groups" as="map(*)">
<xsl:map>
<xsl:for-each-group select="por-out" group-by="order_pos_partner">
<xsl:map-entry key="current-grouping-key()" select="current-group() ! generate-id()"/>
</xsl:for-each-group>
</xsl:map>
</xsl:variable>
<xsl:apply-templates select="*">
<xsl:with-param name="groups" select="$groups"/>
</xsl:apply-templates>
</Order>
</Orders>
</Confirmation>
</xsl:template>
<xsl:template match="por-out">
<xsl:param name="groups"/>
<OrderItem>
<LineNumber>{order_pos_partner}</LineNumber>
<ItemSubNumber>{index-of($groups(order_pos_partner), generate-id())}</ItemSubNumber>
</OrderItem>
</xsl:template>
Both examples use XSLT 3 although with some more verbosity (<LineNumber>{order_pos_partner}</LineNumber> instead of <LineNumber><xsl:value-of select="current-grouping-key()"/></LineNumber>or XML data structures instead of light-weight maps
<xsl:key name="group" match="group" use="#key"/>
<xsl:template match="root_por-out">
<Confirmation>
<Orders>
<Order>
<OrderIdSupplier>{por-out[1]/por_number}</OrderIdSupplier>
<xsl:variable name="groups">
<xsl:for-each-group select="por-out" group-by="order_pos_partner">
<group key="{current-grouping-key()}">
<xsl:for-each select="current-group()">
<item>
<xsl:value-of select="generate-id()"/>
</item>
</xsl:for-each>
</group>
</xsl:for-each-group>
</xsl:variable>
<xsl:apply-templates select="*">
<xsl:with-param name="groups" select="$groups"/>
</xsl:apply-templates>
</Order>
</Orders>
</Confirmation>
</xsl:template>
<xsl:template match="por-out">
<xsl:param name="groups"/>
<OrderItem>
<LineNumber>
<xsl:value-of select="order_pos_partner"/>
</LineNumber>
<ItemSubNumber>
<xsl:value-of select="index-of(key('group', order_pos_partner, $groups)/item, generate-id())"/>
</ItemSubNumber>
</OrderItem>
</xsl:template>
it could be done in XSLT 2.
In my XML I want to copy values into one node from another node if both nodes have matching values in some other nodes.
<XML>
<ORG>
<ITM>
<NUM>1</NUM>
<SEQ>10</SEQ>
<VAL>X</VAL>
</ITM>
<ITM>
<NUM>2</NUM>
<SEQ>20</SEQ>
<VAL>Y</VAL>
</ITM>
</ORG>
<NEW>
<ITM>
<NUM>1</NUM>
<SEQ>10</SEQ>
<VAL>QQQ</VAL>
</ITM>
</NEW>
</XML>
This works, but for large documents it might be slow, is there a better way?
<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="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ORG/ITM/VAL">
<xsl:variable name="NUM" select="../NUM"/>
<xsl:variable name="SEQ" select="../SEQ"/>
<VAL>
<xsl:value-of select="../../../NEW/ITM/VAL[../NUM=$NUM and ../SEQ=$SEQ]"/>
</VAL>
</xsl:template>
</xsl:stylesheet>
The result has the value "QQQ" in the ORG/ITM/VAL node since ORG/ITM/NUM and ORD/ITM/SEQ match with NEW/ITM/NUM and NEW/ITM/SEQ
<?xml version="1.0"?>
<XML>
<ORG>
<ITM>
<NUM>1</NUM>
<SEQ>10</SEQ>
<VAL>QQQ</VAL>
</ITM>
<ITM>
<NUM>2</NUM>
<SEQ>20</SEQ>
<VAL/>
</ITM>
</ORG>
<NEW>
<ITM>
<NUM>1</NUM>
<SEQ>10</SEQ>
<VAL>QQQ</VAL>
</ITM>
</NEW>
</XML>
The preferred method to lookup values is to define a key and use it. In the given example, this could be:
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:key name="new-item" match="NEW/ITM" use="concat(NUM, '|', SEQ)" />
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ORG/ITM/VAL">
<xsl:copy>
<xsl:value-of select="key('new-item', concat(../NUM, '|', ../SEQ))/VAL"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
using 1 variable is more faster.
<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="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ORG/ITM/VAL">
<xsl:variable name="NUMSEQ" select="concat(string(../NUM), string(../SEQ))"/>
<VAL>
<xsl:value-of select="../../../NEW/ITM/VAL[concat(string(../NUM), string(../SEQ))=$NUMSEQ]"/>
</VAL>
</xsl:template>
</xsl:stylesheet>
I have xml file with structure like this:
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.6.xsd">
<changeSet author="system" id="65D7AFA9-17F1-4406-997E-D2B42A0E9008" dbms="oracle">
<sql>GRANT SELECT,INSERT,UPDATE,DELETE ON ${dbSchema}.CATALOG TO ${appUser}</sql>
<rollback>REVOKE SELECT,INSERT,UPDATE,DELETE ON ${dbSchema}.CATALOG FROM ${appUser}</rollback>
</changeSet>
<changeSet author="system" id="E2731435-DCEE-4B7E-BA60-20A87E75227A" dbms="oracle">
<sql>GRANT SELECT,INSERT,UPDATE,DELETE ON ${dbSchema}.CATALOGATTRIBUTE TO ${appUser}</sql>
<rollback>REVOKE SELECT,INSERT,UPDATE,DELETE ON ${dbSchema}.CATALOGATTRIBUTE FROM ${appUser}</rollback>
</changeSet>
<changeSet author="system" id="65D7AFA9-17F1-4406-997E-D2B42A0E9008" dbms="oracle">
<createTable ...>
</changeSet>
</databaseChangeLog>
I'd like to output all changeSets where sql contains one of tables to one document and all others (which doesn't contain the table name) to other document. I was thinking about appending stuff to variable and at the end print two variables, but that doesn't work (or I don't know how to process further).
<xsl:transform version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:uuid="http://uuid.util.java"
exclude-result-prefixes="uuid">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:variable name="tables"
select="('CATALOG','CATALOGATTRIBUTE','EVENTTYPES')"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="changeSet[sql]">
<xsl:variable name="changeSet" select="."/>
<xsl:variable name="sql" select="sql"/>
<xsl:for-each select="$tables">
<!--<xsl:variable name="selected" select="sql[contains(text(),concat('${dbSchema}.', ., ' '))]"/>-->
<xsl:variable name="tableName">
<xsl:value-of select="."/>
<xsl:value-of select="' '"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="contains($sql, $tableName)">
<xsl:copy-of select="$changeSet"/>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:transform>
can I somehow gather all changeSet which match tables and then all changeSets which doe not?
ps: I'm using Saxon-HE-9.8.0-14 for processing.
Thanks
Consider the following simplified example:
XML
<databaseChangeLog>
<changeSet>
<sql>ALPHA,BRAVO,CHARLIE</sql>
</changeSet>
<changeSet>
<sql>BRAVO,CHARLIE,DELTA</sql>
</changeSet>
<changeSet>
<sql>DELTA,ECHO,FOXTROT</sql>
</changeSet>
<changeSet>
<sql>FOXTROT,GOLF,HOTEL</sql>
</changeSet>
</databaseChangeLog>
XSLT 2.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:param name="tables" select="('BRAVO', 'GOLF')"/>
<xsl:template match="/databaseChangeLog">
<xsl:variable name="group1" select="changeSet[some $t in $tables satisfies contains(sql, $t)]" />
<xsl:copy>
<group1>
<xsl:copy-of select="$group1"/>
</group1>
<group2>
<xsl:copy-of select="changeSet except $group1"/>
</group2>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog>
<group1>
<changeSet>
<sql>ALPHA,BRAVO,CHARLIE</sql>
</changeSet>
<changeSet>
<sql>BRAVO,CHARLIE,DELTA</sql>
</changeSet>
<changeSet>
<sql>FOXTROT,GOLF,HOTEL</sql>
</changeSet>
</group1>
<group2>
<changeSet>
<sql>DELTA,ECHO,FOXTROT</sql>
</changeSet>
</group2>
</databaseChangeLog>
Demo: https://xsltfiddle.liberty-development.net/jyRYYiP
A better solution would be to use tokenize():
XSLT 2.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:param name="tables" select="('BRAVO', 'GOLF')"/>
<xsl:template match="/databaseChangeLog">
<xsl:variable name="group1" select="changeSet[tokenize(sql, ',') = $tables]" />
<xsl:copy>
<group1>
<xsl:copy-of select="$group1"/>
</group1>
<group2>
<xsl:copy-of select="changeSet except $group1"/>
</group2>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Demo: https://xsltfiddle.liberty-development.net/jyRYYiP/1
I have the following source XML document:
<UserDefinedFields>
<UserDefinedField>
<Name>ABC</Name>
<Value>123</Value>
</UserDefinedField>
<UserDefinedField>
<Name>XYZ</Name>
<Value>645q3245</Value>
</UserDefinedField>
</UserDefinedFields>
I want to overwrite matching nodes from an input XML if there is a matching <Name> value.. So in other words, the end result of merging this in:
<UserDefinedField>
<Name>XYZ</Name>
<Value>NEWVALUE!</Value>
</UserDefinedField>
... would be:
<UserDefinedFields>
<UserDefinedField>
<Name>ABC</Name>
<Value>123</Value>
</UserDefinedField>
<UserDefinedField>
<Name>XYZ</Name>
<Value>NEWVALUE!</Value>
</UserDefinedField>
</UserDefinedFields>
What is an appropriate XSLT transformation to achieve this?
XSLT 2.0 or 1.0 answers are fine... 2.0 preferred though.
You can do this with grouping:
<xsl:for-each-group
select="$doc1//UserDefinedField, $doc2//UserDefinedField"
group-by="Name">
<xsl:copy-of select="current-group()[last()]"/>
</xsl:for-each-group>
Assuming you are instructing your processor to process the "initial" XML document, and supplying the path to the "overriding" document as a parameter, you could do:
XSLT 2.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:strip-space elements="*"/>
<xsl:param name="path-to-update">update.xml</xsl:param>
<xsl:key name="fld" match="UserDefinedField" use="Name" />
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Value">
<xsl:variable name="update" select="key('fld', ../Name, document($path-to-update))" />
<xsl:copy>
<xsl:value-of select="if ($update) then $update/Value else ."/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Just use this template to override the identity rule:
<xsl:template match="UserDefinedField[key('kFieldByName', Name, $vDoc2)]/Value/text()">
<xsl:value-of select="key('kFieldByName', ../../Name, $vDoc2)[1]"/>
</xsl:template>
Here I assume that the second document has a document element (top element) and can contain many UserDefinedField elements at different depth. For convenience only, the second document is inlined in the transformation -- in a real case the doc() function can be used. I also declare an <xsl:key> to efficiently find a new value using the Name child's value of a UserDefinedField in the second document.
Here is the complete transformation:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kFieldByName" match="Value" use="../Name"/>
<xsl:variable name="vDoc2">
<patterns>
<UserDefinedField>
<Name>XYZ</Name>
<Value>NEWVALUE!</Value>
</UserDefinedField>
</patterns>
</xsl:variable>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="UserDefinedField[key('kFieldByName', Name, $vDoc2)]/Value/text()">
<xsl:value-of select="key('kFieldByName', ../../Name, $vDoc2)[1]"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<UserDefinedFields>
<UserDefinedField>
<Name>ABC</Name>
<Value>123</Value>
</UserDefinedField>
<UserDefinedField>
<Name>XYZ</Name>
<Value>645q3245</Value>
</UserDefinedField>
</UserDefinedFields>
the wanted, correct result is produced:
<UserDefinedFields>
<UserDefinedField>
<Name>ABC</Name>
<Value>123</Value>
</UserDefinedField>
<UserDefinedField>
<Name>XYZ</Name>
<Value>NEWVALUE!</Value>
</UserDefinedField>
</UserDefinedFields>
I want to write an xslt to transform one xml file to another. The source XML file is like the following
<orgs>
<organization revenue="10000">
<name>foo</name>
</organization>
<organization parent="foo">
<name>foo2</name>
</organization>
<organization parent="foo2">
<name>foo3</name>
</organization>
</orgs>
The output xml should be as follows
<orgo>
<organization revenue="10000">
<name>foo</name>
<organization>
<name>foo2</name>
<organization><name>foo3</name></organization>
</organization>
</organization>
</orgo>
So far i've tried writing the xsl as follows
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()">
<xsl:copy>
<xsl:apply-templates select="node()[1]"/>
<xsl:apply-templates select="following-sibling::node()[1]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<orgs>
<organization revenue="10000">
<name>foo</name>
</organization>
<organization parent="foo">
<name>foo2</name>
</organization>
<organization parent="foo2">
<name>foo3</name>
</organization>
</orgs>
produces the wanted, correct output:
<orgs>
<organization>
<name>foo</name>
<organization>
<name>foo2</name>
<organization>
<name>foo3</name>
</organization>
</organization>
</organization>
</orgs>
In case the order of <organization> elements is random, like in the following XML document:
<orgs>
<organization parent="foo2">
<name>foo3</name>
</organization>
<organization parent="foo">
<name>foo2</name>
</organization>
<organization revenue="10000">
<name>foo</name>
</organization>
</orgs>
this transformation produces the wanted result:
<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="/*">
<xsl:copy>
<xsl:apply-templates select="organization[not(#parent)]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="organization">
<xsl:copy>
<xsl:copy-of select="node()"/>
<xsl:apply-templates select="../organization[#parent=current()/name]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>