2

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:

  • How Can i check weather the < city> node having specific value say Pathankot already exist in the xml file Before Inserting a new city node.
  • I am Using absolute Path in" XDocument xmlDocument =
    XDocument.Load(@"C:\Users\Ajax\Documents\Visual Studio
    2012\WebSites\punjabtourism.com\UserSelectedCity.xml");"
    This does not allow me to move the files to new folder without changing
    the path which is not desirable. But if i use the relative path The
    Error Occures "Access Denied";
Rahul Singh
  • 21,585
  • 6
  • 41
  • 56
Anil Kumar
  • 101
  • 10

3 Answers3

1

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.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
0

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"));
Community
  • 1
  • 1
Rahul Singh
  • 21,585
  • 6
  • 41
  • 56
  • Can i use this expression under If() Like 'If(xmlDocument.Descendants("user").First(x => (string)x.Attribute("Id") == "1" && x.Descendants("city").Any(z => z.Value.Trim() == "Pathankot"))'{ //Then Do not make an entry } – Anil Kumar Apr 29 '15 at 06:44
  • 1
    @AnilKumar - Nope because it will return an `XElement` not a boolean if you want to check that first then use my updated code. – Rahul Singh Apr 29 '15 at 06:49
  • Thanx It works well! Would You mind answering my second Question? – Anil Kumar Apr 29 '15 at 06:58
  • @AnilKumar - I didn't get your second question. What is the issue you can give the physical path where XML is present and save to whatever location you want. – Rahul Singh Apr 29 '15 at 07:06
  • Yeah i'm giving the absolute path in XDocument xmlDocument = XDocument.Load(@"YourPhysicalPath"); if the wesite folder is moved to some other directory i need to modify the PhysicalPath appropriately. I dont want to do so. Any alternative from giving the PhysicalPath.? – Anil Kumar Apr 29 '15 at 07:08
  • Did you try running this code? It only adds "Pathankot" when it already **exists** in the XML and it fails it is not there. This code doesn't work. – Enigmativity Apr 29 '15 at 07:13
  • @Enigmativity - Yeah i tried and its working as expected. OP wants to add only when `City exists` for particular user please check the first line. – Rahul Singh Apr 29 '15 at 07:19
  • 1
    @AnilKumar - Okay in that case you should be using `Server.MapPath` but it will only work if the desired XML is already present in your Web Application project. – Rahul Singh Apr 29 '15 at 07:20
  • It's also lazy using `.Descendants(...)` this way. If the structure of the email is changed (or more detailed than we've been told) then this can fail. – Enigmativity Apr 29 '15 at 07:22
  • @RahulSingh - He wants to check if the city exists **before** inserting it - meaning he will only insert it if it **doesn't** exist. I believe he could have made his intent clearer though. – Enigmativity Apr 29 '15 at 07:24
  • @Enigmativity - Okay agree with your point of using `Descendants`. Thanks and lets wait for OP to clear his requirement rather than guessing and arguing:) – Rahul Singh Apr 29 '15 at 07:27
  • 1
    if (!isCityPresent){//Then Make an entry} I'm using it in this way and its working. – Anil Kumar Apr 29 '15 at 07:28
  • Speak Up @DownVoters! Don't just blindly downvote if you don't have anything to say! – Rahul Singh Apr 29 '15 at 08:32
0

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()));
Andrej Kikelj
  • 800
  • 7
  • 11