I have a large XML (from Agile) that I need to pass into a system that cannot quickly read large XMLs. There is a repeating element that has a unique child element, I'd like to remove all of the others except the one I pass in with a parameter.
<AgileData xmlns="http://www.oracle.com/webfolder/technetwork/xml/plm/2013/09/">
<Parts uniqueId="10000:6049719:8336339">
<TitleBlock><Number>300901</Number></TitleBlock>
<PartType>Raw Material</PartType>
<LifecyclePhase>Production</LifecyclePhase>
<Description>Prod 1 Desc</Description>
<ProductLines><Value>123-Line</Value></ProductLines>
</Parts>
<Parts uniqueId="10000:6049719:8337000">
<TitleBlock><Number>300902</Number></TitleBlock>
<PartType>Raw Material</PartType>
<LifecyclePhase>Prototype</LifecyclePhase>
<Description>Prod 2 Desc</Description>
<ProductLines><Value>222-Line</Value></ProductLines>
</Parts>
<Parts uniqueId="10000:6049719:8337034">
<TitleBlock><Number>300908</Number></TitleBlock>
<PartType>Raw Material</PartType>
<LifecyclePhase>Prototype</LifecyclePhase>
<Description>Prod 3 Desc</Description>
<ProductLines><Value>123-Line</Value></ProductLines>
</Parts>
</AgileData>
Desired Output is the following when passing in the parameter Item
<AgileData xmlns="http://www.oracle.com/webfolder/technetwork/xml/plm/2013/09/">
<Parts uniqueId="10000:6049719:8337000">
<TitleBlock><Number>300902</Number></TitleBlock>
<PartType>Raw Material</PartType>
<LifecyclePhase>Prototype</LifecyclePhase>
<Description>Prod 2 Desc</Description>
<ProductLines><Value>222-Line</Value></ProductLines>
</Parts>
</AgileData>
This what I have so far, though it's not working.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.oracle.com/webfolder/technetwork/xml/plm/2013/09/">
<xsl:output omit-xml-declaration="no" indent="yes"/>
<xsl:param name="Item" select="300902" />
<xsl:template match="/AgileData">
<xsl:apply-templates select="//Parts"/>
</xsl:template>
<xsl:template match="//Parts/*[TitleBlock/Number='$Item']">
<xsl:copy-of select="."/>
</xsl:template>
</xsl:stylesheet>
After its working I'll change the parameter to just have a name and not a hardcoded value as it will get passed in from the engine. I can use XSLT 1.0 and 2.0.
Or simply:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://www.oracle.com/webfolder/technetwork/xml/plm/2013/09/">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:param name="Item" select="300902" />
<xsl:template match="/ns0:AgileData">
<xsl:copy>
<xsl:copy-of select="ns0:Parts[ns0:TitleBlock/ns0:Number=$Item]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
If you can use XSLT 2.0, it can be even simpler:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="http://www.oracle.com/webfolder/technetwork/xml/plm/2013/09/">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:param name="Item" select="300902" />
<xsl:template match="/AgileData">
<xsl:copy>
<xsl:copy-of select="Parts[TitleBlock/Number=$Item]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This XSLT 2.0 transformation:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:x="http://www.oracle.com/webfolder/technetwork/xml/plm/2013/09/">
<xsl:output omit-xml-declaration="yes"/>
<xsl:param name="pItem" select="300902" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="x:Parts[x:TitleBlock/x:Number/number() ne $pItem]"/>
</xsl:stylesheet>
When applied on the provided XML document:
<AgileData xmlns="http://www.oracle.com/webfolder/technetwork/xml/plm/2013/09/">
<Parts uniqueId="10000:6049719:8336339">
<TitleBlock><Number>300901</Number></TitleBlock>
<PartType>Raw Material</PartType>
<LifecyclePhase>Production</LifecyclePhase>
<Description>Prod 1 Desc</Description>
<ProductLines><Value>123-Line</Value></ProductLines>
</Parts>
<Parts uniqueId="10000:6049719:8337000">
<TitleBlock><Number>300902</Number></TitleBlock>
<PartType>Raw Material</PartType>
<LifecyclePhase>Prototype</LifecyclePhase>
<Description>Prod 2 Desc</Description>
<ProductLines><Value>222-Line</Value></ProductLines>
</Parts>
<Parts uniqueId="10000:6049719:8337034">
<TitleBlock><Number>300908</Number></TitleBlock>
<PartType>Raw Material</PartType>
<LifecyclePhase>Prototype</LifecyclePhase>
<Description>Prod 3 Desc</Description>
<ProductLines><Value>123-Line</Value></ProductLines>
</Parts>
</AgileData>
produces the wanted, correct result:
<AgileData xmlns="http://www.oracle.com/webfolder/technetwork/xml/plm/2013/09/">
<Parts uniqueId="10000:6049719:8337000">
<TitleBlock><Number>300902</Number></TitleBlock>
<PartType>Raw Material</PartType>
<LifecyclePhase>Prototype</LifecyclePhase>
<Description>Prod 2 Desc</Description>
<ProductLines><Value>222-Line</Value></ProductLines>
</Parts>
</AgileData>
XSLT 1.0 solution:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:x="http://www.oracle.com/webfolder/technetwork/xml/plm/2013/09/">
<xsl:output omit-xml-declaration="yes"/>
<xsl:param name="pItem" select="300902" />
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="x:Parts">
<xsl:if
test="x:TitleBlock/x:Number = $pItem"><xsl:call-template name="identity"/></xsl:if>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the same XML document (above), the same correct / wanted result is produced.
Do note:
The use and overriding of the identity rule
The use of the same namespace as the default namespace in the XML document.
To learn more about the either of these important concepts, do search SO or the internet for "XSLT identity template / rule" and "XSLT processing a document in default namespace"
See also:
Dave Pawson's XSLT FAQ on Identity transformation
Dave Pawson's XSLT FAQ on Namespaces
Related
I have a requirement to transform below XML
<XML>
<Obj1 attr1="value1" attr2="value2" attr="10"/>
<Test1 tatt1="tvalue1" tatt2="tvalue2" attr="10"/>
<Obj1 attr1="value11" attr2="value21" attr="101"/>
<Test1 tatt1="tvalue11" tatt2="tvalue21" attr="101"/>
<Obj1 attr1="value12" attr2="value22" attr="102"/>
<Test1 tatt1="tvalue12" tatt2="tvalue22" attr="102"/>
</XML>
I want transformed XML like
<XML>
<Obj1 attr1="value1" attr2="value2" attr="10" tatt1="tvalue1"/>
<Obj1 attr1="value11" attr2="value21" attr="101" tatt1="tvalue11"/>
<Obj1 attr1="value12" attr2="value22" attr="102" tatt1="tvalue12"/>
</XML>
I have achieved it through normal pattern matching and finding the matching attribute value in all other elements. I doubt about the performance. So wanted to check if it can be done using group-by attribute name and combining attributes from all such elements into one.
I want to merge contents (all attributes of Obj1 and selected attributes from matching elements) of all matching elements having attr= into transformed XML.
Try this XSLT-1.0 stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output method="xml" indent="yes"/>
<xsl:key name="tests" match="Test1" use="#attr" />
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()"> <!-- Identity template: copies all nodes to the output - unless a more specific template matches -->
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Obj1"> <!-- Modifies all 'Obj1' elements -->
<xsl:copy>
<xsl:copy-of select="#*|key('tests',#attr)/#tatt1" />
</xsl:copy>
</xsl:template>
<xsl:template match="Test1" /> <!-- Removes all 'Test1' elements from the output -->
</xsl:stylesheet>
The output should be as desired. And the use of the xsl:key will improve the performance.
AFAICT, you could so 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:template match="/XML">
<xsl:copy>
<xsl:for-each select="Obj1">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:copy-of select="following-sibling::Test1[1]/#tatt1"/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
If you want do it by matching the value of the attr attribute instead of by position, then it would become:
<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="test" match="Test1" use="#attr"/>
<xsl:template match="/XML">
<xsl:copy>
<xsl:for-each select="Obj1">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:copy-of select="key('test', #attr)/#tatt1"/>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Given an XML structure like follows:
<Resources PossibleAttribute="SomethingHere">
<Book ID="1" OtherAttribute="abc" />
<Book ID="2" DifferentAttribute="def" />
</Resources>
I need to copy the structure and element where ID='1'. So I'm looking to end up with:
<Resources PossibleAttribute="SomethingHere">
<Book ID="1" OtherAttribute="abc" />
</Resources>
What I've come up with thus far is:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Resources">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="Book[#ID='1']">
<xsl:copy>
<xsl:value-of select="#*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This, however, is not producing the intended end result, giving me this instead:
<Resources>
<Book>1</Book>
</Resources>
A key item here is that any of these elements may contain additional attributes that are not specified here or currently known. Hence, if additional attributes are present, they too should be copied.
Any tips you can offer would be most appreciated!
Why not 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="*"/>
<xsl:template match="/Resources">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:copy-of select="Book[#ID=1]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
What would be the XSLT to change this XML
<?xml version="1.0" encoding="utf-8"?>
<cas:ADDRESS_DETAILS PRIMARY_ADDRESS_INDICATOR="1" ADDRESS_ID="-289495914026885120" ADDRESS_TYPE="45001" ADDRESS_ACTIVE_FROM_DATE="2006-12-23" PERSON_ID="14512823342202880">
<cas:ADDRESS_ELEMENT VALUE="McMurchy Avenue" TYPE="ADD2" />
<cas:ADDRESS_ELEMENT VALUE="ON" TYPE="PROV" />
<cas:ADDRESS_ELEMENT VALUE="CA" TYPE="COUNTRY" />
<cas:ADDRESS_ELEMENT VALUE="Brampton" TYPE="CITY" />
<cas:ADDRESS_ELEMENT VALUE="440" TYPE="ADD1" />
</cas:ADDRESS_DETAILS>
In to this format
<?xml version="1.0" encoding="utf-8"?>
<cas:ADDRESS_DETAILS PRIMARY_ADDRESS_INDICATOR="1" ADDRESS_ID="-289495914026885120" ADDRESS_TYPE="45001" ADDRESS_ACTIVE_FROM_DATE="2006-12-23" PERSON_ID="14512823342202880" ADD2 ="McMurchy" PROV="ON" COUNTRY="CA" CITY="Brampton" ADD1="440">
</cas:ADDRESS_DETAILS>
Assuming you want to merge all ADDRESS_ELEMENTs inside their parent you can use
<xsl:template match="ADDRESS_ELEMENT[1]">
<xsl:copy>
<xsl:apply-templates select="../ADDRESS_ELEMENT" mode="to-attribute"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ADDRESS_ELEMENT[position() > 1]"/>
<xsl:template match="ADDRESS_ELEMENT" mode="to-attribute">
<xsl:attribute name="{#TYPE}" select="#VALUE"/>
</xsl:template>
plus the identity transformation to handle the rest (i.e. <xsl:mode on-no-match="shallow-copy"/> in XSLT 3 (https://xsltfiddle.liberty-development.net/6qM2e2q) or the corresponding template in earlier versions)
If you want to transform the child elements into attributes of the parent, as your edit seems to indicate, you can simplify the code. Using a namespace requires some adaption however:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xpath-default-namespace="http://example.com/cas"
version="3.0">
<xsl:strip-space elements="*"/>
<xsl:output indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="ADDRESS_DETAILS">
<xsl:copy>
<xsl:apply-templates select="#*, ADDRESS_ELEMENT"/>
</xsl:copy>
</xsl:template>
<xsl:template match="ADDRESS_ELEMENT">
<xsl:attribute name="{#TYPE}" select="#VALUE"/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/6qM2e2q/2
Is there any way on how to convert a string to binary base64? I've seen many references but it didn't work in my end. For example I have this input file:
<RootElement xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Data>
<Binary>
<RawData>This element should convert string to binary base64.</RawData>
</Binary>
</Data>
</RootElement>
I need to generate:
<RootElement xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Data>
<Binary>
<RawData>VGhpcyBlbGVtZW50IHNob3VsZCBjb252ZXJ0IHN0cmluZyB0byBiaW5hcnkgYmFzZTY0Lg==</RawData>
</Binary>
</Data>
I created an xslt and used the namespace I've seen online:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dp="http://www.datapower.com/extensions">
<xsl:output method="xml" version="1.0" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="RawData">
<xsl:element name="RawData">
<xsl:value-of select="dp:encode(., 'base-64')"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Thank you.
There is a pure XSLT 1.0 solution that works for any XSLT processor: JAXP, Saxon, Xalan, Xsltproc, Microsoft:
Download base64.xsl
Download base64_binarydatamap.xml
Use XSLT 1.0:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:b64="https://github.com/ilyakharlamov/xslt_base64">
<xsl:output method="xml"/>
<xsl:include href="base64.xsl"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="/RootElement/Data/Binary/RawData">
<xsl:call-template name="b64:encode">
<xsl:with-param name="asciiString" select="text()"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
I am newbie to XSLT.can you help me for xslt to achieve below output. In my input xml have duplicate nodes and i have to remove based on element (CustAccId) value should not be re-peat.
Inputxml :
<Main>
<Request>
<TypeInd>I</TypeInd>
<CustAcctID>505665599</CustAcctID>
<ServiceOrderID>1452653</ServiceOrderID>
</Request>
<Request>
<TypeInd>O</TypeInd>
<CustAcctID>2011395</CustAcctID>
<ServiceOrderID>1452652</ServiceOrderID>
</Request>
<Request>
<TypeInd>I</TypeInd>
<CustAcctID>505665599</CustAcctID>
<ServiceOrderID>1452653</ServiceOrderID>
</Request>
</Main>
Output XML :
<Main>
<Request>
<TypeInd>I</TypeInd>
<CustAcctID>505665599</CustAcctID>
<ServiceOrderID>1452653</ServiceOrderID>
</Request>
<Request>
<TypeInd>O</TypeInd>
<CustAcctID>2011395</CustAcctID>
<ServiceOrderID>1452652</ServiceOrderID>
</Request>
Here is XSLt i tried but didn't work like it retrun duplicate request node
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:if test="not(following::Request[CustAcctID=current()])">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
You can remove the duplicate with following XSLT:
<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:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="Request[CustAcctID = following::Request/CustAcctID]"/>
</xsl:stylesheet>
Output XML:
<?xml version="1.0" encoding="UTF-8"?>
<Main>
<Request>
<TypeInd>O</TypeInd>
<CustAcctID>2011395</CustAcctID>
<ServiceOrderID>1452652</ServiceOrderID>
</Request>
<Request>
<TypeInd>I</TypeInd>
<CustAcctID>505665599</CustAcctID>
<ServiceOrderID>1452653</ServiceOrderID>
</Request>
</Main>
The template matching all Request nodes where the CustAcctID matches the CustAcctID of the following Request will not produce any output for the matching Request, so the duplicates will not be written.
Update for the advice in the comment by michael.hor257k: Another approach is to use Muenchian grouping:
<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="x" match="ServiceOrderID" use="." />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="Request">
<xsl:for-each select=".">
<xsl:if test="generate-id(ServiceOrderID) =
generate-id(key('x', ServiceOrderID)[1])">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
which produces the same output XML but can be more efficient as you'll find described in detail in the article by Jeni Tennison http://www.jenitennison.com/xslt/grouping/muenchian.xml that michael.hor257k already recommended.
As additional reference for XSLT grouping you can have a look at http://www.dpawson.co.uk/xsl/sect2/N4486.html