XPath based on attribute value - xslt

I am trying to get an attribute based on a condition, my XML is
<listitems xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"
xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
xmlns:rs="urn:schemas-microsoft-com:rowset"
xmlns:z="#RowsetSchema">
<rs:data ItemCount="2">
<z:row ows_Attachments=";#/Attachments/79/164351;#" ows_ID="79" />
<z:row ows_Attachments=";#/Attachments/80/164352;#" ows_ID="80" />
</rs:data>
</listitems>
I want to get ows_ID where ows_Attachments ends with a specific number, for example
I Want 79 if ows_Attachments endswith or contain 164351;#
If any regex available to remove all namespaces etc and then if it makes easy for processing, I am open to that option as well.

Strictly speaking, with XPath you can select the attribute with :
//*[local-name()="row" and contains(#ows_Attachments,"/164351")]/#ows_ID
And extract the value with :
string(//*[local-name()="row" and contains(#ows_Attachments,"/164351")]/#ows_ID)
Output : 79

Related

How can you apply concat(...) in a value-of directive in case of multiple nodes?

I am outputting the name node of each property node in a ; delimited string as following:
<xsl:value-of select="properties/property/name" separator=";" />
I want to alter this such that each element is prefixed with _. An example output should be:
_alpha;_beta;_gamma
I tried the following:
<xsl:value-of select="concat('_', properties/property/name)" separator=";" />
I want to use this to create an output node containing that string:
<my_node>
<xsl:value-of select="concat('_', properties/property/name)" separator=";" />
</my_node>
This gives an error when there are multiple properties:
XPTY0004: A sequence of more than one item is not allowed
as the second argument of fn:concat() (<name>, <name>)
Is there a way to get this working in XSLT 2.0/3.0?
I could resort to the XSLT 1.0 for-each solution as given in https://stackoverflow.com/a/57856287/12042211 (in which we are manually adding the separator), but I am wondering if something elegant in XSLT 2.0/3.0 is possible.
The answer is yes. XSLT 2.0 allows you to write expressions like this...
<xsl:value-of select="properties/property/concat('_', name)" separator=";" />
So, for each property it selects the concatenation of "_" with the name element.
Such syntax is not valid in XSLT 1.0 though.
In XSLT 3.0 I would tend to write this as
<xsl:value-of select="properties/property ! ('_' || name)" separator=";" />
and perhaps use string-join() instead of xsl:value-of. You haven't shown the context, but try to use xsl:value-of only when you really want a text node, not when you just want a string.

Refer to specific cell in xslt import/export filter for Calc

I am using xslt filter for importing/exporting data from Calc worksheet. Is it possible to refer to a specific cell address ? For example, if we want to export data from cell B2, how do we refer to this cell address in export xslt ?
Without knowing much about Openoffice or their xslt filter function, I can tell you that you're probably going to need a fairly simple XPath to reference a specific Cell's data - I doubt it would be as simple as calling getCell('B2') unless they have provided you with some custom xslt functions (I'm assuming they've put you in a raw XSLT environment).
Anyway, I think this question may be more about XSLT and xpath, than it is about openoffice. With that in mind, I'm going to fashion my own sample xml and examples and hopefully that will be enough to get you started.
For an input xml that looks something like this:
<ooo_calc_export>
<ooo_sheet num="1" name="sheet1">
<ooo_row num="2">
<fisrtCell>Oh</firstCell>
<secondCell>Hai</secondCell>
<thirdCell>There</thirdCell>
</ooo_row>
<ooo_row num="3">
<fisrtCell>Oh</firstCell>
<secondCell>Hello</secondCell>
<thirdCell>Back!</thirdCell>
</ooo_row>
</ooo_sheet>
</ooo_calc_export>
An absolute XPath to access cell B2's data would look like this ooo_calc_export/ooo_sheet/ooo_row[#num='2']/secondCell/text()
But the above is an absolute path and in XSLT, we would often author relative xpaths as we are in the midst of processing a document. Imagine you're in a template which matches on the ooo_calc_export node and you wanted to store Cell B2's data in a variable for later use. Consider this example:
<xsl:template match="/ooo_calc_export">
<!-- a relative xpath does not being with a forward slash -->
<xsl:variable name="B2" select="ooo_sheet/ooo_row[#num='2']/secondCell/text()" />
</xsl:template>
Now lets imagine you wanted a template to match on the cell B2 node itself:
<xsl:template match="ooo_row[#num='2']/secondCell">
<!-- a relative xpath does not being with a forward slash -->
<xsl:variable name="B2_text" select="text()" />
</xsl:template>
This is a good tutorial on XSLT to get you started. Also, the W3 Schools references on XPath and XSLT aren't the worst.

How to use contains() with a set of strings in XSLT

I have the following XML snippet:
<figure customer="ABC DEF">
<image customer="ABC"/>
<image customer="XYZ"/>
</figure>
I'd like to check if the figure element's customer attribute contains the customer attributes of the image elements.
<xsl:if test="contains(#customer, image/#customer)">
...
</xsl:if>
I get an error saying:
a sequence of more than one item is not allowed as the second argument of contains
It's important to note that I cannot tell the values of the customer attributes in advance, thus using xsl:choose is not an option here.
Is it possible to solve this without using xsl:for-each?
In XSLT 2.0 you can use:
test="image/#customer/contains(../../#customer, .) = true()"
and you will get a true() result if any of them are true. Actually, that leads me to suggest:
test="some $cust in image/#customer satisfies contains(#customer, $cust)"
but that won't address the situation where the customer string is a subset of another customer string.
Therefore, perhaps this is best:
test="tokenize(#customer,'\s+') = image/#customer"
... as that will do a string-by-string comparison and give you true() if any of the tokenized values of the figure attribute is equal to one of the image attributes.

XSLT 1.0 Regular Expression in for loop

So I have xml like
<depot id="D7">
<address>
<number>6000</number>
<street>Yonge</street>
<city>Toronto</city>
<province>ON</province>
<postal_code>M2M 2E4</postal_code>
</address>
</depot>
I have hundreds of there depot in xml
now in my xsl, I have defined a variable called 'locale' that stores a postal code like "M1C".
After this I want to select only those depot where the postal_code is like 'locale'. In other words, If I specify locale to be "M1C", then I should get all the depot whose postal_code contains "M1C", so depot with "M1C A18", "M1C B2C", etc all should be in the result.
Currently I have the line below
< xsl:for-each select="depot[address[postal_code=$locale]]">
which gives me only depot with exact postal code match and not the ones with "M1C A18", "M1C B2C", etc. I want to use something like
<xsl:for-each select="depot[address[postal_code=*$locale*]]">
with wildcards but it does not works. Suggestions?
Use:
depot[starts-with(address/postal_code, $locale)]
Here we assume that any depot has a single address/postal_code descendent and that no possible value of $locale is a prefix of any other possible value of $locale.
If, for example, the second assumption isn't true, then use:
depot[starts-with(address/postal_code, concat($locale, ' '))]
True regular expression capabilities are available in XPath 2.0 (such as the matches() function), but they aren't necessary for a simple problem as this one.
Use this:
<xsl:for-each select="depot[contains(address/postal_code,$locale)]" />
to match only depot elements that contain the fragment defined in $locale.

libxml2 XPATH - Selecting subset of data from XML

I am fairly new to XML dev.. I had a few questions regarding XML parsing with XPATH and libxml.
I have an XML structured as :
<resultset>
<result count=1>
<row>
<name> He-Man! </name>
<home> Greyskull </home>
<row>
</result>
<result count=2>
<row>
<name> Spider-Man</name>
<home> Some downtown apartment </home>
<row>
<row>
<name> Disco-Man!</name>
<home> The 70's dance floor </home>
<row>
</result>
<resultset>
I need to pick out the names from this XML , but where the count is 2 , i need it only from the first record. I ran through a few tutorials, but i am unable to come up with an XPATH query which would serve this purpose.
/name will select all name elements.
/result[#count > 1 ]/row[1]/name | /result[#count =1 ]/row/name
Is this possible to be done with XPATH ? Is this better to be done via XPATH or by walking the XML tree?
Can some one point me to some complex searches through out XML's ?
Edit : The actual scenario requires select a subset of the XML row , which are nested at 2 levels at times. This sounds like i need to OR '|' many paths to select the nodes i require... I am not sure if that would be efficient as opposed to walking a tree... The above is typed to replicate the problem :)
Thanks!
Try this XPath -
/resultset/result[#count=2]/row/name
This will give a list of all nodes falling under this XPath. From this just take the first element (as you needed only the first record).
I'd probably keep my xpath simpler and just extract both cases, then loop over both node sets.
If you do need to go down the single xpath route, you should try out your xpath expressions in something that lets you enter them live, rather than having to recompile C/C++ code. You should be able to do that by loading your XML into firefox and using firebug - for example typing $x('//name') in the firebug console gives three nodes.
NOTE however that your XML is invalid... You have a bunch of "<row>"s that should be "</row>" and the same for "<resultset>" and your counts need to be
<result count="1">
i.e. with quote marks around the value.