I have a xml document that has a record set like this.
<document>
<row>
<Pub_Code>OHB-A0011</Pub_Code>
<Sec>16</Sec>
<Pags>20</Pags>
<Copies>1,000</Copies>
<Binding>Saddle Stitch</Binding>
<Tab>No tabs</Tab>
<Qty>0</Qty>
<Cover>Self Cover</Cover>
<Tpgs>0</Tpgs>
</row>
</document>
I have a linq query wrtten this way:
string xml_path = #"D:\Server-Apps\BooksData.xml";
XElement root = XElement.Load(xml_path);
var selected = from myBooks in root.Elements("row") where myBooks.Element("Pub_Code").Value == "OHB-A0011" select myBooks;
foreach (var d in selected)
{
Console.WriteLine("Pub_Code: {0}", d.Element("Pub_Code").Value);
Console.WriteLine("Cover: {0}", d.Element("Cover").Value);
d.SetElementValue("Tpgs", "test");
}
I can read the value find but when I uses d.SetElementValue("Tpgs", "test"); nothing gets update.
the tag is already in the xml file .
If you want to save the updated XML back to the file, you need to do:
root.Save(xml_path);
I added the line t the end of your program and it seems to work correctly.
Related
I am trying to restrict duplicate entry to an XML file and below is the XML file.
<?xml version="1.0" standalone="yes"?>
<Info>
<Details>
<ID>Ryan</ID>
</Details>
<Details>
<ID>Danny</ID>
</Details>
</Info>
Now if I try to add Ryan or Danny again to the ID I should alert like user name already exists.
I'm using the below code and it doesn't work. strName is a string and has username value to be added. Can anyone provide suggestions?
XDocument xDoc = XDocument.Load(Server.MapPath("~/Info.xml"));
bool userExistsAlready = xDoc.Descendants("Details").Any(x => (string)x.Attribute("ID") == strName);
if (userExistsAlready)
{
//alert
}
Try this way:
bool userExistsAlready = xDoc.Descendants("Details")
.Elements("ID")
.Any(x => x.Value == "Ryan");
The problem with your code is that it tries to access attribute ID. But ID is in fact another XML element contained inside element <Details>.
You could set ID as an attribute of Details and then check if that entry exist using the XmlDocument method GetElementByID, or implement a for cycle that checks out the property InnerText of every element in the array resulting from the call to GetElementsByName method.
I have below a xml file with the below format:
<?xml version="1.0" encoding="utf-8" ?>
<Root>
<Countries>
<country>India</country>
<country>USA</country>
<country>UK</country>
</Countries>
</Root>
string newCountry="UAE"
I want to insert this "UAE" country to the above xml file, before that I want to check whether "UAE" is already exists in the xml. If not exists then only want to insert otherwise no operation. How can I do this?
Like this:
XDocument xml = XDocument.Load("path_to_file");
string newCountry = "UAE";
XElement countries = xml.Descendants("Countries").First();
XElement el = countries.Elements().FirstOrDefault(x => x.Value == newCountry);
if (el == null)
{
el = new XElement("country");
el.Value = newCountry;
countries.Add(el);
}
//Console.WriteLine(countries.ToString());
The easiest way would probably be to read the xml into C# objects, check for the existance of UAE, potentially add it, and write the objects back to XML.
in a project I am reading XML file to get string data. It works very well but with one exception. Here is the structure of XML File:
<Table>
<row>
<queryId>customAnalyticsParam</queryId>
<queryStatement>
1|-1|-1|-1
</queryStatement>
</row>
</Table>
The table tag contains multiple row tags. Each row tag is distinguished by queryId tag. Above structure works very well with following code:
string query = "";
XmlDocument xd = new XmlDocument();
xd.Load(xml_query_filePath);
XmlNode xnode = xd.SelectNodes("/Table/row/queryId[.='" + id.Trim() + "']")[0];
query = xnode.NextSibling.NextSibling.InnerText;
In the above code in thrid line id is the queryId provided in the parameter of the function. The exception arises when XML is in following format:
<Table>
<row>
<queryId>
customAnalyticsParam
</queryId>
<queryStatement>
1|-1|-1|-1
</queryStatement>
</row>
</Table>
That is line spaces before and after in queryId node. Due to these line breaking spaces the code could not find the node and returns null.Please help me to find a way.Thanks
Try with normalize-space:
XmlNode xnode = xd.SelectNodes("/Table/row/queryId[normalize-space(.)='" + id.Trim() + "']").[0];
You can also use LINQ to XML with simple Trim() method to remove white-spaces
var xdoc = XDocument.Load(xml_query_filePath);
var statements = from r in xdoc.Descendants("row")
let queryId = (string)r.Element("queryId")
let statement = (string)r.Element("queryStatement")
where queryId.Trim() == id.Trim()
select statement.Trim();
Or if you need just first statement (note that as in your code, elements should exist in xml)
var statement = xdoc.Descendants("row")
.First(r => r.Element("queryId").Value.Trim() == id.Trim())
.Element("queryStatement").Value.Trim();
Friends,
My school project is having an xml data file:
<patients>
<patient>
<regNo>2012/Mar/003</regNo>
<name>Jhon</name>
<add>Somewhere</add>
<mobile>0000</mobile>
.
.
.
<stay>2</stay>
<costofroom>100</costofroom>
<total>200</total>
</patient>
</patients>
My Windowsform "EditPatients_Load" is able to fetch all info of patient Jhon, and now let's assume that the Admin needs to change some information in the form & resubmit.
Then how to write back all values to Jhon's account in the same xml
file????
I'm not able to makeup the logical code, even if I check the node if (patients.paptient.name = "nameComboBox.text").... how to make sure that I'm writing other values on proper place?
Rgrdz,
Try this:
//string xml =
//#"<patients><patient><regNo>2012/Mar/003</regNo><name>Jhon</name><add>Somewhere
//</add><mobile>0000</mobile><stay>2</stay><costofroom>100</costofroom><total>200</total>
//</patient></patients>";
XDocument xmlDoc = XDocument.Load(#"c:\abc.xml");
var items = (from item in xmlDoc.Descendants("patient")
where item.Element("name").Value == "Jhon"
select item);
if (items.Count() > 0)
{
var item = items.First();
item.SetElementValue("add", "New New Address");
xmlDoc.Save(#"c:\abc.xml", SaveOptions.None);
}
You can get single element using
var item = (from item in xmlDoc.Descendants("patient")
where item.Element("name").Value == "Jhon"
select item).FirstOrDefault();
then update it using SetElementValue() method.
//Updated Xml
<?xml version="1.0" encoding="utf-8"?>
<patients>
<patient>
<regNo>2012/Mar/003</regNo>
<name>Jhon</name>
<add>New Address</add>
<mobile>0000</mobile>
<stay>2</stay>
<costofroom>100</costofroom>
<total>200</total>
</patient>
</patients>
Reference:
Update XML with C# using Linq
I would take the xml serialization/deserialization route to solve this:
http://support.microsoft.com/kb/815813
How to Deserialize XML document
That way you can work with objects and not have to parse xml files manually.
If you're using .NET 3.5 onward you can use the XDocument class like the following. I'm assuming your content is in a .xml file.
XDocument xdoc = XDocument.Load(#"C:\Tmp\test.xml");
//this would ensure you get the right node and set its text content/value
xdoc.Element("patients")
.Element("patient").Element("add").Value = "some new address?";
xdoc.Save(#"C:\Tmp\test.xml");
The file test.xml would change to:
<patients>
<patient>
<regNo>2012/Mar/003</regNo>
<name>Jhon</name>
<add>some new address?</add>
<mobile>0000</mobile>
<stay>2</stay>
<costofroom>100</costofroom>
<total>200</total>
</patient>
</patients>
El Padrino showed a solution:
How to change XML Attribute
where an xml element can be loaded directly (no for each..), edited and saved!
My xml is:
<?xml version="1.0" encoding="ISO-8859-8"?>
<g>
<page no="1" href="page1.xml" title="נושא 1">
<row>
<pic pos="1" src="D:\RuthSiteFiles\webSiteGalleryClone\ruthCompPics\C_WebBigPictures\100CANON\IMG_0418.jpg" width="150" height="120">1</pic>
</row>
</page>
</g>
and I need to select a node by two attributes(1. "no" in the page tag and "pos" in the pic tag)
I've found :
How to access a xml node with attributes and namespace using selectsinglenode()
where direct access is possible but beside the fact that I dont understand the solution, I think it uses the xpath object which can't be modified and save changes.
What's the best way to
access directly an xml node (I'm responsible that the node will be unique)
edit that node
save changes to the xml
Thanks
Asaf
You can use the same pattern as the first answer you linked to, but you will need to include the conditions on the attributes in the XPath. Your basic XPath would be g/page/row/pic. Since you want the no attribute of page to be 1, you add [#no='1'] as a predicate on page. So, the full XPath query is something like g/page[#no='1']/row/pic[#pos='1']. SelectSingleNode will return a mutable XmlNode object, so you can modify that object and save the original document to save changes.
Putting the XPath together with El Padrino's answer:
//Here is the variable with which you assign a new value to the attribute
string newValue = string.Empty;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(xmlFile);
XmlNode node = xmlDoc.SelectSingleNode("g/page[#no='1']/row/pic[#pos='1']");
node.Attributes["src"].Value = newValue;
xmlDoc.Save(xmlFile);
//xmlFile is the path of your file to be modified
Use the new, well-designed XDocument/XElement instead of the old XmlDocument API.
In your example,
XDocument doc = XDocument.Load(filename);
var pages = doc.Root.Elements("page").Where(page => (int?) page.Attribute("no") == 1);
var rows = pages.SelectMany(page => page.Elements("row"));
var pics = rows.SelectMany(row => row.Elements("pic").Where(pic => (int?) pic.Attribute("pos") == 1));
foreach (var pic in pics)
{
// outputs <pic pos="1" src="D:\RuthSiteFiles\webSiteGalleryClone\ruthCompPics\C_WebBigPictures\100CANON\IMG_0418.jpg" width="150" height="120">1</pic>
Console.WriteLine(pic);
// outputs 1
Console.WriteLine(pic.Value);
// Changes the value
pic.Value = 2;
}
doc.Save(filename);