Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to retrieve last node using XPath in C#?

Tags:

c#

xml

xpath

I have following part of xml file.

<UN N="@U1">
    <DT N="24/06/2011">
        <PN N="@P1">
            <TM N="02:24:11">
                <JB T="GP">
                    <A>notepad</A>
                    <Z>Notepad</Z>
                    <N>Untitled - Notepad</N>
                    <J>1;0;1;1;0;0</J>
                    <C>0.00500;0.09500;0.03500</C>
                    <S>1;0;1;1</S>
                    <P>0;0</P>
                    <F>0</F>
                </JB>
            </TM>
        </PN>
    </DT>

    <DT N="23/06/2011">
        <PN N="@P1">
            <TM N="02:38:49">
                <JB T="PAGP">
                    <A>notepad</A>
                    <Z>Notepad</Z>
                    <N>Untitled - Notepad</N>
                    <J>1;1;1;1;0;1</J>
                    <C>0.00500;0.09500;0.03500</C>
                    <S>1;1;0;0</S>
                    <P>1;1</P>
                    <F>0</F>
                </JB>
            </TM>               
        </PN>
    </DT>

    .....
    .....

</UN>

I need to get the last node where PN N=@P1.

Sample query will be appreciated.

Thanks in advance.

like image 365
Syed Avatar asked Aug 24 '11 17:08

Syed


2 Answers

The XPath operation:

"/UN/DT[PN/@N='@p1'][last()]"

will return the last DT node for which PN=@p1.

like image 87
Emiliano Poggi Avatar answered Sep 19 '22 23:09

Emiliano Poggi


The last() XPath function can be used to find the last of a node set. So the last <PN> node with attribyte N having a value @P1 would be:

//PN[@N='@P1'][position() = last()]

The .NET code will depend on which XML API you're using: XmlDocument, XPathDocument or LINQ to XML (XDocument). (The approach with XmlReader would be to load into one of the three and then use that.)

Edit (based on comment) To return the <F> descendant of the last <PN> with attribute as above, where xDoc is an instance of XmlDocument:

var foundNode = xDoc.SelectSingleNode("//PN[@N='@P1'][position() = last()]/TM/JB/F");

Edit #2 (based on another comment): To return the last <PN> where each <DT> can contain multiple <DT> elements1 across all the <DT> elements.

Testing this with some better sample XML (see below), using XmlElement.SelectNodes and then iterating over the returned node set showed that the right node was being found, it just wasn't the first and therefore not returned by SelectSingleNode. This is the clue: the last() predicate was being applied within each //PN[@N='@P1'] matching. A quick change of precedence was all that was needed:

(//PN[@N='@P1'])[last()]

1 For future reference, if multiple elements are possible, then show at least two in the sample, otherwise readers will assume only singular instances. Also strip out all nodes (like children) not needed:

The sample XML I used for testing (added x attributes to make it easy to identify the element selected while testing):

<UN N='@U1'>
  <DT N='24/06/2011'>
    <PN N='@P1' x='#1'/>
    <PN N='@P1' x='#2'/>
    <PN N='@P2' x='#3'/>
  </DT>

  <DT N='24/06/2011'>
    <PN N='@P1' x='#4'/>
    <PN N='@P1' x='#5'/>
    <PN N='@P2' x='#6'/>
  </DT>

  <DT N='24/06/2011'>
    <PN N='@P3' x='#7'/>
    <PN N='@P4' x='#8'/>
    <PN N='@P5' x='#9'/>
  </DT>
</UN>
like image 33
Richard Avatar answered Sep 21 '22 23:09

Richard