LCJ
LCJ

Reputation: 22652

Selecting element based on other element values

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;
}

Upvotes: 0

Views: 448

Answers (2)

MarcinJuraszek
MarcinJuraszek

Reputation: 125610

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.

Upvotes: 1

cuongle
cuongle

Reputation: 75296

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();

Upvotes: 1

Related Questions