XPath 2 expressions interfering with each other - logic

I have a XML like this:
<Values>
<Value AttributeID="asset.extension">pdf</Value>
<Value AttributeID="asset.size">10326</Value>
<Value AttributeID="ATTR_AssetPush_Webshop">1</Value>
<Value AttributeID="asset.format">PDF (Portable Document Format application)</Value>
<Value AttributeID="asset.mime-type">application/pdf</Value>
<Value AttributeID="asset.filename">filename.pdf</Value>
<Value AttributeID="asset.uploaded">2018-01-10 17:05:39</Value>
<Value AttributeID="ATTR_Verwendungsort" Derived="true">WebShop,</Value>
</Values>
I have 2 (or more) XPath-expressions like this:
<xsl:template match="/STEP-ProductInformation/Assets/Asset/Values/Value[not(#AttributeID='asset.mime-type')]" />
<xsl:template match="/STEP-ProductInformation/Assets/Asset/Values/Value[not(#AttributeID='asset.size')]" />
For some reason though, If I have 2 of them together, all information are being stripped. If I use only 1 expressoin, I get my desired output. Can't I use 2 expressions like this?
I also tried combining them like this:
<xsl:template match="/STEP-ProductInformation/Assets/Asset/Values/Value[not(#AttributeID='asset.mime-type') and (#AttributeID='asset.size')]" />
But that didn't do it, either.
The desired output would be like this:
<Values>
<Value AttributeID="asset.size">10326</Value>
<Value AttributeID="asset.mime-type">application/pdf</Value>
</Values>

I think in XSLT 2/3 you could express it as
<xsl:template match="Values/Value[not(#AttributeID = ('asset.mime-type', 'asset.size'))]"/>
In XSLT/XPath 1.0 you would need Values/Value[not(#AttributeID = 'asset.mime-type' or #AttributeID = 'asset.size')].

<xsl:template match="/STEP-ProductInformation/Assets/Asset/Values
/Value[not(#AttributeID='asset.mime-type')]" />
<xsl:template match="/STEP-ProductInformation/Assets/Asset/Values
/Value[not(#AttributeID='asset.size')]" />
This is a logical error -- has nothing to do with XPath.
It is like saying:
From all days of the week I will work only on Mondays
From the days selected above I will work only on Tuesdays
The first statement above selects only Mondays. The 2nd statement selects all Tuesdays from these Mondays -- that is the empty set.
A correct statement:
From all days of the week I will work only on Mondays or Tuesdays

Related

XPath 1.0 fetch the child record

Below is the XML
<on-error-continue type="APIKIT:BAD_REQUEST" enableNotifications="true" logException="true">
<set-variable value="200" doc:name="httpStatus" variableName="httpStatus" />
<set-variable value="Bad request" doc:name="logDescription" variableName="logDescription" />
<flow-ref doc:name="global-prepare-error-response-sub-flow" name="global-prepare-error-response-sub-flow"/>
</on-error-continue>
<on-error-continue type="APIKIT:TOO_MANY_REQUEST" enableNotifications="true" logException="true">
<set-variable value="200" doc:name="httpStatus" variableName="httpStatus" />
<set-variable value="Many request" doc:name="logDescription" variableName="logDescription" />
<flow-ref doc:name="global-prepare-error-response-sub-flow" name="global-prepare-error-response-sub-flow"/>
</on-error-continue>
Wanted to get the single record
"set-variable value="200" doc:name="httpStatus" variableName="httpStatus"
using xPath 1.0 expression: Parent is -->on-error-continue type="APIKIT:BAD_REQUEST" and child is -->set-variable value = "200".
Have tried below expression. It is working fine with Xpath2.0 but not working with 1.0
//*[local-name()='on-error-continue'][#*[local-name()='type' and .='APIKIT:BAD_REQUEST']]/set-variable[#value='200' and #variableName='httpStatus']
Using this handy website, I took the xml and put it in a root element, <root>YOUR XML</root>.
With this XPath:
//root/on-error-continue[#type='APIKIT:TOO_MANY_REQUEST']/set-variable[#value='200' and #variableName='httpStatus']
I was able to extract the matching record. Try it yourself and replace the root with * in the above XPath. You should see the records that you're seeking.
The wildcard operator can be used like any element in the path.

Xpath 1.0 nodelist based on node names

I don't like to ask for help, but this time I'm getting totally stuck with a xpath query.
Please have a look at this XML:
<doc>
<car>
<property id="color">
<attribute id="black" />
<attribute id="white" />
<attribute id="green" />
</property>
<property id="size">
<attribute id="small" />
<attribute id="medium" />
<attribute id="large" />
</property>
</car>
<attributes>
<color>white</color>
<size>small</size>
</attributes>
</doc>
The car/properties should be output according to the attributes nodenames. The desired output is:
<property id="color"><attribute id="white" /></property>
<property id="size"><attribute id="small" /></property>
The xpath
/doc/car/property[#id=name(/doc/attributes/*)]/attribute[#id=/doc/attributes/*/text()]
results only the first node, because the name() function returns only the name of the first element.
Who can help me to find out a working xpath (XSLT 1.0)? Many thanks for your help in advance!
You can achieve this with XSLT-1.0, but not only with XPath-1.0, because in XPath-1.0 you can only return the first item. This is not a problem in XSLT-1.0, because you can use an xsl:for-each loop, like the following:
<xsl:for-each select="/doc/attributes/*">
<property id="{/doc/car/property[#id=current()/local-name()]/#id}"><attribute id="{/doc/car/property[#id=current()/local-name()]/attribute[#id=current()/.]/#id}" /></property>
</xsl:for-each>
This code emits the following XML:
<property id="color"><attribute id="white"/></property>
<property id="size"><attribute id="small"/></property>
As seen, your requirements seem to be a little bit redundant, but I guess that your greater scenario justify the means.
What about these options (it's still unclear to me why you're using name() since I don't see any namespace in your sample data) :
//property|//attribute[#id=//attributes/*]
//attribute[#id=//attributes/*]|//attribute[#id=//attributes/*]/parent::property
//property|//attribute[#id=substring-before(normalize-space(//attributes)," ") or #id=substring-after(normalize-space(//attributes)," ")]
Third option should work even if you have to deal with a namespace for the #id inside the attributes node.
Output :
My working solution:
<xsl:stylesheet version="1.0">
<xsl:template match="/">
<xsl:for-each select="/doc/car/property">
<property id="{#id}">
<xsl:variable name="id" select="#id" />
<xsl:copy-of select="attribute[#id=/doc/attributes/*[name()=$id]/text()]" />
</property>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Another solution without using a loop:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:apply-templates select="doc/car/property"/>
</xsl:template>
<xsl:template match="property">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:copy-of select="attribute[#id = /doc/attributes/*[name() = current()/#id]]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
For each property it copies the element node and its attributes. Then it copies its attribute children having an id matching the respective element below /doc/attributes.

XPATH to get multiple values

<values>
<value index="1">
<token>
<secret>
<client_key>
</value>
<value index="2">
<token>
<secret>
<client_key>
</value>
<value index="3">
<token>
<secret>
<client_key>
</value>
</values>
Can anyone please help me with the xpath to get the token value from all the indexes . The no of indexes will vary each time and not a constant.possible values are (1 or >1)
Searches for tokens inside values that has attribute index
//value[#index]/token

XPath with multiple child node conditions

I'm trying to find a XPath expression, to get an elment with multiple conditions on child nodes.
Which XPath can I use to get the ball element with ART_NR = 146334 and FABRICATOR = SPALDING?
The corresponding XML:
<xml>
<ball sellCode="ABC7001" type="basket ball">
<detail>
<type>INFO</type>
<values>
<type>NUMERIC</type>
<value>146334</value>
<id>ART_NR</id>
</values>
<values>
<type>NUMERIC</type>
<value>39.99</value>
<id>PRICE</id>
</values>
<values>
<type>STRING</type>
<value>SPALDING</value>
<id>FABRICATOR</id>
</values>
<values>
</detail>
<detail>
<type>MOD</type>
...
</detail>
</ball>
<ball sellCode="ABC34564" type="golf ball">
...
</xml>
Both the following XPath expressions should work:
/xml/ball[detail[values[id='ART_NR'][value=146334]]
[values[id='FABRICATOR'][value='SPALDING']]]
/xml/ball[detail[values[id='ART_NR' and value=146334]
and values[id='FABRICATOR' and value='SPALDING']]]

XSLT - Sort by a a custom string set

I have an xml as follows
<feed>
<entry>
<id>4</id>
<updated>2012-11-18T16:55:54Z</updated>
<title>ASSIGNED</title>
</entry>
<entry>
<id>3</id>
<updated>2011-01-16T16:55:54Z</updated>
<title>ASSIGNED</title>
</entry>
<entry>
<id>2</id>
<updated>2014-12-01T16:55:54Z</updated>
<title>EXPIRED</title>
</entry>
<entry>
<id>1</id>
<updated>2013-01-12T16:55:54Z</updated>
<title>COMPLETED</title>
</entry>
<entry>
<id>1</id>
<updated>2012-01-09T16:55:54Z</updated>
<title>ASSIGNED</title>
</entry>
<entry>
<id>1</id>
<updated>2011-04-18T16:55:54Z</updated>
<title>COMPLETED</title>
</entry>
</feed>
I want to sort by with ASSIGNED first, then followed by EXPIRED, and then COMPLETED.
If there are more than one entries in each of these categories, I would like to sort by updated value descending.
I can sort by updated descending using xsl:sort, but how do I sort based on a set of strings {ASSIGNED, EXPIRED, COMPLETED} in an order
Appreciate your response!
You can use a translate in the xsl:sort line to convert the first character of the strings "ASSIGNED", "EXPIRED", and "COMPLETED" into simple "1", "2", "3". Since the first characters of your strings are unique, that's all that it takes; it would be harder if there were two strings starting with an "A".
The following example forces a hardcoded <feed> (as the template match itself removes it) and uses an Identity Transform for all other elements.
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/feed">
<feed>
<xsl:apply-templates select="entry">
<xsl:sort select="translate (title, 'AaEeCc', '112233')" />
<xsl:sort select="updated" />
</xsl:apply-templates>
</feed>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Resources