I have some XML like this:
<engine-set>
<engine host-ref="blah1.com">
<property name="foo" value="true"/>
<property name="bar" value="true"/>
</engine>
<engine host-ref="blah2.com">
<property name="foo" value="true"/>
<property name="bar" value="false"/>
</engine>
</engine-set>
I want to match on all engine elements that have a child node property with a name equal to "bar" and and value equal to "true". I'm finding the fact that "true" appears in my XML is causing my condition to always evaluate to true in an XPath expression. Is there a way around? I'm using Python and lxml.
EDIT:
My xpath expression is (that isn't working) is:
//engine[(property/#name='bar' and property/#value="true")]
Thanks,
I want to match on all engine elements
This is:
//engine
that have a child node property
Now this becomes:
//engine[property]
with a name equal to "bar"
Still more specific:
//engine[property[#name = 'bar']]
and and value equal to "true".
Finally:
//engine[property[#name = 'bar' and #value = 'true']]
So you're saying
//engine[property[#name='bar' and #value='true']]
gives you too many results? Because for me it gives just one.
What XPath expression did you try?
The following seems to work well in getting "blah1.com" but not "blah2.com":
//engine[property[#value="true"][#name="bar"]]
Remember that you need to encase your parameter test values in quotes.
Related
I want to remove a node from an XML file (using SaxonHE9-8-0-11J):
<project name="Build">
<property name="src" value="src/main/resources" />
<property name="target" value="target/classes" />
<condition property="target.exists">
<available file="target" />
</condition>
</project>
Apparently there are 2 ways I can do this.
XPath1: using a not function
XPath2: using an except clause. But both simply return the entire node-set.
With a not function:
saxonb-xquery -s:test.xml -qs:'*[not(local-name()="condition")]'
With an except clause:
saxonb-xquery -s:test.xml -qs:'* except condition'
With -explain switch the queries are:
<query>
<body>
<filterExpression>
<axis name="child" nodeTest="element()"/>
<operator op="ne (on empty return true())">
<functionCall name="local-name">
<dot/>
</functionCall>
<literal value="condition" type="xs:string"/>
</operator>
</filterExpression>
</body>
</query>
and
<query>
<body>
<operator op="except">
<axis name="child" nodeTest="element()"/>
<path>
<root/>
<axis name="descendant" nodeTest="element(condition, xs:anyType)"/>
</path>
</operator>
</body>
</query>
In general, XPath select nodes from one or more input documents, it doesn't allow you to construct new ones, for that you need XSLT or XQuery. And removing the condition child of the project root, if that is what you want to achieve, is something you need XSLT or XQuery for, with XPath, even if you use /*/(* except condition), you then get all children except the condition element, but as a sequence, not wrapped into a a root.
So with XQuery you could use
/*/element {node-name()} { * except condition }
as a compact but generic way to reconstruct any root with all child elements except the condition: https://xqueryfiddle.liberty-development.net/948Fn5b
Whether you get such an expression through a command line shell is a different problem, on Windows with a Powershell window and the cmd shell it works for me to use
-qs:"/*/element {node-name()} { * except condition }"
I'm trying to write a check in Schematron that will ensure no elements contain duplicated attribute data. These elements are at a specific location in the XML document, I have the XPATH that locates them.
For example:
should fail because it has duplicate foo and bar attribute values.
<id foo="test1" bar="abc" />
<id foo="test1" bar="abc" />
This should pass as the foo attributes are not the same.
<id foo="test1" bar="abc" />
<id foo="test2" bar="abc" />
I'm not sure if this is too complicated for Schematron.
Any thoughts?
I don't know Schematron, but if you're able to use XPath 2.0 (which is possible at least with some implementations), deep-equal($val1, $val2) will come in handy.
not(deep-equal(<id foo="test1" bar="abc" />, <id foo="test1" bar="abc" />)) (: false :)
not(deep-equal(<id foo="test1" bar="abc" />, <id foo="test2" bar="abc" />)) (: true :)
If not, there should be a solution using XSLT 1.0, but you will have to construct the recursive comparisons on your own (and I don't know XSLT well enough to do so).
I would do it this way in Schematron (checked with XML ValidatorBuddy):
<iso:pattern id="unique name attributes">
<iso:rule context="id">
<iso:assert test="count(id) = count(id[not(#foo=preceding-sibling::person/#foo)])">
Not all foo attributes of the id elements are unique
</iso:assert>
</iso:rule>
</iso:pattern>
You can also add a check for the bar attribute here.
Given this XML fragment (I've removed superfluous fluff):
<Event name="DataComplete">
<Task id="d20a0053-7678-43ba-bc8a-ece24dcff15b"/>
<DataItems>
<DataItem name="Survey" type="task">
<Value status="NotStarted" taskId="00000000-0000-0000-0000-000000000000" />
</DataItem>
<GroupDataItem name="CT_Visit"> --- this may repeat
<ItemGroup id="1" >
<DataItem name="Special Contractor" type="string">Yes</DataItem>
What xPath expression will determine if any DataItem with name="Special Contractor" has the value "Yes".
I'm trying something like this:
Yes = /Event/Task/DataItems/GroupDataItem/ItemGroup/DataItem/#[normalize-space() = 'Special Contractor']
and many variations usually resulting in "invalid xPath expression".
Any clues most welcome. Thanks!
[EDIT]
Thanks for the answers Jiri and Will. Will was close, but as my question states, I'm trying to determine if any* element has the value Yes. I should have been more explicit in saying that I need a boolean, true or false. Adapting Will's answer led me to this:
"Yes" = //Event/Task/DataItems/GroupDataItem/ItemGroup/DataItem[#name='Special Contractor']
This returns a simple Boolean='true' or Boolean='false'.
Thanks guys!
/Event/DataItems/GroupDataItem/ItemGroup/DataItem[#name = "Special Contractor"][. = "Yes"]
Returns the DataItem in question. Note that this will be a sequence of matching DataItem elements if there are more than one. If you just want a boolean:
exists(/Event/DataItems/GroupDataItem/ItemGroup/DataItem[#name = "Special Contractor"][. = "Yes"])
(as an aside; I removed Task from the xpath, since it's not actually an ancestor of the DataItem in the XML fragment you posted, even though the indentation makes it look like it is.)
Use this xpath
/Event/Task/DataItems/GroupDataItem/ItemGroup/DataItem[#name='Special Contractor']
for following xml:
<Event name="DataComplete">
<Task id="d20a0053-7678-43ba-bc8a-ece24dcff15b">
<DataItems>
<DataItem name="Survey" type="task">
<Value status="NotStarted" taskId="00000000-0000-0000-0000-000000000000" />
</DataItem>
<GroupDataItem name="CT_Visit"> --- this may repeat
<ItemGroup id="1" >
<DataItem name="Special Contractor" type="string">Yes</DataItem>
</ItemGroup>
</GroupDataItem>
</DataItems>
</Task>
...
</Event>
If the task is really non-pair element, then omit it from the xpath expression.
Although I know that I can use "e, I was wondering if there was a less blunt and long way, such as \", or the like.
Here is an example of the XML:
<root name="test" type="Node" action="{puts :ROOT.to_s}">
<leaf type="Node" decider="{print :VAL1.to_s; gets.chomp.to_i}" action="{puts :ONE.to_s}" />
<leaf type="Node" decider="{print :VAL2.to_s; gets.chomp.to_i}" action="{puts :TWO.to_s}" />
<branch type="Node" decider="{100}" action="{}">
<leaf type="LikelihoodNode" decider="{100}" action="{puts :HI.to_s}" arg="0"/>
</branch>
</root>
The attributes that need this are decider and action. Right now the embedded code is using a little :sym.to_s hack, but that is not a solution.
NOTE: Although the action attribute is only a block in brackets, the processing code pre-pends the lambda.
A double quote inside an XML attribute is written as "e; (or " or "). You'll have similar issues with single quotes too so you can't use those. However, you can use % as-is in an XML attribute so %|...|, %Q|...|, and %q|...| are available and they're as easy to read and type as quotes:
<root name="test" type="Node" action="{puts %|ROOT|}">
<leaf type="Node" decider="{print %|VAL1|; gets.chomp.to_i}" action="{puts %|ONE|}" />
<!-- ... -->
</root>
Choose whichever delimiters you find the easiest to type and read.
You can also use single quotes for your attributes in XML so you can have:
<leaf type='Node' decider='{print "VAL1"; gets.chomp.to_i}' ...
But then you'd have to use ' inside the attribute if you needed to include a single quote.
Alternatively, you could switch to elements instead of attributes:
<leaf type="Node">
<decider><![CDATA[
print "VAL1"
gets.chomp.to_i
]]></decider>
<action><![CDATA[
puts "ONE"
]]></decider>
</leaf>
but that's a bit verbose, ugly, and not as easy to work with as attributes (IMHO).
The code below will get me all nodes with name=visible, like this node:
<property name="visible" value="false"/>
vis = #noko_obj.search("property[name=visible]")
...regardless of what value is. However, what if I want the nodes whose name="visible" AND whose value="true"?
thanks
Just add another attribute selector:
vis_true = #noko_obj.search('property[name=visible][value=true]')
The order of the attribute selectors doesn't matter so you could also do this:
vis_true = #noko_obj.search('property[value=true][name=visible]')