XPath following-sibling with multiple conditions - xpath

I have the following xml document. I am trying to select any child which has a sibling with the same value2 and where value3 is not Closed.
<root>
<child>
<value1>Type A</value1>
<value2>Class 1</value2>
<value3>Active</value3>
</child>
<child>
<value1>Type B</value1>
<value2>Class 1</value2>
<value3>Closed</value3>
</child>
<child>
<value1>Type C</value1>
<value2>Class 2</value2>
<value3>Active</value3>
</child>
<child>
<value1>Type D</value1>
<value2>Class 2</value2>
<value3>Active</value3>
</child>
<child>
<value1>Type E</value1>
<value2>Class 3</value2>
<value3>Active</value3>
</child>
</root>
My attempt is:
//child[following-sibling::child/value2=value2 and value3!='Closed']
but this is not having the desired result of applying the multiple conditions to the following-sibling function.
Any ideas?

//child[value2 = following-sibling::child[not(value3 = 'Closed')]/value2]
To apply to any sibling, union the following-sibling and the preceding-sibling axes.
//child[value2 = (following-sibling::child | preceding-sibling::child)[not(value3 = 'Closed')]/value2]

Related

Extract numeric and non-numeric part from a string with WSO2

Thanks before. i want need help. I have simple process flow with wso2. The plan was validate and print string for alphabet and numeric. I can print both of them. but i think the formula was so much affort with that. i want find the simple way. i done try with regular expression. but when i try with that. i always get error result
My code :
<?xml version="1.0" encoding="UTF-8"?>
<api context="/split1" name="SplitAlphaNumber" xmlns="http://ws.apache.org/ns/synapse">
<resource methods="POST">
<inSequence>
<property expression="//OperationValueRegex/Value" name="Value" scope="default" type="STRING"/>
<payloadFactory media-type="xml">
<format>
<OperationValueRegex xmlns="">
<Result1>$1</Result1>
<Result2>$2</Result2>
</OperationValueRegex>
</format>
<args>
<arg evaluator="xml" expression="translate(., translate($ctx:Value,'0123456789',''), '')"/>
<arg evaluator="xml" expression="translate(., translate($ctx:Value,'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',''), '')"/>
</args>
</payloadFactory>
<respond/>
</inSequence>
<outSequence/>
<faultSequence/>
</resource>
</api>
Expected Result was like this
<OperationValueRegex>
<Result1>1234</Result1>
<Result2>Mario Naga</Result2>
</OperationValueRegex>
Sample Input from postman :
<OperationValueRegex>
<Value>Mario Naga 1234</Value>
</OperationValueRegex>
and actual output :
<OperationValueRegex>
<Result1>
1234
</Result1>
<Result2>
MarioNaga
</Result2>
</OperationValueRegex>
Please need suggestion with this. thanks
Here is a simpler way to achieve what you need. Make sure Xpath 2.0 is enabled in WSO2 Server.
Use the following two XPath expressions.
fn:tokenize($ctx:Value, ' ')[matches(., '\d+')] // Tokenize the String with space and extract the part with numeric values.
fn:replace($ctx:Value, ' \d+', '') //Replace the numeric part from the string
PLFactory Mediator
<payloadFactory media-type="xml">
<format>
<OperationValueRegex xmlns="">
<Result1>$1</Result1>
<Result2>$2</Result2>
</OperationValueRegex>
</format>
<args>
<arg evaluator="xml" expression="fn:tokenize($ctx:Value, ' ')[matches(., '\d+')]" xmlns:fn="http://www.w3.org/2005/xpath-functions" />
<arg evaluator="xml" expression="fn:replace($ctx:Value, ' \d+', '')" xmlns:fn="http://www.w3.org/2005/xpath-functions" />
</args>
</payloadFactory>
It seems you do a second translate to get rid of leading/trailing spaces. This messes up your values. Instead you can use normalize-space() function as follows, this gets you the desired output:
<api xmlns="http://ws.apache.org/ns/synapse" name="SplitAlphaNumber" context="/split1">
<resource methods="POST">
<inSequence>
<property name="Value" expression="//OperationValueRegex/Value" scope="default" type="STRING"/>
<payloadFactory media-type="xml">
<format>
<OperationValueRegex xmlns="">
<Result1>$1</Result1>
<Result2>$2</Result2>
</OperationValueRegex>
</format>
<args>
<arg evaluator="xml" expression="normalize-space(translate($ctx:Value,'0123456789',''))"/>
<arg evaluator="xml" expression="normalize-space(translate($ctx:Value,'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',''))"/>
</args>
</payloadFactory>
<respond/>
</inSequence>
<outSequence/>
<faultSequence/>
</resource>
</api>

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.

Selecting Value of Sibling Node When Node = X

When looking at this XML tree:
<ids>
<id>
<id>1</id>
<qty>0.0000</qty>
</id>
<id>
<id>2</id>
<qty>0.0000</qty>
</id>
<id>
<id>3</id>
<qty>0.0000</qty>
</id>
<id>
<id>4</id>
<qty>10.0000</qty>
</id>
<id>
<id>5</id>
<qty>0.0000</qty>
</id>
</ids>
How would one go about choosing the qty with a sibling id of 4?
So far I've tried:
<xsl:value-of select="ids/id[id = '4']/qty"/>
<xsl:value-of select="ids/id/qty[../id = '4']"/>
However, the only result that is ever returned is qty = 0.0000.
Try this XPath-1.0 expression:
/ids/id[id='4']/qty

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']]]

Grouping in XSLT 2.0 (grouping by text)

I have a problem figuring out this grouping in xslt:
The initial information:
<Application>
<ApplicationItem LayoutPath="Attachments.Package.Attachment[bfd0b74d-2888-49d9-a986-df807f08ad8a].UniqueID" Value="bfd0b74d-2888-49d9-a986-df807f08ad8a" />
<ApplicationItem LayoutPath="Attachments.Package.Attachment[bfd0b74d-2888-49d9-a986-df807f08ad8a].Filename" Value="Document 1 Test" />
<ApplicationItem LayoutPath="Attachments.Package.Attachment[bfd0b74d-2888-49d9-a986-df807f08ad8a].URI" Value="https/.test.pdf" />
<ApplicationItem LayoutPath="Attachments.Package.Attachment[bfd0b74d-2888-49d9-a986-df807f08ad8b].UniqueID" Value="bfd0b74d-2888-49d9-a986-df807f08ad8b" />
<ApplicationItem LayoutPath="Attachments.Package.Attachment[bfd0b74d-2888-49d9-a986-df807f08ad8b].Filename" Value="Document 2 Test" />
<ApplicationItem LayoutPath="Attachments.Package.Attachment[bfd0b74d-2888-49d9-a986-df807f08ad8b].URI" Value="google.com" />
</Application>
The expected result:
<Package>
<Attachment UniqueID="bfd0b74d-2888-49d9-a986-df807f08ad8a"
Filename="Document 1 Test"
URI="https/.test.pdf"/>
<Attachment UniqueID="bfd0b74d-2888-49d9-a986-df807f08ad8b"
Filename="Document 2 Test"
URI="google.com"/>
<Package>
My code:
I've done the grouping by using the id from the square brackets.
<xsl:for-each-group select="ApplicationItem[contains(#LayoutPath,'Attachments.Package.Attachment')]" group-by="substring-before(substring-after(#LayoutPath, 'Attachments.Package.Attachment['), ']')">
<Attachment>
<xsl:for-each select="current-group()">
<xsl:attribute name="UniqueID" select="current-grouping-key()"/>
<xsl:attribute name="Filename" select=".[contains(#LayoutPath,'Filename')]/#Value"/>
<xsl:attribute name="URI" select=".[contains(#LayoutPath,'URI')]/#Value"/>
</xsl:for-each>
<Attachment>
</xsl:for-each-group>
My results:
<Package>
<Attachment UniqueID="bfd0b74d-2888-49d9-a986-df807f08ad8a"
Filename=""
URI="https/.test.pdf"/>
<Attachment UniqueID="bfd0b74d-2888-49d9-a986-df807f08ad8b"
Filename=""
URI="google.com"/>
<Package>
What i need to change in code to use the grouping because for now is not working taking only the last ApplicationItem with the unique #LayoutPath.
I think the problem is with the grouping but don't now how to fix it.
Remove the <xsl:for-each select="current-group()"> and change
<xsl:attribute name="Filename" select=".[contains(#LayoutPath,'Filename')]/#Value"/>
<xsl:attribute name="URI" select=".[contains(#LayoutPath,'URI')]/#Value"/>
to
<xsl:attribute name="Filename" select="current-group()[contains(#LayoutPath,'Filename')]/#Value"/>
<xsl:attribute name="URI" select="current-group()[contains(#LayoutPath,'URI')]/#Value"/>

Resources