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";
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.
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"));
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()));
Related
I have an XML document containing processing instructions. I know that, with the XmlDocument class, you can use
var node = xmlDoc.SelectSingleNode("processing-instruction('xml-stylesheet')") as XmlProcessingInstruction;
but I want to use XDocument. How can I do this?
This is how I access an XML file's nodes with the XDocument class.
However, you'll have to be more specific on what you want to do with it.
XDocument doc = XDocument.Load("filepath");
var node = doc.Nodes().OfType<XElement>().SingleOrDefault(n => n.Name == "node name");
var node_value = node.Value;
var node_descendants = node.Descendants();
UPDATE:
As you may have noticed there's no SelectSingleNode in XDocument, in fact, to retrieve the node you want you'll have to fetch it from the corresponding ienumerable collection, or alternatively from the predefined FirstNode, NextNode, PreviousNode, LastNode, but you cannot apply any filters to those. Therefore the only ways to retrieve ProcessingInstruction nodes are
var pI_nodes = doc.Nodes().OfType<XProcessingInstruction>();
And
var pI_nodes = (from node in doc.Nodes()
where node.NodeType == System.Xml.XmlNodeType.ProcessingInstruction
select node);
If you expect to retrieve several ProcessingInstructions and need to filter these as well, the equivalent to the node name would the Target property
var filtered_pIs = pI_nodes_1.Where(pI => pI.Target == "xml-stylesheet");
And as a final reminder the value of the processing instruction is stored in the Data property.
string pI_value = filtered_pIs.First().Data
Here is one way:
var node = xDoc.Root.Nodes().OfType<XProcessingInstruction>().First();
I have this XML and i try to read the scpefic nodes but not work =(
the my code is:
XmlDocument doc = new XmlDocument();
doc.Load("https://apps.db.ripe.net/whois/search.xml?query-string=193.200.150.125&source=ripe");
XmlNode node = doc.SelectSingleNode("/whois-resources/objects/attributes/descr");
MessageBox.Show(node.InnerText);
the two circled values on image
Url: https://apps.db.ripe.net/whois/search.xml?query-string=193.200.150.125&source=ripe
it is possible?
When you are looking for a node which has an attribute "name" set to a particular value, you need to use different syntax.
You're looking for something like:
XmlNode node = doc.SelectSingleNode("/whois-resources/objects/object/attributes/attribute[#name=\"descr\"]");
XmlAttribute attrib = node.Attributes["value"];
MessageBox.Show(attrib.Value);
This will select your second node example, get the value of the value attribute, and display it.
How about using Linq To Xml?
var xDoc = XDocument.Load("https://apps.db.ripe.net/whois/search.xml?query-string=193.200.150.125&source=ripe");
var desc = xDoc.Descendants("attribute")
.Where(a => (string)a.Attribute("name") == "descr")
.Select(a => a.Attribute("value").Value)
.ToList();
or
var desc = xDoc.XPathSelectElements("//attribute[#name='descr']")
.Select(a => a.Attribute("value").Value)
.ToList();
I have a case where a user uploads an XML file. I am using XDocument to handle it. However, I want to replace the stylesheet used with one on the server. Is there a way to use XDocument to modify the
I can get the expression with linq, as an XmlNode, as shown below:
var node = from proc in doc.Nodes()
where proc.NodeType == XmlNodeType.ProcessingInstruction
select proc;
But I cannot figure out how to replace the node.
Update: I feel like an idiot...
var node = doc.Nodes().Where(type => type.NodeType == System.Xml.XmlNodeType.ProcessingInstruction).Single() as XProcessingInstruction;
node.Data = "href=\"" + Utilities.Utility.XSLLocation + "\" type=\"text/xsl\"";
I have this XML file:
<MyXml>
<MandatoryElement1>value</MandatoryElement1>
<MandatoryElement2>value</MandatoryElement2>
<MandatoryElement3>value</MandatoryElement3>
<CustomElement1>value</CustomElement1>
<CustomElement2>value</CustomElement2>
<MyXml>
All 3 elements that are called 'MandatoryElementX' will always appear in the file. The elements called 'CustomElementX' are unknown. These can be added or removed freely by a user and have any name.
What I need is to fetch all the elements that are not MandatoryElements. So for the file above I would want this result:
<CustomElement1>value</CustomElement1>
<CustomElement2>value</CustomElement2>
I don't know what the names of the custom elements may be, only the names of the 3 MandatoryElements, so the query needs to somehow exclude these 3.
Edit:
Even though this was answered, I want to clarify the question. Here is an actual file:
<Partner>
<!--Mandatory elements-->
<Name>ALU FAT</Name>
<InterfaceName>Account Lookup</InterfaceName>
<RequestFolder>C:\Documents and Settings\user1\Desktop\Requests\ALURequests</RequestFolder>
<ResponseFolder>C:\Documents and Settings\user1\Desktop\Responses</ResponseFolder>
<ArchiveMessages>Yes</ArchiveMessages>
<ArchiveFolder>C:\Documents and Settings\user1\Desktop\Archive</ArchiveFolder>
<Priority>1</Priority>
<!--Custom elements - these can be anything-->
<Currency>EUR</Currency>
<AccountingSystem>HHGKOL</AccountingSystem>
</Partner>
The result here would be:
<Currency>EUR</Currency>
<AccountingSystem>HHGKOL</AccountingSystem>
You can define a list of mandatory names and use LINQ to XML to filter:
var mandatoryElements = new List<string>() {
"MandatoryElement1",
"MandatoryElement2",
"MandatoryElement3"
};
var result = xDoc.Root.Descendants()
.Where(x => !mandatoryElements.Contains(x.Name.LocalName));
Do you have created this xml or do you get it by another person/application?
If it's yours I would advise you not to number it. You can do something like
<MyXml>
<MandatoryElement id="1">value<\MandatoryElement>
<MandatoryElement id="2">value<\MandatoryElement>
<MandatoryElement id="3">value<\MandatoryElement>
<CustomElement id="1">value<\CustomElement>
<CustomElement id="2">value<\CustomElement>
<MyXml>
In the LINQ-Statement you don't need the List then.
Your question shows improperly formatted XML but I am assuming that is a typo and the real Xml can be loaded into the XDocument class.
Try this...
string xml = #"<MyXml>
<MandatoryElement1>value</MandatoryElement1>
<MandatoryElement2>value</MandatoryElement2>
<MandatoryElement3>value</MandatoryElement3>
<CustomElement1>value</CustomElement1>
<CustomElement2>value</CustomElement2>
</MyXml> ";
System.Xml.Linq.XDocument xDoc = XDocument.Parse(xml);
var result = xDoc.Root.Descendants()
.Where(x => !x.Name.LocalName.StartsWith("MandatoryElement"));
lets say TestXMLFile.xml will contain your xml,
XElement doc2 = XElement.Load(Server.MapPath("TestXMLFile.xml"));
List<XElement> _list = doc2.Elements().ToList();
List<XElement> _list2 = new List<XElement>();
foreach (XElement x in _list)
{
if (!x.Name.LocalName.StartsWith("Mandatory"))
{
_list2.Add(x);
}
}
foreach (XElement y in _list2)
{
_list.Remove(y);
}
I have an XML file with the following structure:
<Definitions>
<Definition Execution="Firstkey" Name="Somevalue"></Definition>
<Definition Execution="Secondkey" Name="Someothervalue"></Definition>
</Definitions>
How can I get the values of the keys (Firstkey, Secondkey) and write them down using C# in my .NET application?
Thanks.
Using Linq to XML this is straightforward.
To just get the keys:
var keys = doc.Descendants("Definition")
.Select(x => x.Attribute("Execution").Value);
foreach (string key in keys)
{
Console.WriteLine("Key = {0}", key);
}
To get all values:
XDocument doc = XDocument.Load("test.xml");
var definitions = doc.Descendants("Definition")
.Select(x => new { Execution = x.Attribute("Execution").Value,
Name = x.Attribute("Name").Value });
foreach (var def in definitions)
{
Console.WriteLine("Execution = {0}, Value = {1}", def.Execution, def.Name);
}
Edit in response to comment:
I think what you really want is a dictionary, that maps from a key ("Execution") to a value ("Name"):
XDocument doc = XDocument.Load("test.xml");
Dictionary<string, string> dict = doc.Descendants("Definition")
.ToDictionary(x => x.Attribute("Execution").Value,
x => x.Attribute("Name").Value);
string firstKeyValue = dict["Firstkey"]; //Somevalue
using System.Xml.Linq;
var keys = XDocument.Load("path to the XML file")
.Root
.Elements()
.Select(x => x.Attribute("Execution"));
Using XMLDocumnet/XMLNode(s):
//Load the XML document into memory
XmlDocument doc = new XmlDocument();
doc.Load("myXML.xml");
//get a list of the Definition nodes in the document
XmlNodeList nodes = doc.GetElementsByTagName("Definition");
//loop through each node in the XML
foreach (XmlNode node in nodes)
{
//access the key attribute, since it is named Execution,
//that is what I pass as the index of the attribute to get
string key = node.Attributes["Execution"].Value;
//To select a single node, check if we have the right key
if(key == "SecondKey") //then this is the second node
{
//do stuff with it
}
}
basically you load the xml into a document variable, select the nodes you wish to view. Then iterate through them and store pertinent information.
XPath would be a great choice, I'd say.
Below is a sample XPath expression.
//Definition[#Execution='Firstkey']/#Name
As a XPath expression is a string, you can easily replace 'Firstkey' with whatever you need.
Use this with a XmlDocument.SelectSingleNode or XmlDocument.SelectNodes method
Both the above mentioned methods return an XmlNode. You can easily access the XmlNode.Value
Here are some XPath expressions
Don't forget XPath Visualizer which makes working with XPath so much easier!