I am new to XML files and how to manage them. This is for a web app I am writing (aspx).
At the present time I am able to find the first instance of a node and add an item to it with the following code:
xmlClone.Element("PCs").Element("PC").Element("pc_hwStatus").AddAfterSelf(new XElement("user_name", txt_v0_nombre.Text));
What I really want is to add ("user_name", txt_v0_nombre.Text) to a node in particular, not the first one. The content of my XML file is:
<PCs>
<PC>
<pc_name>esc01</pc_name>
<pc_ip>10.10.10.10</pc_ip>
<pc_hwStatus>Working</pc_hwStatus>
</PC>
<PC>
<pc_name>esc02</pc_name>
<pc_ip>10.10.10.11</pc_ip>
<pc_hwStatus>Under Maintenance</pc_hwStatus>
</PC>
</PCs>
The decision of what node to update is made selecting an item from a dropdown list (the PC name).
With my current code, the new item is always added as last line of node with "pc_
name = esc01". I want to be able to added it to esc02 or esc03 and so on... How can this be accomplished? (Using xdocument)
If I understand you correctly, what you are looking for is the FirstOrDefault extension method. In there specify which node you are wanting, in this case a string from your dropdown box, which can be passed in. So to get the first node:
var pc = xmlClone.Element("PCs").Elements("PC").FirstOrDefault(e => e.Element("pc_name").Value == "esc01");
Now you have this in your XElement:
<PC>
<pc_name>esc01</pc_name>
<pc_ip>10.10.10.10</pc_ip>
<pc_hwStatus>Working</pc_hwStatus>
</PC>
To get any element like that, just replace this clause:
.FirstOrDefault(e => e.Element("pc_name").Value == "esc01");
with this one
.FirstOrDefault(e => e.Element("pc_name").Value == desiredPC);
where desiredPC is the value of the xml node: pc_name.
Now to add your data just call the plain old Add method:
pc.Add(new XElement("user_name", txt_v0_nombre.Text);
That should do the trick for you.
Here's a solution that uses LINQ query syntax with LINQ to XML:
XDocument document = XDocument.Parse(xmlContent);
string pcName = "esc02";
IEnumerable<XElement> query =
from pc in document.Element("PCs").Elements("PC")
where pc.Element("pc_name").Value.Equals(pcName)
select pc;
XElement xe = query.FirstOrDefault();
if (xe != null)
{
xe.Add(new XElement("user_name", "DMS"));
}
I have incorporated your sample data and this query into a demonstration program. Please see below for the output from the demonstration program followed by the program itself.
Expected Output
<PC>
<pc_name>esc02</pc_name>
<pc_ip>10.10.10.11</pc_ip>
<pc_hwStatus>Under Maintenance</pc_hwStatus>
<user_name>DMS</user_name>
</PC>
Demonstration Program
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace LinqToXmlDemo
{
public class Program
{
public static void Main(string[] args)
{
string xmlContent = GetXml();
XDocument document = XDocument.Parse(xmlContent);
XElement xe = FindPCName(document, "esc02");
if (xe != null)
{
xe.Add(new XElement("user_name", "DMS"));
Console.WriteLine(xe);
}
else
{
Console.WriteLine("Query returned no results.");
}
}
private static XElement FindPCName(XDocument document, String pcName)
{
IEnumerable<XElement> query =
from pc in document.Element("PCs").Elements("PC")
where pc.Element("pc_name").Value.Equals(pcName)
select pc;
return query.FirstOrDefault();
}
private static String GetXml()
{
return
#"<?xml version='1.0' encoding='utf-8'?>
<PCs>
<PC>
<pc_name>esc01</pc_name>
<pc_ip>10.10.10.10</pc_ip>
<pc_hwStatus>Working</pc_hwStatus>
</PC>
<PC>
<pc_name>esc02</pc_name>
<pc_ip>10.10.10.11</pc_ip>
<pc_hwStatus>Under Maintenance</pc_hwStatus>
</PC>
</PCs>";
}
}
}
Method .Element returns the first element with the specified name.
You can get the whole list with method .Elements, and iterate this list to find the one you are looking for.
Related
How do I read and parse an XML file in C#?
XmlDocument to read an XML from string or from file.
using System.Xml;
XmlDocument doc = new XmlDocument();
doc.Load("c:\\temp.xml");
or
doc.LoadXml("<xml>something</xml>");
then find a node below it ie like this
XmlNode node = doc.DocumentElement.SelectSingleNode("/book/title");
or
foreach(XmlNode node in doc.DocumentElement.ChildNodes){
string text = node.InnerText; //or loop through its children as well
}
then read the text inside that node like this
string text = node.InnerText;
or read an attribute
string attr = node.Attributes["theattributename"]?.InnerText
Always check for null on Attributes["something"] since it will be null if the attribute does not exist.
LINQ to XML Example:
// Loading from a file, you can also load from a stream
var xml = XDocument.Load(#"C:\contacts.xml");
// Query the data and write out a subset of contacts
var query = from c in xml.Root.Descendants("contact")
where (int)c.Attribute("id") < 4
select c.Element("firstName").Value + " " +
c.Element("lastName").Value;
foreach (string name in query)
{
Console.WriteLine("Contact's Full Name: {0}", name);
}
Reference: LINQ to XML at MSDN
Here's an application I wrote for reading xml sitemaps:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Data;
using System.Xml;
namespace SiteMapReader
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Please Enter the Location of the file");
// get the location we want to get the sitemaps from
string dirLoc = Console.ReadLine();
// get all the sitemaps
string[] sitemaps = Directory.GetFiles(dirLoc);
StreamWriter sw = new StreamWriter(Application.StartupPath + #"\locs.txt", true);
// loop through each file
foreach (string sitemap in sitemaps)
{
try
{
// new xdoc instance
XmlDocument xDoc = new XmlDocument();
//load up the xml from the location
xDoc.Load(sitemap);
// cycle through each child noed
foreach (XmlNode node in xDoc.DocumentElement.ChildNodes)
{
// first node is the url ... have to go to nexted loc node
foreach (XmlNode locNode in node)
{
// thereare a couple child nodes here so only take data from node named loc
if (locNode.Name == "loc")
{
// get the content of the loc node
string loc = locNode.InnerText;
// write it to the console so you can see its working
Console.WriteLine(loc + Environment.NewLine);
// write it to the file
sw.Write(loc + Environment.NewLine);
}
}
}
}
catch { }
}
Console.WriteLine("All Done :-)");
Console.ReadLine();
}
static void readSitemap()
{
}
}
}
Code on Paste Bin
http://pastebin.com/yK7cSNeY
There are lots of way, some:
XmlSerializer. use a class with the target schema
you want to read - use XmlSerializer
to get the data in an Xml loaded into
an instance of the class.
Linq 2 xml
XmlTextReader.
XmlDocument
XPathDocument (read-only access)
You could use a DataSet to read XML strings.
var xmlString = File.ReadAllText(FILE_PATH);
var stringReader = new StringReader(xmlString);
var dsSet = new DataSet();
dsSet.ReadXml(stringReader);
Posting this for the sake of information.
You can either:
Use XmlSerializer class
Use XmlDocument class
Examples are on the msdn pages provided
Linq to XML.
Also, VB.NET has much better xml parsing support via the compiler than C#. If you have the option and the desire, check it out.
Check out XmlTextReader class for instance.
There are different ways, depending on where you want to get.
XmlDocument is lighter than XDocument, but if you wish to verify minimalistically that a string contains XML, then regular expression is possibly the fastest and lightest choice you can make. For example, I have implemented Smoke Tests with SpecFlow for my API and I wish to test if one of the results in any valid XML - then I would use a regular expression. But if I need to extract values from this XML, then I would parse it with XDocument to do it faster and with less code. Or I would use XmlDocument if I have to work with a big XML (and sometimes I work with XML's that are around 1M lines, even more); then I could even read it line by line. Why? Try opening more than 800MB in private bytes in Visual Studio; even on production you should not have objects bigger than 2GB. You can with a twerk, but you should not. If you would have to parse a document, which contains A LOT of lines, then this documents would probably be CSV.
I have written this comment, because I see a lof of examples with XDocument. XDocument is not good for big documents, or when you only want to verify if there the content is XML valid. If you wish to check if the XML itself makes sense, then you need Schema.
I also downvoted the suggested answer, because I believe it needs the above information inside itself. Imagine I need to verify if 200M of XML, 10 times an hour, is valid XML. XDocument will waste a lof of resources.
prasanna venkatesh also states you could try filling the string to a dataset, it will indicate valid XML as well.
public void ReadXmlFile()
{
string path = HttpContext.Current.Server.MapPath("~/App_Data"); // Finds the location of App_Data on server.
XmlTextReader reader = new XmlTextReader(System.IO.Path.Combine(path, "XMLFile7.xml")); //Combines the location of App_Data and the file name
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
break;
case XmlNodeType.Text:
columnNames.Add(reader.Value);
break;
case XmlNodeType.EndElement:
break;
}
}
}
You can avoid the first statement and just specify the path name in constructor of XmlTextReader.
If you want to retrive a particular value from an XML file
XmlDocument _LocalInfo_Xml = new XmlDocument();
_LocalInfo_Xml.Load(fileName);
XmlElement _XmlElement;
_XmlElement = _LocalInfo_Xml.GetElementsByTagName("UserId")[0] as XmlElement;
string Value = _XmlElement.InnerText;
Here is another approach using Cinchoo ETL - an open source library to parse xml file with few lines of code.
using (var r = ChoXmlReader<Item>.LoadText(xml)
.WithXPath("//item")
)
{
foreach (var rec in r)
rec.Print();
}
public class Item
{
public string Name { get; set; }
public string ProtectionLevel { get; set; }
public string Description { get; set; }
}
Sample fiddle: https://dotnetfiddle.net/otYq5j
Disclaimer: I'm author of this library.
I don't have much experience with XML files but I'm trying to append a tutorial I found online to suite my needs and I'm not getting the results I would expect.
https://support.microsoft.com/en-us/help/307548/how-to-read-xml-from-a-file-by-using-visual-c
I've searched around but everything I've found doesn't make sense to me.
My XML looks like this for the most part:
<US>
<!-- Kentucky Start -->
<State>Kentucky KY
<City>Newport
<Street>Pavilion Parkway<Number>130<PostalCode>41071</PostalCode></Number></Street>
</City>
<City>Corbin
<Street>Highway 90<Number>7351<PostalCode>40701</PostalCode></Number></Street>
</City>
</State>
</US>
I'm trying to populate a listbox with the value of each state but my code either returns white space or just the text within the XML tag
e.g.
State..
State..
repeated for each element.
while (reader.Read()) {
switch (reader.NodeType) {
case XmlNodeType.Element: // The node is an element.
// Skip over root element
if (reader.Name.Equals("US")) {
reader.MoveToNextAttribute();
}
else {
if(reader.Name.Equals("State")) {
lbState.Items.Add(reader.Name);
lbState.Items.Add(reader.Value);
}
}
break;
reader.Name returns "State"
reader.Value returns "Whitespace"
I don't understand why reader.Value does not return Kentucky KY...
I've seen other examples that use string builder, is this a bad approach?
Use reader.ReadString() instead of reader.Value
Try xml linq :
sing System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication2
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var results = doc.Descendants("State").Select(x => new {
cities = x.Elements("City").Select(y => new {
state = (string)x,
city = (string)y,
streets = y.Elements("Street").Select(z => (string)z).ToList()
}).ToList()
}).SelectMany(x => x.cities).ToList();
}
}
}
You can use XmDocument (see: https://msdn.microsoft.com/en-us/library/system.xml.xmldocument(v=vs.110).aspx) and then use an xpath expression to get the right elements from your document:
Also it is better to encapsulate the name of the state (that is, if you own the xml document) like this:
<name>Kentucy KY</name>
So you can do the following:
var items = new List<string>();
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.LoadXml("yourxml");
var xmlNodes = xmlDocument.SelectNodes("//State/Name");
foreach (XmlNode node in xmlNodes)
{
items.Add(xmlNode.Value);
}
I'm trying to modify an XML document that we didn't create initially. Snippit from XML is below:
<DEALS>
<DEAL>
<LOANS>
<LOAN LoanRoleType="SubjectLoan">
<BUYDOWN>
<BUYDOWN_RULE>
<BuydownInformation>0</BuydownInformation>
</BUYDOWN_RULE>
</BUYDOWN>
</LOAN>
<LOAN LoanRoleType="SubjectLoan">
<LOAN_IDENTIFIERS>
<LOAN_IDENTIFIER>
...
</LOAN_IDENTIFIER>
<LOAN_IDENTIFIER>
<SellerLoanIdentifier>1234567890</SellerLoanIdentifier>
</LOAN_IDENTIFIER>
</LOAN_IDENTIFIERS>
</LOAN>
</LOANS>
</DEAL>
<DEAL>
...Same format as above...
</DEAL>
</DEALS>
The first LOAN element of each DEAL will never contain LOAN_IDENTIFIERS. I need to get the SellerLoanIdentifier's InnerText, and then put it into <BuydownInformation> of the first LOAN element. I've tried nested loops and can't seem to get it to differentiate between the two LOAN elements (the second loop isn't even seeing the LOAN elements). I'm thinking it also might have to do with the fact that they both carry the exact same attribute but can't find anything online up to this point to help.
XmlDocument xmlExport = new XmlDocument();
xmlExport.Load(fileDestination);
string loanNumber = "";
XmlNodeList loan_XMLDeals = xmlExport.GetElementsByTagName("DEAL");
Logger.WriteDebug("Found " + loan_XMLDeals.Count + " Deals");
foreach (XmlNode loan_XMLDeal in loan_XMLDeals)
{
XmlNodeList loan_XMLLoans = loan_XMLDeal.SelectNodes("LOAN");
Logger.WriteDebug("Found " + loan_XMLLoans.Count + " Loan categories");
foreach (XmlNode loan_XMLCategory in loan_XMLLoans)
{
if(loan_XMLCategory.SelectSingleNode("SellerLoanIdentifier") != null)
{
loanNumber = loan_XMLCategory.SelectSingleNode("SellerLoanIdentifier").ToString();
Logger.WriteDebug("Got loan number " + loanNumber);
}
}
}
This becomes a lot easier with linq to xml. This means ditching the (old) XmlDocument and replacing it with the friendlier XDocument.
Instead of searching the whole document for the target, you need to start from the context of where you found the SellerLoanIdentifier. You can walk back up to the LOAN element, find its previous sibling, then search that for the BuydownInformation. Because this was all scoped within a single LOANS entry, you can be sure you're targeting the right element.
So...
var doc = XDocument.Load(fileDestination);
//we're going to select a sequence of items that contain 2 values...
//the element we want to change and the value we want to store in it
var changes= doc.Root
.Elements("DEAL")
.Descendants("SellerLoanIdentifier")
//from each SellerLoanIdentifier in DEAL elements
.Select(e => new{
//the node we want to change
//in this case we get the parent LOAN
//element, take the last of the elements
//that precede it in the document
//(e.g. the previous sibling which
//contains the target node)
//and find in it a descendant of type
//BuydownInformation
nodeToChange = e.Ancestors("LOAN")
.Single()
.ElementsBeforeSelf()
.Last()
.Descendants("BuydownInformation")
.Single(),
//the string value of the current element
val = (string)e
});
//then apply the changes back to the document
foreach(var change in changes)
{
change.nodeToChange.Value = change.val;
}
var newXmlString = doc.ToString();
There are assumptions here about the shape of your data that may not hold true, but it should be relatively easy to modify.
Try this simple XML Linq code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
List<XElement> loans = doc.Descendants("LOANS").ToList();
foreach (XElement loan in loans)
{
string sellerLoanIdentifier = (string)loan.Descendants("SellerLoanIdentifier").FirstOrDefault();
XElement buydownInformation = loan.Descendants("BuydownInformation").FirstOrDefault();
buydownInformation.Value = sellerLoanIdentifier;
}
}
}
}
I have string XML. I loaded to XmlDocument. How can I add, edit and delete by simplest method by line, because I know only line which I should edit. It's better work wih XML like with string, or better work with XmlDocuments?
using System;
using System.Xml;
namespace testXMl
{
class Program
{
static void Main(string[] args)
{
string xml="<?xml version=\"1.0\"?>\r\n<application>\r\n<features>\r\n<test key=\"some_key\">\r\n</features>\r\n</application>";
XmlDocument xm = new XmlDocument();
xm.LoadXml(xml);
//Edit third line
//xm[3].EditName(featuresNew);
//xml->"<?xml version=\"1.0\"?>\r\n<application>\r\n<featuresNew>\r\n<test key=\"some_key\">\r\n</featuresNew>\r\n</application>"
//Add fourth line the Node
//xm[4].AddNode("FeatureNext");
//xml->"<?xml version=\"1.0\"?>\r\n<application>\r\n<FeatureNext>\r\n<FeatureNext>\r\n</features2>\r\n<test key=\"some_key\">\r\n</features>\r\n</application>"
//Delete sixth line
//xm[6].DeleteNode;
//xml->"<?xml version=\"1.0\"?>\r\n<application>\r\n<FeatureNext>\r\n<FeatureNext>\r\n</features2>\r\n</features>\r\n</application>"
}
}
}
Thanks, in advance.
You should always work with XDocument/XmlDocument objects. A key knowledge is the XPath query language.
This a quick XML crash course. Run with debugger and inspect the XML variable as you move on.
var xml = new XmlDocument();
xml.LoadXml(#"<?xml version='1.0'?>
<application>
<features>
<test key='some_key' />
</features>
</application>");
// Select an element to work with; I prefer to work with XmlElement instead of XmlNode
var test = (XmlElement) xml.SelectSingleNode("//test");
test.InnerText = "another";
test.SetAttribute("sample", "value");
var attr = test.GetAttribute("xyz"); // Works, even if that attribute doesn't exists
// Create a new element: you'll need to point where you should add a child element
var newElement = xml.CreateElement("newElement");
xml.SelectSingleNode("/application/features").AppendChild(newElement);
// You can also select elements by its position;
// in this example, take the second element inside "features" regardless its name
var delete = xml.SelectSingleNode("/application/features/*[2]");
// Trick part: if you found the element, navigate to its parent and remove the child
if (delete != null)
delete.ParentNode.RemoveChild(delete);
I have a xml structure similar to below one:
<test>
<test1>test1 value</test1>
</test>
Now I am reading the value of node using below LINQ to xml code.
var test = from t in doc.Descendants("test") select t.Element("test1").Value;
Console.WriteLine("print single node value");
foreach (var item in test)
{
Console.WriteLine(item);
}
above code works fine, but here I have one single node, but to retrive value I am using foreach loop, which I dont think is good..any better way of doing the same thing without a foreach loop
Thanks.
Try something like this:
using System;
using System.Linq;
using System.Xml.Linq;
public class Example
{
static void Main()
{
String xml = #"<test>
<test1>test1 value</test1>
</test>";
var test = XElement.Parse(xml)
.Descendants("test1")
.First()
.Value;
Console.WriteLine(test);
}
}
you can also try providing XML file path like following:
XElement xmldoc = XElement.Load("filePath");
var nodeValueFromXMlFile = xmldoc
.Descendants("node name")
.First()
.Value;
System.Console.WriteLine(nodeValueFromXMlFile);