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);
}
Related
I have an xml file as below, and I need to extract values and put them inside a multidimensional array. The idea is, when I have more than one tag <string> per root element <Etiquette>, I need to repeat the same other values with each different value of the tag <string>
<?xml version="1.0" encoding="utf-8"?>
<ArrayOfEtiquette xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Etiquette>
<BgColor>#8075D1C5</BgColor>
<BorderColor>#FF4E5B6F</BorderColor>
<AssociatedAffaireId>
<string>d4689f33-5600-47fe-883d-efcbf5e469c2</string>
<string>1bae35dd-d501-4d87-bdd4-147fc0ba29d2</string>
</AssociatedAffaireId>
<Label>Ouverte</Label>
</Etiquette>
<Etiquette>
<BgColor>#80949CA8</BgColor>
<BorderColor>#FF155E70</BorderColor>
<AssociatedAffaireId>
<string>203cc4a8-8c24-4a2d-837c-29c7c1f73007</string>
</AssociatedAffaireId>
<Label>Fermée</Label>
</Etiquette>
</ArrayOfEtiquette>
Desired result:
{"#8075D1C5","#FF4E5B6F","d4689f33-5600-47fe-883d-efcbf5e469c2","Ouverte"}
{"#8075D1C5","#FF4E5B6F","1bae35dd-d501-4d87-bdd4-147fc0ba29d2","Ouverte"}
{"#80949CA8","#FF155E70","203cc4a8-8c24-4a2d-837c-29c7c1f73007","Fermée"}
Regards,
Using Xml Linq :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication157
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
var results = doc.Descendants("Etiquette")
.SelectMany(x => x.Descendants("string")
.Select(y => new { BgColor = (string)x.Element("BgColor"), BorderColor = (string)x.Element("BorderColor"), UID = (string)y }))
.ToList();
}
}
}
I you want just an array instead of anonymous type use :
new string[] { (string)x.Element("BgColor"), (string)x.Element("BorderColor"), (string)y }
You can try with XDocument
XDocument xdoc = XDocument.Load("XMLFile7.xml");
var mdAarray = xdoc.Descendants("Etiquette")
.SelectMany(etiquette =>
etiquette.Descendants("string")
.Select(associatedaffaire => new string[] {
etiquette.Element("BgColor").Value.ToString(),
etiquette.Element("BorderColor").Value.ToString(),
associatedaffaire.Value.ToString(),
etiquette.Element("Label").Value.ToString() }))
.ToArray();
Console.WriteLine(JsonConvert.SerializeObject(mdAarray));
OUTPUT
[
["#8075D1C5","#FF4E5B6F","d4689f33-5600-47fe-883d-efcbf5e469c2","Ouverte"],
["#8075D1C5","#FF4E5B6F","1bae35dd-d501-4d87-bdd4-147fc0ba29d2","Ouverte"],
["#80949CA8","#FF155E70","203cc4a8-8c24-4a2d-837c-29c7c1f73007","Fermée"]
]
you only need to iterate first on Etiquette then iterate again on AssociatedAffaireId
Each time you can insert inside the array or list (I will use list for simplicity)
XDocument xdoc = XDocument.Load("pathToXml.xml");
// iterate all Etiquette elements
foreach (var etiquette in xdoc.Root.Elements("Etiquette"))
{
// store common values
string bgColor = etiquette.Element("BgColor").Value;
string borderColor = etiquette.Element("BorderColor").Value;
string label = etiquette.Element("Label").Value;
// iterate all AssociatedAffaireId.string elements and add to list
var associatedAffaireIdEl = etiquette.Element("AssociatedAffaireId");
foreach (var associatedAffaireId in associatedAffaireIdEl.Elements("string"))
{
string aaid = associatedAffaireId.Value;
listOfArray.Add(new string[]{bgColor, borderColor, aaid, label});
}
}
I hope this could help.
Sorry I found some errors. Check out my fiddle here.
List<string> list = new List<string>();
foreach (XPathNavigator node in nav.Select("configuration/company/work/worktime"))
{
string day = getAttribute(node, "day");
string time = getAttribute(node, "time");
string worktype = ?? // how to get worktype attribute valuefrom parent node
list.Add(day,time,worktype); // add to list
}
</configuration>
<company>
<work worktype="homeWork">
<worktime day="30" time="10:28"></worktime>
<worktime day="25" time="10:50"></worktime>
</work>
<work worktype="officeWork">
<worktime day="12" time="09:28"></worktime>
<worktime day="15" time="12:28"></worktime>
</work>
</company>
</configuration>
need output as :
list[0] = homeWork,30,10:28
list[1] = homeWork,25,10:50
list[2] = officeWork,12,09:28
list[3] = officeWork,15,12:28
I am trying to get the list from XML but failed to get output like given above (using xpath navigator, how can I access parent node to get worktype attribute, and other remaining inner node attribute?
I'd suggest using LINQ to XML over XPath, but if you must use XPathNavigator then you need to iterate each work element followed by each of its worktime child elements. This way you can use the worktype from the parent context:
foreach (XPathNavigator work in nav.Select("configuration/company/work"))
{
var workType = work.GetAttribute("worktype", string.Empty);
foreach (XPathNavigator worktime in work.Select("worktime"))
{
var day = worktime.GetAttribute("day", string.Empty);
var time = worktime.GetAttribute("time", string.Empty);
list.Add($"{workType}, {day}, {time}");
}
}
See this fiddle for a working demo.
Use a nested loop. Initially retrieve the work nodes with configuration/company/work. Retrieve the worktype attribute and store in a variable. Then loop through the child worktype nodes and add a string to the list for each one
Use Net Library enhanced xml (linq xml)
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);
var results = doc.Descendants("work").Select(x => new {
worktype = (string)x.Attribute("worktype"),
worktime = x.Elements("worktime").Select(y => new {
day = (int)y.Attribute("day"),
time = (DateTime)y.Attribute("time")
}).ToList()
}).ToList();
}
}
}
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 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.
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);