xpath to exclude elements that have a class - php

I'm trying to select all dom elements that have id="mydiv" but exclude the ones that also have the class="exclass". Right now I'm doing the first part //*[#id="mydiv"]. How do I add the class exclusion part?
P.S. In case you're wondering why I need to select multiple elements that have the same id, I'm just working on an existing DOM that I can't control.

You can use negation:
//*[#id="mydiv" and #class!="exclass"]
If the class attribute may not exist on all nodes, you need this:
//*[#id="mydiv" and (not(#class) or #class!="exclass")]
The last (somewhat) odd logic can be turned into what Michael proposed:
//*[#id="mydiv" and not(#class="exclass")]
Though, personally, the fact that XPath cannot make comparisons if the attribute is missing feels a bit like a shortcoming.

The answer to the question as written is
//*[#id="mydiv" and not(#class="exclass")]
The first half of the condition is true if there is an #id attribute and its value is mydiv. The second half is true if there is no #class attribute with the value exclass: that is, if there is no class attribute, or if there is a class attribute and its value is something other than "exclass".
Avoid using != in this situation: 90% of the time, if you think of writing A!=B, you probably wanted not(A=B). The meaning is different in the case where either A or B is not a singleton, that is, where it is either empty, or can contain multiple values.

Related

What is the difference between DOMXPath::evaluate and DOMXPath::query?

Trying to decide which is more appropriate for my use case...
After comparing the documentation for these methods, my vague understanding is evaluate returns a typed result but query doesn't. Furthermore, the query example includes looping through many results but the evaluate example assumes a single typed result.
Still not much the wiser! Could anyone explain (in as close as possible to layman's terms) when you would use one or the other - e.g. will the multiple/single results mentioned above always be the case?
DOMXPath::query() supports only expressions that return a node list. DOMXPath::evaluate() supports all valid expressions. The official method is named evaluate(), too: http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathEvaluator
Select all p elements inside a div: //div//p
Select all href attributes in a elements the current document: //a/#href
You can use the string() function to cast the first element of a node list to a string. This will not work with DOMXpath::query().
Select the title text of a document: string(/html/head/title)
There are other function and operators that will change the result type of an expression. But it is always unambiguous. You will always know what type the result is.
query will return a DOMNodeList regardless of your actual XPath expression. This suggests that you don't know what the result may be. So you can iterate over the list and check the node type of the nodes and do something based on the type.
But query is not limited to this use case. You can still use this when you know what type you will get. It may be more readable in the future what you wanted to achieve and therefore easier to maintain.
evaluate on the other hand gives you exactly the type that you select. As the examples point out:
$xpath->evaluate("1 = 0"); // FALSE
$xpath->evaluate("string(1 = 0)"); // "false"
As it turns out selecting attributes //div/#id or text nodes //div/text() still yields DOMNodeList instead of strings. So the potential use cases are limited. You would have to enclose them in string: string(//div/#id) or text nodes string(//div/text()).
The main advantage of evaluate is that you can get strings out of your DOMDocument with fewer lines of code. Otherwise it will produce the same output as query.
ThW's answer is right that some expressions will not work with query:
$xpath->query("string(//div/#id)") // DOMNodeList of length 0
$xpath->evaluate("string(//div/#id)") // string with the found id

Use ConfigParser to read an array from ini file [duplicate]

This question already has answers here:
How to ConfigParse a file keeping multiple values for identical keys?
(5 answers)
Closed 9 years ago.
I have read this post, and defined an array in subscriber.ini
[smtp]
subscriber[] = aaa#hotmail.com
subscriber[] = bbb#XX.webmail
subscriber[] = ccc#test.org
Then I try to use ConfigParser to read the array
#!/usr/bin/python
import ConfigParser
CONFIG_FILE = 'subscriber.ini'
config = ConfigParser.ConfigParser()
config.read( CONFIG_FILE )
subscriber = config.get('smtp' , 'subscriber[]' )
print subscriber
It will output the last element, ccc#test.org. But I expect a full subscriber list.
How do I get the array from ini file ?
Python ConfigParser doesn't provide this feature. Use following instead:
[smtp]
subscriber = aaa#hotmail.com bbb#XX.webmail ccc#test.org
Then in your script:
subscriber = config.get('smtp' , 'subscriber').split()
This syntax, where subscriber[] automatically makes subscriber into a list of multiple values, is not a feature of .ini files in general, nor of ConfigParser; it's a feature of Zend_Config_Ini.
In Python, a ConfigParser ini file creates a dict mapping each key to its value. If you have more than one value, it will just override previous values. The magic [] suffix means nothing.
However, the ConfigParser constructor lets you specify a custom dictionary type or factory, in place of the default OrderedDict.
One simple solution would be to use a defaultdict(list) (or an OrderedDefaultDict, which there are recipes for in the docs) for the underlying storage, have __setitem__(self, key, value) do self.dd[key].append(value), and delegate everything else normally. (Or, if you prefer, inherit from defaultdict, override the constructor to pass list to the super, and then just don't override anything but __setitem__.) That will make all of your values into lists.
You could even do something hacky where a value that's only seen once is a single value, but if you see the same name again it becomes a list. I think that would be a terrible idea (do you really want to check the type of config.get('smtp', 'subscriber[]') to decide whether or not you want to iterate over it?), but if you want to, How to ConfigParse a file keeping multiple values for identical keys? shows how.
However, it's not at all hard to reproduce the exact magic you're looking for, where all keys ending in [] are lists (whether they appear once or multiple times), and everything else works like normal (keeps only the last value if it appears multiple times). Something like this:
class MultiDict(collections.OrderedDict):
def __setitem__(self, key, value):
if key.endswith('[]'):
super(MultiDict, self).setdefault(key, []).append(value)
else:
super(MultiDict, self).__setitem__(key, value)
This obviously won't provide all of the extended features that Zend_Config_Ini adds on top of normal .ini files. For example, [group : subgroup : subsub] won't have any special meaning as a group name, nor will key.subkey.subsub as a key name. PHP values TRUE, FALSE, yes, no, and NULL won't get converted to Python values True, False, True, False, and None. Numbers won't magically become numbers. (Actually, this isn't a feature of Zend_Config_Ini, but a misfeature of PHP's leaky typing.) You have to use # comments, rather than freely mixing #, ;, and //. And so on. Any of those features that you want to add, you'll have to add manually, just as you did this one.
As I suggested in a comment, if you really want to have more than two levels of hierarchy, you may be better off with a naturally infinitely-hierarchical format, where any value can be a list or dict of other values.
JSON is ubiquitous nowadays. It may not be quite as human-editable as INI, but I think more people are familiar with it than INI in 2014. And it has the huge advantage that it's a standardized format, and that both Python (2.6+) and PHP (5.2+) come with parsers and pretty-printers for in their standard libraries.
YAML is a more flexible and human-editable format. But you will need third-party modules in both languages (see the list at the YAML site). And it can also bring in some security concerns if you're not careful. (See safe_load and friends in the PyYAML docs; most other libraries have similar features.)
I wonder if you have considered using Michael Foord's configobj module? It seems to be capable
of doing what you want, which might be better that trying to pervert ConfigParser to do what you apparently need.

Properly detecting numeric data in ajax xml response

I'm using jQuery to post ajax requests, and PHP to construct XML responses. Everything works fine, but I wonder about the method I've used for data typing, and whether there's a more standard way, or a more correct way. My XML generally looks like this, with some attributes representing text and other attributes representing numeric data:
<UnitConversions>
<UnitConversion basicUnitName="mile" conversionFactor="5280" conversionUnit="foot"/>
<UnitConversion basicUnitName="mile" conversionFactor="1760" conversionUnit="yard"/>
</UnitConversions>
I have a lot of different objects, not just this one type, so in my constructors, rather than initializing every property explicitly, I just copy the attributes over from the XML node:
var UnitConverter = function(inUnitConversionNode) {
var that = this;
$.each(inUnitConversionNode.attributes, function(i, attribute) {
that[attribute.name] = attribute.value;
});
};
I had trouble early on when I checked for numeric values, as in if(someValueFromTheXML === 1) -- this would always evaluate to false because the value from the XML was a string, "1". So I added nodes in key places in the XML to tell my client-side code what to interpret as numeric and what to leave as text:
<UnitConversions>
<NumericFields>
<NumericField fieldName="conversionFactor"/>
</NumericFields>
<UnitConversion basicUnitName="mile" conversionFactor="5280" conversionUnit="foot"/>
<UnitConversion basicUnitName="mile" conversionFactor="1760" conversionUnit="yard"/>
</UnitConversions>
So now I pass the NumericFields node into the constructor so it will know which fields to store as actual numbers.
This all works great, but it seems like a bit of a naive solution, maybe even a hack. Seems like there would be something more sophisticated out there. It seems like this issue is related to XML schemas, but my googling seems to suggest that schemas are more about validation, rather than typing, and they seem to be geared toward server-side processing anyway.
What's the standard/correct way for js to know which fields in the XML are numeric?
You can use isNaN() to detect whether the string is a number. For example isNaN("5043") returns false indicating that "5043" can be parsed as a number. Then, just use parseInt() to compare the value. For example:
if (parseInt(someValueFromTheXML, 10) === 1) {
...
}
Another way is to use loose comparison with the == operator so that "1" == 1 evaluates to true. However, it would be better practice to use the first suggestion instead. There is really no other way to go about this since XML/HTML attributes are always strings.

How do I render the path array key in Drupal 7 where field type = node reference

I have a node reference field which I can output using echo render( $content['field_link'] );
This is fine for one situation in the node but I also need to output just the path of that node. I can output it using echo $node->field_link['und'][0]['node']->uri['path'] but I don't want to hard code the 'und' and '0' array keys in. There is presumably a way to do it with render().
If someone could point me in the right direction that'd be great, thanks.
Ben
You can't do it with render but you don't need to hard code the language code, you can get it from the global variables:
global $language;
echo $node->field_link[$language->language][0]['node']->uri['path']
You won't be able to get around using the 0 key though, all fields are stored with the potential to be multiple so you will always need to 'pick' which element you want to get.
If the cardinality of your field is 1, you can always assume that the element you're looking is for is at field_link[$language->language][0]. If not you'd need to run through each element in the und array and decide which one to display.
EDIT
You can also use the LANGUAGE_NONE constant (which will generally return 'und', but either way wil be the correct language code for the default content).

DIfference between Select all href attributes and Select all anchor elements with href attributes using xpath with php

Given that in a html document all anchor elements have a href attribute that is set:
What is the difference between the following xpath queries
Case 1:
//#href
Case 2:
//a/#href
Case 3:
*/a/#href
In my situation:
//#href returns all the data contained in the href attribute.
//a/#href returns nothing but I expect it to return the same as //#href
*/a/#href returns nothing but I expect it to return the same as //#href and //a/#href
I fear that I am grossly misunderstanding how these queries work. Is there anybody that can set me straight. Thank you in advance for your assistance.
What is the difference between the
following xpath queries
Case 1:
//#href
This selects all href attribute nodes in the XML document.
Case 2:
//a/#href
This selects all href attributes belonging to any element named a that is in no namespace.
Case 3:
*/a/#href
This selects all href attributes belonging any element named a that is in no namespace and that is a grandchild of the current (context) node.
Most probably you wanted to write:
//*/a/#href
This selects all href attributes belonging any element named a that is in no namespace and whose parent is an element.
In my situation:
//#href returns all the data
contained in the href attribute.
//a/#href returns nothing but I
expect it to return the same as
//#href
*/a/#href returns nothing but I
expect it to return the same as
//#href and //a/#href
I fear that I am grossly
misunderstanding how these queries
work. Is there anybody that can set me
straight. Thank you in advance for
your assistance.
A very frequent situation is that the XML document has a default namespace. This is most probably the cause of your problem. In this case no a element that is in "no namespace" exists in the document and any XPath expression having a as a location step selects nothing.
Apart from this, the three expressions are not equivalent.
//#href and //a/#href
could select different sets of nodes if in the document there are other elements except a that have an href attribute, or if the document is in a default namespace. In the last case the second expression selects nothing.
//a/#href and //*/a/#href
could select different sets of nodes if the top element of the document is an a that has an href attribute. The href attribute of this top element is selected by the first XPath expression but is not selected by the second, because the parent of the top element is not an element (it is the root node /).
You haven't shown the source document. But I'm prepared to bet you've made the #1 XSLT mistake, which is to forget or fail to notice that it declares a default namespace, which means an unprefixed name like //a will not select any elements.
If you want to select a node that has an attribute, use this syntax: //a[#href]. I'm not entirely sure why the other method doesn't work, as it makes sense in principle. Just how XPath is I guess.

Categories