Anil Kumar
Anil Kumar

Reputation: 131

Selecting a child node having specific value

I want to check that if the "< city>" node 'having a specific value (say Pathankot )' already exist in the xml file under the a particular "< user Id="any"> having a specific Id", before inserting a new city node into the xml.

< users> 
     < user Id="4/28/2015 11:29:44 PM"> 
          <city>Fazilka</city> 
          <city>Pathankot </city> 
          <city>Jalandher</city> 
          <city>Amritsar</city> 
     </user> 
</users> 

In order to insert I am using the Following c# code

 XDocument xmlDocument = XDocument.Load(@"C:\Users\Ajax\Documents\UserSelectedCity.xml");
        string usrCookieId = Request.Cookies["CookieId"].Value;
xmlDocument.Element("Users")
            .Elements("user")
.Single(x => (string)x.Attribute("Id") == usrCookieId)
             //Incomplete because same named cities can be entered more that once 
             //need to make delete function
            .Add(
            new XElement("city", drpWhereToGo.SelectedValue));

My Questions:

Upvotes: 2

Views: 1757

Answers (3)

Rahul Singh
Rahul Singh

Reputation: 21825

Try this:-

First load the XML file into XDocument object by specifying the physical path where your XML file is present. Once you have the object just take the First node with matching condition (please note I am using First instead of Single cz you may have multiple nodes with same matching condition, Please see the Difference between Single & First)

XDocument xmlDocument = XDocument.Load(@"YourPhysicalPath");
xmlDocument.Descendants("user").First(x => (string)x.Attribute("Id") == "1"
                   && x.Elements("city").Any(z => z.Value.Trim() == "Pathankot"))
                 .Add(new XElement("city", drpWhereToGo.SelectedValue));
xmlDocument.Save("YourPhysicalPath");

Finally add the required city to the node retrieved from the query and save the XDocument object.

Update:

If you want to check first if all the criteria fulfills then simply use Any like this:-

bool isCityPresent = xdoc.Descendants("user").Any(x => (string)x.Attribute("Id") == "1"
                  && x.Elements("city").Any(z => z.Value.Trim() == "Pathankot"));

Upvotes: 0

Enigmativity
Enigmativity

Reputation: 117175

I would use this simple approach:

var query =
    xmlDocument
        .Root
        .Elements("user")
        .Where(x => x.Attribute("Id").Value == usrCookieId)
        .Where(x => !x.Elements("city").Any(y => y.Value == "Pathankot"));

foreach (var xe in query)       
{
    xe.Add(new XElement("city", drpWhereToGo.SelectedValue));
}

It's best to avoid using .Single(...) or .First(...) if possible. The description of your problem doesn't sound like you need to use these though.

Upvotes: 1

Andrej Kikelj
Andrej Kikelj

Reputation: 800

I'd create an extension to clean it up a bit, and use XPath for the search.

public static class MyXDocumentExtensions
{
    public static bool CityExists(this XDocument doc, string cityName)
    {
        //Contains
        //var matchingElements = doc.XPathSelectElements(string.Format("//city[contains(text(), '{0}')]", cityName));
        //Equals
        var matchingElements = doc.XPathSelectElements(string.Format("//city[text() = '{0}']", cityName));

        return matchingElements.Count() > 0;
    }
}

And call it like that:

XDocument xmlDocument = XDocument.Load("xml.txt");
var exists = xmlDocument.CityExists("Amritsar");

Expanding on your question in the comment, you can then use it as:

if(!xmlDocument.CityExists("Amritsar"))
{
    //insert city
}

If you would like to match regardless of the trailing whitespace in the XML, you can wrap the text() call in XPath with a normalize-space:

var matchingElements = doc.XPathSelectElements(string.Format("//city[normalize-space(text()) = '{0}']", cityName.Trim()));

Upvotes: 0

Related Questions