27

I have a piece of XML like so:

<root>
    <foo src=""/>
    <foo src="bar"/>
    <foo />
</root>

I want to know which elements have a src attribute, which are empty and which have values.

The furthest I have come is with

$ xmlstarlet sel -t -m '//foo' -v @src -n foo.xml 

bar

Though that doesn't tell me the third foo is missing the attribute.

hendry
  • 9,725
  • 18
  • 81
  • 139

4 Answers4

38

This will select the foos with no src attribute.

/root/foo[not(@src)]

For the other two tasks, I would use a mix of the expressions pointed out by @TOUDIdel and @Dimitre Novatchev: /root/foo[@src and string-length(@src)=0] for foos with an empty src, and /root/foo[@src and string-length(@src)!=0] for foos with an src with content in it.

As an aside, I would avoid using the "anywhere" selector, // (not to mention the * wildcard), unless you're sure that this is specifically what you need. // is like making your very eager dog sniff a piece of cloth and telling it, "bring me everything that smells like this, wherever you find it". You won't believe the weird crap it can decide to bring back.

Jean-François Corbett
  • 37,420
  • 30
  • 139
  • 188
16

I want to know which elements have a src attribute, which are empty and which have values.

Elements having a @src attribute which is empty (no string-value):

//*[@src[not(string())]]

Elements having a @src attribute which has value (string-value):

//*[string(@src)]

From http://www.w3.org/TR/xpath/#section-String-Functions

A node-set is converted to a string by returning the string-value of the node in the node-set that is first in document order. If the node-set is empty, an empty string is returned.

From http://www.w3.org/TR/xpath/#function-boolean

A string is true if and only if its length is non-zero.

Cœur
  • 37,241
  • 25
  • 195
  • 267
  • would `//*[not(string())]` mean something without any attributes? – someone Mar 07 '16 at 22:44
  • With the above, a `src=" "` attribute would be considered a non-empty attribute. Which is why [the above answer](https://stackoverflow.com/questions/5580372/testing-for-an-xml-attribute#5582026) uses the [`normalize-space()`](https://developer.mozilla.org/en-US/docs/Web/XPath/Functions/normalize-space) function. – Jens Jun 04 '19 at 23:47
6

/root/foo[string-length(@src)!=0] return all foo elements have non empty value.

Unfortunately /root/foo[string-length(@src)=0] indicates elements which don't have src attribute and also elements have src attribute but empty.

TOUDIdel
  • 1,322
  • 14
  • 22
  • Well this doesn't solve the problem then! I need to importantly distinguish between when the attribute is missing and when the attribute is empty. :-) – hendry Apr 07 '11 at 12:13
5

Use:

//*[@src and not(string-length(@src))]

This selects all elements in the XML document that have a src attribute whose string-value has length of zero.

//*[@src and string-length(@src)]

This selects all elements in the XML document that have a src attribute whose string-value has length that is not zero.

//*[@src and string-length(normalize-space(@src))]

This selects all elements in the XML document that have a src attribute whose string-value after excluding the starting and ending whitespace has length that is not zero.

//[not(@src)]

This selects all elements in the XML document that don't have a src attribute.

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
  • Still I don't see a method of testing that the src attribute is there at all. :/ I want to flag the `` case for missing the src attribute. – hendry Apr 07 '11 at 13:48
  • @Hendry: Why, all expressions do test exactly that: `someExpr[@src]` means: select all nodes that `someExpr` selects and that have a `src` attribute. – Dimitre Novatchev Apr 07 '11 at 14:37
  • @Dimetre: to spell it out, he means testing that the `src` attribute is NOT there. Just use the `not` operator; see my answer. – Jean-François Corbett Apr 07 '11 at 14:53
  • 1
    @Jean-François-Corbett: Thanks, but this is not what the OP asked in his comment: "a method of testing that the src attribute is there at all" – Dimitre Novatchev Apr 07 '11 at 15:48
  • @hendry: You wrote _"I want to know which **elements have a src attribute**"_. –  Apr 07 '11 at 16:50
  • @Dimitre: I think that second and third could be simplified to `//*[string-length(@src)]` and `//*[normalize-space(@src)]`. Also look up the missing parentheses typo. –  Apr 07 '11 at 16:53
  • @Alejandro:Good catch -- fixed. As for simplifications, the OP made it clear he wants to see if when the attribute *exists* it is "empty. – Dimitre Novatchev Apr 07 '11 at 17:18