0

I have referred XPathSelectElement select the second when there is more than one and XPath and XPathSelectElement. But this is a different question.

I have following xml. I need to find out the Message element value (from StatusDetail) corresponding to Sequence 2. If there is no sequence 2 present, it should return null. What is the best way to do this in C# using XPathSelectElement?

Note: There can be any number of StatusDetail (or not at all)

Note: The StatusDetail element can be in any order. We need to look only for the value “2” in 2

CODE

 XDocument xDoc = XDocument.Parse(@"  
          <Status>
            <StatusMsg>
                <StatusType>INVOICE</StatusType>
                <StatusCode>READYPAY</StatusCode>
                <StatusTimestamp>2013-03-19T21:20:54Z</StatusTimestamp>
                <StatusDetail>
                <Sequence>1</Sequence>
                <Message>.Document posted successfully </Message>
                </StatusDetail>
                <StatusDetail>
                <Sequence>2 </Sequence>
                <Message>Invoice is ready for pay</Message>
                </StatusDetail>
            </StatusMsg>
            </Status>
           ");

 var statusDetails = xDoc.XPathSelectElements(@"Status/StatusMsg/StatusDetail");

UPDATE

Following is the solution that I am using based on the selected answer

var statusDetails = xDoc.XPathSelectElements(@"Status/StatusMsg/StatusDetail/Sequence[text()=2]/../Message").FirstOrDefault();
if (statusDetails != null)
{
     selectedMessage = statusDetails.Value;
}
Community
  • 1
  • 1
LCJ
  • 22,196
  • 67
  • 260
  • 418

2 Answers2

1
var statusDetails = xDoc.XPathSelectElements(@"Status/StatusMsg/StatusDetail/Sequence[text()=2]/../Message");

It uses text() to select element by its value and parent selector /.. to get back from that element to its parent.

Add ToList() or SingleOrDefault to enumerate results and save it into a list or single XElement object.

Update

LINQ to XML query version:

var results = from sd in xDoc.Root.Elements("StatusMsg").Elements("StatusDetail")
              let s = sd.Element("Sequence")
              where s != null && ((string)s).Trim() == "2"
              select (string)sd.Element("Message");

and with Method-based query:

results = xDoc.Root.Elements("StatusMsg").Elements("StatusDetail")
              .Select(sd => new { sd, s = sd.Element("Sequence") })
              .Where(x => x.s != null && ((string)x.s).Trim() == "2")
              .Select(x => (string)x.sd.Element("Message"))

You can add another .Where(x => x != null) to skip null results (which exists when there is StatusDetail with Seqience == 2, but no Message element.

MarcinJuraszek
  • 124,003
  • 15
  • 196
  • 263
  • I've tested it and it works. What problem do you have with it? – MarcinJuraszek Mar 20 '13 at 08:47
  • I don't understand you. How do you try applying `Root` into `XPathSelectElements`? Add `ToList()` to enumerate the query and get results as `List` – MarcinJuraszek Mar 20 '13 at 08:58
  • Following is what I am using. It works fine. var statusDetails = xDoc.XPathSelectElements(@"Status/StatusMsg/StatusDetail/Sequence[text()=2]/../Message").FirstOrDefault(); – LCJ Mar 20 '13 at 09:08
1

Why not using LINQ to XML:

var result = xDoc.Descendants("StatusDetail")
            .Where(x =>
                       {
                           var xElement = x.Element("Sequence");
                           return xElement != null && xElement.Value.Trim() == "2";
                       })
            .Select(x => (string)x.Element("Message"))
            .SingleOrDefault();
cuongle
  • 74,024
  • 28
  • 151
  • 206
  • If we encounter a `StatusDetail` element (with value 2) without a `Message` element, it will fail. Any suggestions? – LCJ Mar 20 '13 at 07:04
  • 2
    You can use `(string)x.Element` to avoid `(?:)` consitional statement. However, I think `XPath` is more readable in that case. – MarcinJuraszek Mar 20 '13 at 07:12
  • It fails if there is a `StatusDetail` element without `Sequence` – LCJ Mar 20 '13 at 07:12
  • Need to use FirstOrDefault instead of SingleOrDefault, as there can be multiple elements with same sequence – LCJ Mar 20 '13 at 09:03