1

I have an XML document that has multiple instances of 'User Area' node as below:

        <UserArea>
            <Property>
                <NameValue name="ShipDate">2022-01-27</NameValue>
            </Property>
            <Property>
                <NameValue name="ShipTime">12:07</NameValue>
            </Property>
            <Property>
                <NameValue name="CartonID">00000100270000031369</NameValue>
            </Property>
            <Property>
                <NameValue name="ShippingID">9</NameValue>
            </Property>
            <Property>
                <NameValue name="WeightType">Actual</NameValue>
            </Property>
            <Property>
                <NameValue name="FreeFreight">yes</NameValue>
            </Property>
            <Property>
                <NameValue name="BOLNo">128018300</NameValue>
            </Property>
        </UserArea>

I am trying to find the very last value for @name="BOLNo".

When I use this XPath Query it returns all 10 of the values: /UserArea/Property/NameValue[@name="BOLNo"]

How can I use last() with this XPath to get only the last instance of this value?

Thanks so much in advance!

AlliDeacon
  • 1,365
  • 3
  • 21
  • 35
  • Since there are only one node with `@name="BOLNo"` it's not quite clear what is your actual problem and why you need the last one.. – JaSON Mar 18 '22 at 12:31

2 Answers2

1

Wrapping in Parens did the trick!

(/UserArea/Property/NameValue[@name="BOLNo"])[last()]
AlliDeacon
  • 1,365
  • 3
  • 21
  • 35
  • 1
    ...if your XPath begins with `/UserArea` then you can't have multiple instances of `` nodes. – Tomalak Mar 18 '22 at 12:57
  • `/UserArea/Property/NameValue[@name="BOLNo" and position()=last()]` should also work. – LMC Mar 18 '22 at 13:00
  • 3
    @LMC no it won't - that only picks the last matching `` in its respective parent. It won't give you the last matching `` of the entire set. Only parentheses do that. – Tomalak Mar 18 '22 at 13:05
  • @Tomalak How about `//NameValue[@name="BOLNo" and position()=last()]`, would that work? – LMC Mar 18 '22 at 13:17
  • 2
    @LMC No, because `position()` checks the parent node when used in a [node test](https://stackoverflow.com/a/2990317/18771). Only when you apply it to a node set (parentheses do that) will it check the position within that set. – Tomalak Mar 18 '22 at 13:34
1

If every UserArea has exactly one BOLNo and all UserArea's belong to the same parent, you also could use:

//UserArea[last()]/Property/NameValue[@name='BOLNo']

If not every UserArea has a BOLNo and all UserArea's belong to the same parent you could also use:

//UserArea[Property/NameValue/@name='BOLNo'][last()]/Property/NameValue[@name='BOLNo']

If all UserArea's do not belong to the same parent, you could also use this simple XPath (is slower at huge xml-content):

(//NameValue[@name="BOLNo"])[last()]
Siebe Jongebloed
  • 3,906
  • 2
  • 14
  • 19
  • `//UserArea[Property/NameValue/@name='BOLNo'][last()]/Property/NameValue[@name='BOLNo']` can still select more than one node - it selects the last `` with a `BOLNo` property *in its respective parent*. So if there are multiple separate sets of ``, you will get as many results. If there are multiple `BOLNo` properties in one ``, you will get them all, too. Only `(...)[last()]` is guaranteed to return the very last such node of the entire document. – Tomalak Mar 20 '22 at 11:01
  • @Tomalak: Your first statement is only true if UserArea can live in different parents. If(as in my assumption) they live in the same root, the last() predicate will only select the last UserArea. Second and third statement correct – Siebe Jongebloed Mar 20 '22 at 11:46
  • That's what I said. *"So if there are multiple separate sets of `` ..."*. My point is that using `(...)[last()]` is *guaranteed* to only ever select a single node, while other uses of `[last()]` only do so under certain circumstances. They are semantically different. – Tomalak Mar 20 '22 at 11:51
  • @Tomalak: I see. I will add some more info to the answer. – Siebe Jongebloed Mar 20 '22 at 13:12