简体   繁体   中英

XPath to select the nodes that matches

I have a XML that looks like this:

<?xml version="1.0"?>
<RootName>
  <RandomNode v="someValue"/>
  <Series>
    <Idendity v="C16"/>
    <CodeOut v="C41073"/>
    <Period>
      <TimePeriod v="2013-07-18T22:00Z/2013-07-19T22:00Z"/>
      <Resolution v="PT60M"/>
      <Interval>
        <Pos v="1"/>
        <Qty v="14.1"/>
      </Interval>
      <Interval>
        <Pos v="2"/>
        <Qty v="20.7"/>
      </Interval>

And I need a xPath that returns all the Period nodes which matches these conditions:

  • The node CodeOut / CodeIn has the value of any of the values that I have in an array
  • This node CodeOut can be named CodeOut or CodeIn , but only one of these
  • The date on TimePeriod must match

The only node that repeates over the xml is the Series node. In other words, there is only one Period per Series , but there is a lot of different Series .

For example, get all Period nodes which have his Codeout or CodeIn value to being C41073 or B85028 and the date being 2013-07-18 .

I tried, to match the multiple names, using something like:

//*[@v="C41073"] | //*[@v="B85028"] | ...

But I think it will be better if only matches the correct nodes, just in case some other node has the same value, isn't it?

I was searching to use something like "contains", but it works in a different way.

I'm using .Net, if that matters, and I'm going to use this xPath on the .SelectNodes() function.


EDIT:

Something strange is happening. Maybe the syntax is not correct. Look at this tests:

This: doc.SelectNodes("/*")(0).Name is returning RootName
This: doc.SelectNodes("/*/*").Count is returning 912
This: doc.SelectNodes("/*/*")(11).Name is returning Series

But this: doc.SelectNodes("/RootName").Count is returning 0
This: doc.SelectNodes("/*/Series").Count is returning 0
And this: doc.SelectNodes("/*/RootName").Count is returning 0

Making all the other xPath sequences suggested in answers not working.

EDIT:

Ok, it was the namespace, I did this:

Dim xmlnsManager As Xml.XmlNamespaceManager = New System.Xml.XmlNamespaceManager(doc.NameTable)
xmlnsManager.AddNamespace("ns", "http://example")

And adding ns: before every element node name in the xPath sequence. (See this for more information about it: Is it possible to specify the namespace prefix just once in a xpath expression? )

To select all of the Period elements limited just by the CodeIn / CodeOut list, you could do something like this:

/RootName/Series[(CodeOut/@v = 'C41073') or (CodeOut/@v = 'B85028') or (CodeIn/@v = 'C41073') or (CodeIn/@v = 'B85028')]/Period

If you don't want to list each item in the list as a separate condition, you could concatenate them all together into a delimited list and then use the contains function, like this:

/RootName/Series[(CodeOut/@v and contains('|C41073|B85028|', concat('|', CodeOut/@v, '|'))) or (CodeIn/@v and contains('|C41073|B85028|', concat('|', CodeIn/@v, '|')))]/Period

Notice, to avoid the problem with a substring like C4 matching the full value, like C41073 , you need to concatenate the delimiter before and after the attribute value. Also, you need to make sure your delimiter exists at the beginning and ending of the delimited list of values. Also, whatever delimiter you choose must be an invalid character which would never occur in any of the values in the list.

However, limiting it also by the TimePeriod will be a bit more problematic, since it appears to be a non-standard time range value. If the start and end times were stored in separate nodes, it would be easier.

If all you need to do is match an exact TimePeriod value, for instance, you could just do something like this:

/RootName/Series[(CodeOut/@v = 'C41073') or (CodeOut/@v = 'B85028') or (CodeIn/@v = 'C41073') or (CodeIn/@v = 'B85028')]/Period[TimePeriod/@v = '2013-07-18T22:00Z/2013-07-19T22:00Z']

You can split the string on the / character, with substring-before(TimePeriod, '/') and substring-after(TimePeriod, '/') , but unless you are using XPath 2.0, you can't compare strings to see if they are greater or less than. If you are using 2.0, you could compare each of those substrings with the search value using the compare function, but it's still messy. It's probably best to handle that time-range comparison in your .NET code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM