select all except few nodes by xpath - xpath

Just little help. Please let me know the correct xpath expression for this
I have xml doc like this
<parent>
<child id='1' show='true' />
<child id='2' show='true' />
<child id='3' show='true' />
<child id='4' show='true' />
<child id='5' show='true' />
<child id='6' show='true' />
</parent>
I want to select all show attributes except 2nd and 5th childs so that i can turn their values to false

You can use boolean and in your XPath expression:
/parent/child[#id != '2' and #id != '5']
If you really want the second and fifth element, you can use the position() function:
/parent/child[position() != 2 and position() != 5]

Use:
/*/*[not(position() = 2 or position() = 5)]/#show
This selects any show attribute of any child of the top element of the xml document, that isn't the second or the fifth child of its parent.

Related

Get XML attribute value of property using XPath

I have a XML like the example below:
<?xml version="1.0" encoding="UTF-8" ?>
<testsuite errors="0" failures="0" name="test" tests="1" time="2.747">
<properties>
<property name="categories" value="ExampleCategory" />
<property name="timestamp" value="1519664414463" />
</properties>
<testcase classname="com.example.junit.Test" name="test" time="2.747" />
</testsuite>
Is there a way to retrieve the property tag value according to the name of the property?
Right now, I'm using something like that:
#doc.xpath('//testsuite//properties//property/#value').text
This will give me "ExampleCategory1519664414463".
I know if I use .first or [0], [1], etc, I can get the values separately, but I couldn't find a way to get the values separately according to the "name" attribute.
Anyone know how can I retrieve that?
This XPath,
//property[#name='timestamp']/#value
will select all value attributes of property elements with a name attribute value equal to 'timestamp'.

Projection and preserve structure

for the following document
<root>
<child no=1>
<grandchild no=1></grandchild>
<grandchild no=2></grandchild>
<grandchild no=3></grandchild>
</child>
<child no=2>
<grandchild no=1></grandchild>
<grandchild no=2></grandchild>
<grandchild no=3></grandchild>
<grandchild no=4></grandchild>
</child>
</root>
how can I select all children together with only their respective first two grandchildren? I.e. I'd like to return
<child no=1>
<grandchild no=1></grandchild>
<grandchild no=2></grandchild>
</child>
<child no=2>
<grandchild no=1></grandchild>
<grandchild no=2></grandchild>
</child>
My attempt //child/grandchild[#no=1 or #no=2] only gives me
<grandchild no=1></grandchild>,
<grandchild no=2></grandchild>,
<grandchild no=1></grandchild>,
<grandchild no=2></grandchild>
i.e. grandchildren are no longer contained in their respective children.
Many thanks for suggestions and pointers!
bb
Xpath is used to select the tag in the xml, not modify it. you want modify the child tag and xpath can not do it.
But you can use lxml or xml library to parse the xml and modify it.

Formatting legend in Anychart included with Application Express

I am trying to format the legend within Application Express Anychart Flash chart. Here is the relevant XML section:
<legend enabled="true" position="Right" align="Near" elements_layout="Vertical">
<title enabled="true">
<text>Legend</text>
<font family="Tahoma" size="10" color="0x000000" />
</title>
<icon>
<marker enabled="true" />
</icon>
<font family="Tahoma" size="10" color="0x000000" />
</legend>
I am simply trying to add two line items, Sales and Tickets, to describe the lines in my chart with a correctly colored line icons but instead I get two generic entries - Value and Value. Can anyone help me sort out the proper code for this XML?
When I change it to the following:
<legend enabled="true" position="Right" align="Near" elements_layout="Vertical" ignore_auto_item="True">
<title enabled="true">
<text>Legend</text>
<font family="Tahoma" size="10" color="0x000000" />
</title>
<items>
<item>
<text>This is a test</text>
</icon></item>
<item><text>Item 2</text>
</item>
</items>
<icon>
<marker enabled="true" />
</icon>
<font family="Tahoma" size="10" color="0x000000" />
</legend>
This gives me the two series I want but no icon.
Please forgive my ignorance here. I still am not getting the simple legend I want. I am using two series queries, Total_Sales and Total_Tickets:
SELECT NULL Link,
trunc(tix.timestamp) AS label,
sum(tixp.Price) AS value
FROM LS_tickets tix LEFT OUTER JOIN
LS_ticket_prices tixP
ON tixp.series_prefix = tix.ticket_series
WHERE tix.event_id = :P145_event_id
and tix.event_id = tixp.event_id
and tix.voided_flag != 'Y'
GROUP BY trunc(tix.timestamp)
ORDER BY trunc(tix.timestamp) ASC
And
SELECT NULL Link,
trunc(tix.timestamp) AS label,
sum( tixp.quantity ) AS value
FROM LS_tickets tix LEFT OUTER JOIN
LS_ticket_prices tixP
ON tixp.series_prefix = tix.ticket_series
WHERE tix.event_id = :P145_event_id
and tix.event_id = tixp.event_id
and tix.voided_flag != 'Y'
GROUP BY trunc(tix.timestamp)
ORDER BY 1
But I am getting an empty legend whenever i try and add ICON information specific for each label as follows:
<legend enabled="true" position="Right" align="Near" elements_layout="Vertical" ignore_auto_item="True">
<title enabled="true">
<text>Legend</text>
<font family="Tahoma" size="10" color="0x000000" />
</title>
<icon><marker enabled="true" /></icon>
<items>
<item source="Series" series="Total_Sales">
<text>{%Icon} Sales</text>
</item>
<item source="Series" series="Total_Tickets"><text>{%Icon} Tickets</text>
</item>
</items>
<font family="Tahoma" size="10" color="0x000000" />
</legend>
It depends on what data structure you are using. You can specify what data should be shown in legend using these xml settings.
Each automatic item have attributes source which can be "Points" or "Series" and series, that specifies the series name:
http://www.anychart.com/products/anychart/docs/users-guide/index.html?legend-text-formatting.html#automatic-items
In case of custom line items you can add your own items with any information:
http://www.anychart.com/products/anychart/docs/users-guide/index.html?legend-text-formatting.html#custom-items
Here is a list of all keywords that you can use to format the items values:
http://www.anychart.com/products/anychart/docs/users-guide/index.html?legend-text-formatting.html#keywords
it looks like the issue occurs while apex working with the series, all of them are created with the name set to "VALUE. Here is a solution for the similar problem:
https://community.oracle.com/message/12637203#12637203

Need to figure out xpath to get sibling node

I have to return a certain node that is a sibling to a node that I am using to select a certain parent of both nodes...
suppose...
<?xml version="1.0" encoding="utf-16"?>
<dataTemplateSpecification id="id1" name="name1">
<templates xmlns="">
<template>
<elements>
<element id="element0" name="PatientId" display="Patient ID" dataType="String" visable="true" readOnly="false" value="32">
<mapping path="//Template/TemplateData/ACOData/PATIENT_ID" />
<validation>
<rules>
<rule id="r0" test="#element0.value == ''">
<fail>
<html>
<b>Patient ID is null, value must be present</b>
</html>
</fail>
</rule>
</rules>
</validation>
</element>
<element id="element4" name="Active" display="ACTIVE" dataType="String" visable="true" readOnly="true" value="A">
<mapping path="//Template/TemplateData/ACOData/ACTIVE" />
<!--//Templates/Patient/sources/source/empi"/>-->
<validation>
<rules>
<rule id="r1" test="#element1.value == ''">
<fail>
<html>
<b>EMPI ID is null, value must be present</b>
</html>
</fail>
</rule>
</rules>
</validation>
</element>
<element id="element2" name="PopulationPatientID" display="Population Patient ID" dataType="String" visable="true" readOnly="true" enc="223" value="198">
<mapping path="//Template/TemplateData/ACOData/POPULATION_PATIENT_ID" />
<!--Patient/compositeID[./idType='populationPatientID']/id-->
<validation>
<rules>
<rule id="r1" test="#element1.value == ''">
<fail>
<html>
<b>EMPI ID is null, value must be present</b>
</html>
</fail>
</rule>
</rules>
</validation>
</element>
<element id="element1" name="EncounterId" display="Encounter ID" dataType="String" visable="true" readOnly="false" value="223">
<mapping path="//Template/TemplateData/ACOData/FOCUSED_READMISSIONS_ID" />
<validation>
<rules>
<rule id="r0" test="#element0.value == ''">
<fail>
<html>
<b>Patient ID is null, value must be present</b>
</html>
</fail>
</rule>
</rules>
</validation>
</element>
The Xpath I have right now currently only gets the right template. But I need the right element...
//dataTemplateSpecification/templates/template[./elements/element[#name="PopulationPatientID" and #value="198" and #enc="223"]]
I need to xpath to the node that has an attribute named "Active" Is that even possible? I was thinking I might need to drill backwords in the [] section... you know [./../../] where I would be selecting by a finer granularity before then... //dataTemplateSpecification/templates/template/elements/element[./../../] ect.. Does that make sense or am I completely rambling here? Any help would be appreciated. Thanks.
The reason you are getting the template instead of the element with your XPath search is because you are searching for template.
//dataTemplateSpecification/templates/template[./elements/element[#name="PopulationPatientID" and #value="198" and #enc="223"]]
If you want the element instead, you need to specify it before the predicate part of the XPath statement (the predicate being the part in [ ] or brackets).
Also, if you are looking for the element with the name attribute that has a value of "Active", you can specify it as part of your XPath statement.
Either one of the following statements will get the element with that has a name of "Active":
/dataTemplateSpecification/templates/template/elements/element[#name = 'Active']
//element[#name = 'Active']
Use:
/*/*/*/*/element
[#name="PopulationPatientID" and #value="198" and #enc="223"]
/preceding-sibling::element[1]
or even simpler:
/*/*/*/*/element[#name='Active']
#royerboat and #Dimitre Novatchev...
both of you guys had the right idea and it inspired me to get the Xpath that I needed...
//dataTemplateSpecification/templates/template[./elements/element[#name="PopulationPatientID" and #value="198" and #enc="223"]]//element[#name = 'Active']
That xpath is precisely what I need. Dimitre, I tried both of your sugestions and they came close, but no cigar. Thanks for the help guys.

Working With Nested XPath Predicates ... Refined

All great answers! But the question deserves refinement ...
I've got the following sample XML ...
<objects>
<object objectId="1123" ... />
<properties refObjectId="1123" ... />
<properties refObjectId="1123" refPropertyId="2311" ... />
<properties refObjectId="1123" refPropertyId="4611" ... />
<object objectId="2123" ... />
<properties refObjectId="2123" refPropertyId="4311" ... />
<properties refObjectId="2123" refPropertyId="8611" ... />
....
</objects>
... and the following XPath query ...
//object[//properties[#refObjectId=#objectId and not(#refPropertyId)]]
I thought this query would return all object nodes where there is a properties node that has a refObjectId attribute that equals the objectId attribute of the object node and no 'refPropertyId' attribute ... namely object 1123 only, not object 2123 ... but it doesn't. It seems the #objectId in the nested predicate does not refer to the objectId attribute of the object node.
Any ideas? I know the XML structure isn't nested as you would expect, but there are reasons for this structure.
Generally you should avoid using // where you can. I'd consider rephrasing:
//object[../properties/#refObjectId=#objectId]
In the expression provided, your nested predicate is actually checking for
//properties/#refObjectId=//properties/#objectId
of which there are none.
I hope this helps!
EDIT: Since the question has been updated here is an updated response:
You added "It seems the #objectId in the nested predicate does not refer to the objectId attribute of the object node." You're absolutely right! So let's fix it!!
//object[../properties[not(#refPropertyId)]/#refObjectId=#objectId]
This should be closer to what you're after!
Try this:
//objects[object/#objectId = properties/#refObjectId]/object
This should work:
//objects/object[#objectId = ../properties/#refObjectId]
I am not sure how your xml is. However, if it is in the following format:
<objects>
<object objectId="1111" />
<properties refObjectId="1111" />
<object objectId="2111" />
<properties refObjectId="3111" />
<object objectId="4111" />
<properties refObjectId="5111" />
<object objectId="6111" />
<properties refObjectId="4111" />
<object objectId="7111" />
<properties refObjectId="7111" />
</objects>
Then you should use the following xpath to get only objects 1111 and 7111. The result should not include 4111 because the properties where refObjectId = 4111 does not immediately follow the object whose objectId=4111.
//objects/properties[#refObjectId = preceding::object[1]/#objectId]/preceding::object[1]
Assuming that all <properties> nodes that belong to a given <object> actually follow that object (your input seems to imply that), you could do:
/objects/properties[
#refObjectId = preceding-sibling::object[1]/#objectId
and
not(#refPropertyId)
]/preceding-sibling::object[1]
This should perform pretty well.
If you happen to be in XSLT, things get a lot simpler:
<xsl:key name="kPropertiesByObjectId" match="properties" use="#refObjectId" />
and
<xsl:template match="object">
<!-- This tests for an empty node-set. Non-empty node sets can only happen
for objects with at least one <properties> node without #refPropertyId -->
<xsl:if test="key('kPropertiesByObjectId', #objectId)[not(#refPropertyId)]">
<xsl:copy-of select="." />
</xsl:if>
</xsl:template>
In the XSLT case, the order of object and proerties nodes becomes irrelevant.

Resources