XSLT - select a node from a complex xml file - xslt

I have an XML file below where I need to get the text inside of the <Description> tag under the <Checklist> tag where the <Sequence> tag has the text 40. How to achieve it?
<?xml version="1.0" encoding="UTF-8"?>
<SyncMaintenanceOrder>
<DataArea>
<MaintenanceOrder>
<MaintenanceOrderHeader>
<DocumentID>
<ID accountingEntity="AT">1105442</ID>
</DocumentID>
<Description>Routine Bridge Inspection - S6</Description>
<PriorityCode>2</PriorityCode>
<ReportedDateTime>2020-04-29T20:21:27Z</ReportedDateTime>
</MaintenanceOrderHeader>
<MaintenanceOrderLine>
<LineNumber>10</LineNumber>
<RemainingDuration>PT8H0M0S</RemainingDuration>
<ActivityDeferredIndicator>false</ActivityDeferredIndicator>
<UserArea>
<EamCheckListInfo>
<CheckList>
<CheckListItem>
<Sequence>40</Sequence>
<Description>Half joints (Superstructure elements)</Description>
</CheckListItem>
<CheckListItem>
<Sequence>160</Sequence>
<Description>Substructure drainage (Durability elements)</Description>
</CheckListItem>
<CheckListItem>
<Sequence>60</Sequence>
<Description>Parapet beam or cantilever (Superstructure elements)</Description>
</CheckListItem>
</CheckList>
</EamCheckListInfo>
</UserArea>
</MaintenanceOrderLine>
</MaintenanceOrder>
</DataArea>
</SyncMaintenanceOrder>
I need sample of an XSLT code for selecting only the text node described above.

I'm not sure I really get your question.
This will print the Description of the CheckListItem which has a Sequence of 40:
<xsl:value-of select="//CheckListItem/Sequence[text()='40']/../Description"/>
Try it here: https://xsltfiddle.liberty-development.net/ehVZvvZ

Try the below code
<xsl:value-of select="*[local-name(.)='SyncMaintenanceOrder']/*[local-name(.)='DataArea']/*[local-name(.)='MaintenanceOrder']/*[local-name(.)='MaintenanceOrderLine']/*[local-name(.)='UserArea']/*[local-name(.)='EamCheckListInfo']/*[local-name(.)='CheckList']/*[local-name(.)='CheckListItem']/*[local-name(.)='Sequence'][text() = '40']/../*[local-name(.)='Description']" />

Related

How to trim the XSLT use attribute expression

I am new to XSLT and I have got a special requirement. My XML file looks like below.
<results>
<result>
<Price>
<LinePrices>
<ProductId>ProductId:1000</ProductId>
<Uom>KG</Uom>
<Quantity>0</Quantity>
</LinePrices>
<LinePrices>
<ProductId>ProductId:1002</ProductId>
<Uom>EACH</Uom>
<Quantity>0</Quantity>
</LinePrices>
</Price>
</result>
<result>
<productlist>
<productid>1000</productid>
<relevance>0.9</relevance>
<sponsored>0</sponsored>
</productlist>
<productlist>
<productid>1001</productid>
<relevance>0.8</relevance>
<sponsored>0</sponsored>
</productlist>
</result>
</results>
I have to write XSLT keys to match the pattern. I have written the key for productlist as shown below.
<xsl:key name="productsIdForProduct" match="productlist" use="productid" />
In the similar way I am trying to write the key for LinePrices as shown below.
<xsl:key name="productsIdPrice" match="result/LinePrices" use="ProductId" />
However this returns 'ProductId:1000' in use attribute. I am trying to trim 'ProductId:' from the use attribute value. How can I write a trim expression to extract '1000' from 'ProductId:1000' inside XPATH expression?
Just use substring-after():
substring-after(ProductId,':')

XPath selector not working during PDF transformation

I have a DITA bookmap where I am storing image paths:
<bookmap>
<bookmeta>
<data id="productLogo">
<image href="images/_notrans/frontcover/productLogo.svg" />
</data>
<data id="productPhoto" >
<image href="images/_notrans/frontcover/productPhoto.jpg" />
</data>
</bookmeta>
</bookmap>
Then I attempt to grab the href values by data[#id]:
<xsl:variable name="productLogo"><xsl:value-of select="//data[#id='productLogo']/image/#href" /></xsl:variable>
<xsl:variable name="productPhoto"><xsl:value-of select="//data[#id='productPhoto']/image/#href" /></xsl:variable>
(These XPath expressions match the href when I test against my bookmap in Oxygen.)
During transformation I output:
<xsl:message>productPhoto: <xsl:value-of select="$productPhoto"/></xsl:message>
The value-of is always empty.
However, everything works as expected if I replace the id attribute with numbers:
<xsl:variable name="productLogo"><xsl:value-of select="//data[1]/image/#href" /></xsl:variable>
<xsl:variable name="productPhoto"><xsl:value-of select="//data[2]/image/#href" /></xsl:variable>
What am I doing wrong that's preventing using #id="whatever"?
The XSLT is not applied directly over the Bookmap contents, it is applied over an XML document which contains the bookmap with all topic references expanded in it and with some preprocessing applied to it.
If you set the "clean.temp" parameter to "no" you will find in the temporary files folder a file called something like "mapName_MERGED.xml", that is the XML document over which the XSLT is applied and as you will see in it, all IDs have been changed to be unique in the context of the entire XML document.
When usually working with data elements you should set the #name attribute to them like:
<data name="productLogo">
and match that name in the XSLT code.
There are examples of using "data" in the DITA 1.2 specs as well:
http://docs.oasis-open.org/dita/v1.2/os/spec/langref/data.html#data
Another option, depending on your needs, is to develop a naming convention for the product photos and use the element to build the URI. As the product logo shouldn't change for a product family, it wouldn't hurt to hard-code that in the XSLT code.

Processing hl7 type message using xslt or regex, or combination of two (XSLT 1.0)

so I have this hl7 type message that I have to transform using either regex or xslt or combination of two.
Format of this message is DateTime(as in YYYYMMDDHHMMSS)^UnitName^room^bed|). Each location is separated with a pipe, so each person can have one or multiple locations.
And the messages looks like this( when a patient has only one location):
20130602201605^Some Hospital^ABFG^411|
End xml result should look like this:
<Location>
<item>
<when>20130602201605</when>
<UnitName>Some Hospital</UnitName>
<room>ABFG</room>
<bed>411</bed>
</item>
</Location>
I would probably use substring type of function if it was only one location.
The problem I am running into is when there is more than one. I am relatively new to xslt and regex in general so I don't know how to use recursion in these instances.
So if I have a message like this with multiple locations:
20130601003203^GBMC^XXYZ^110|20130602130600^Sanai^ABC^|20130602150003^John Hopkins^J615^A|
The end result should be:
<Location>
<item>
<when>0130601003203</when>
<UnitName>GBMC</UnitName>
<room>XXYZ</room>
<bed>110</bed>
</item>
<item>
<when>20130602130600</when>
<UnitName>Sanai</UnitName>
<room>ABC</room>
<bed></bed>
</item>
<item>
<when>20130602150003</when>
<UnitName>John Hopkins</UnitName>
<room>J615</room>
<bed>A</bed>
</item>
</Location>
So how would I solve this? Thanks in advance.
Given that your Hl7 message is "|^~\&" encoded and not in an XML format, it is not clear how you will be using an XSLT 1.0 processor for your task. Can you describe your processing pipeline in greater detail? Your snippets are not complete messages, and it is not clear whether you will be starting with complete messages or attempting to parse isolated fields handed to a larger processing task through parameters or something.
If your processing starts with a complete HL7 message, I would suggest looking into the HAPI project, or a similar set of libraries, to have the messages converted from |^~\& to </> format, then invoking your XSLT on that version of the data. (You could also use the HAPI libraries in a full-Java solution. In either case, there are code examples at the HAPI site and at an Apache site on HL7.) If you are not interested in using Java at all, but are open to partial non-XSLT solutions, there are other projects that provide similar serialization options (e.g., Net::HL7 for Perl, nHAPI for VB/C#, etc.).
If you have isolated "|^~\&" encoded data in an otherwise XML formatted file, then I would suggest looking into the str:tokenize function in the XSLT 1.0 exslt functions. (XSLT 2.0 has a built-in tokenize function.) You can have str:tokenize split your data on the field or component separators, then create elements using the tokenized substrings.
Here is a stylesheet
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:str="http://exslt.org/strings"
extension-element-prefixes="str"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="data">
<Location>
<xsl:for-each select="str:tokenize(.,'|')">
<xsl:call-template name="handle-field">
<xsl:with-param name="field" select="."/>
</xsl:call-template>
</xsl:for-each>
</Location>
</xsl:template>
<xsl:template name="handle-field">
<xsl:param name="field"/>
<xsl:variable name="components" select="str:tokenize($field,'^')"/>
<item>
<when><xsl:value-of select="$components[1]"/></when>
<UnitName><xsl:value-of select="$components[2]"/></UnitName>
<room><xsl:value-of select="$components[3]"/></room>
<bed><xsl:value-of select="$components[4]"/></bed>
</item>
</xsl:template>
</xsl:stylesheet>
that runs over this input
<?xml version="1.0" encoding="UTF-8"?>
<data>20130601003203^GBMC^XXYZ^110|20130602130600^Sanai^ABC^|20130602150003^John Hopkins^J615^A|</data>
to produce this output with xsltproc:
<?xml version="1.0"?>
<Location>
<item>
<when>20130601003203</when>
<UnitName>GBMC</UnitName>
<room>XXYZ</room>
<bed>110</bed>
</item>
<item>
<when>20130602130600</when>
<UnitName>Sanai</UnitName>
<room>ABC</room>
<bed/>
</item>
<item>
<when>20130602150003</when>
<UnitName>John Hopkins</UnitName>
<room>J615</room>
<bed>A</bed>
</item>
</Location>
Your source message is in a string form, you need to create a parser that uses regex to split the message based on first pipes and then carat. refer to Unable to parse ^ character which has my original code for the parser and the solution gives a different approach to it.
After you have individual elements you need to add it to your xml as nodes.

how to add root node tag in a XML document with XSLT

Iam parsing the xml document in SSIS through the xmlsource. It does not have any root tag. So iam trying to add the root tag to my xml document through XSLT, but getting the error as
[XML Task] Error: An error occurred with the following error message: "There are multiple root elements. Line 11, position 2.".
what is the XSL to be used to add the root element.? Please help..this is very urgent..
Please find the xml source below
<organizational_unit>
<box_id>898</box_id>
<hierarchy_id>22</hierarchy_id>
<parent_box_id>0</parent_box_id>
<code>Team</code>
<description />
<name>CAPS Teams</name>
<manager_title />
<level>0</level>
</organizational_unit>
<organizational_unit>
<box_id>967</box_id>
<hierarchy_id>31</hierarchy_id>
<parent_box_id>0</parent_box_id>
<code>main</code>
<description />
<name>Protegent</name>
<manager_title />
<level>0</level>
<organizational_unit>
<box_id>968</box_id>
<hierarchy_id>31</hierarchy_id>
<parent_box_id>967</parent_box_id>
<code>19L</code>
<description>19L</description>
<name>19L</name>
<level>1</level>
<managers>
<manager>
<hierarchy_mgr_id>243</hierarchy_mgr_id>
<hierarchy_id>31</hierarchy_id>
<box_id>968</box_id>
<rep_id>19499</rep_id>
<unique_rep_id>100613948</unique_rep_id>
<first_name>Ed</first_name>
<last_name>Kill</last_name>
</manager>
</managers>
</organizational_unit>
<organizational_unit>
<box_id>1152</box_id>
<hierarchy_id>31</hierarchy_id>
<parent_box_id>967</parent_box_id>
<code>UNKNOWN_m</code>
<description>Unknown Reps</description>
<name>Unknown Reps</name>
<level>1</level>
</organizational_unit>
</organizational_unit>
Well which XSLT processor do you use, how do you use it? I usually don't suggest to use string processing to construct XML but if you have a fragment without a root element then perhaps doing string concatenation "<root>" + fragment + "</root>" is the easiest way to get a well-formed document. XSLT can work with fragments but how you do that depends on the XSLT processor or XML parser you use, for instance .NET can use an XmlReader with XmlReaderSettings with ConformanceLevel set to fragment, which can then be loaded in an XPathDocument (for processing with XSLT 1.0 and XslCompiledTransform) and probably also with Saxon's XdmNode (although I am not sure I remember that correctly).
The stylesheet would then simply do
<xsl:template match="/">
<root>
<xsl:copy-of select="node()"/>
</root>
</xsl:template>
to wrap all top level nodes into a root element.

Translating itunes affiliate rss via xslt

I can't get this working for the life of me. Here is a snippet of the xml I get from an RSS feed from itunes affiliate. I want top print the values within tags but I cannot for some reason:
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns:im="http://itunes.apple.com/rss" xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
<id>http://ax.itunes.apple.com/WebObjects/MZStoreServices.woa/ws/RSS/toppaidapplications/sf=143441/limit=100/genre=6014/xml</id><title>iTunes Store: Top Paid Applications</title><updated>2010-03-24T15:36:42-07:00</updated><link rel="alternate" type="text/html" href="http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewTop?id=25180&popId=30"/><link rel="self" href="http://ax.itunes.apple.com/WebObjects/MZStoreServices.woa/ws/RSS/toppaidapplications/sf=143441/limit=100/genre=6014/xml"/><icon>http://phobos.apple.com/favicon.ico</icon><author><name>iTunes Store</name><uri>http://www.apple.com/itunes/</uri></author><rights>Copyright 2008 Apple Inc.</rights>
<entry>
<updated>date</updated>
<id>someID</id>
<title>a</title>
<im:name>b</im:name>
</entry>
<entry>
<updated>date2/updated>
<id>someID2</id>
<title>a2</title>
<im:name>b2</im:name>
</entry>
</feed>
If I try <xsl:apply-templates match="entry"/> it spits out the entire contents of file. If I use <xsl:call-template name="entry"> it will show only one entry and I have to use <xsl:value-of select="//*[local-name(.)='name']"/> to get name but that's a hack. I've used xslt before for xml without namespaces and xml that has proper parent child relationships but not like this RSS feed. Notice entry is not wrapped in entries or anything.
Any help is appreciated. I want to use xslt because I want to alter the itunes link to go through my affiliate account - so something automated wouldn't work for me.
You are matching elements that are in no namespace, but the actual elements in the XML document do belong to a (deafult) namspace: xmlns="http://www.w3.org/2005/Atom".
Therefore, you need to declare the namespace in your stylesheet, let's say xmlns:atom="http://www.w3.org/2005/Atom". and then match not just on {elementName} but on {atom:elementName}, where {elementName} in your case is: "entry".