so here is the code i am using to get the other elements from my xml.
I have a data grid view that displays a lot of other elements but whenever I try to get 'Address1' etc it throws up an error. But I can call 'Address' and it puts all the elements of address into the one cell in my datagridview.
Can someone help me get 'Address1' and 'Address2' etc by themselves? I am stuck on this for a while now.
So am trying to get the details from 'Address1 & Address2'
Thanks in advance.
You can use XElement to parse your XML. ie:
void Main()
{
string s = #"<address>
<address1>Address 1</address1>
<address2>Address 2</address2>
<address3>Address 3</address3>
<address4>Address 4</address4>
<postcode>1234</postcode>
<country>MyCountry</country>
</address>";
var data = from r in XElement.Parse(s).DescendantsAndSelf("address")
select new {
Address1 = (string)r.Element("address1"),
Address2 = (string)r.Element("address2"),
Address3 = (string)r.Element("address3"),
Address4 = (string)r.Element("address4"),
Postcode = (string)r.Element("postcode"),
Country = (string)r.Element("country")
};
foreach(var d in data)
{
Console.WriteLine($"Address line1:{d.Address1}, Postcode:{d.Postcode}");
}
}
You can work with XDocument and then easily git the values of any node or its childs
see the following example:
var xdoc = XDocument.Load("filePath, xml as a stream, xml reader or text reader" );
var elems = fileDataXDoc.Descendants("Address").Elements();
the above part of code will return all sub address nodes as IEnumerable<XElements>
then you can work with it
Related
I'm a beginner programmer working on a small webscraper in C#. The purpose is to take a hospital's public website, grab the data for each doctor, their department, phone and diploma info, and display it in a Data Grid View. It's a public website, and as far as I'm concerned, the website's robots.txt allows this, so I left everything in the code as it is.
I am able to grab each data (name, department, phone, diploma) separately, and can successfully display them in a text box.
// THIS WORKS:
string text = "";
foreach (var nodes in full)
{
text += nodes.InnerText + "\r\n";
}
textBox1.Text = text;
However, when I try to pass the data on to the data grid view using a class, the foreach loop only goes through the first name and fills the data grid with that.
foreach (var nodes in full)
{
var Doctor = new Doctor
{
Col1 = full[0].InnerText,
Col2 = full[1].InnerText,
Col3 = full[2].InnerText,
Col4 = full[3].InnerText,
};
Doctors.Add(Doctor);
}
I spent a good few hours looking for solutions but none of what I've found have been working, and I'm at the point where I can't decide if I messed up the foreach loop somehow, or if I'm not doing something according to HTML Agility Pack's rules. It lets me iterate through for the textbox, but not the foreach. Changing full[0] to nodes[0] or nodes.InnerText doesn't seem to solve it either.
link to public gist file (where you can see my whole code)
screenshot
Thank you for the help in advance!
The problem is how you're selecting the nodes from the page. full contains all individual names, departments etc. in a flat list, which means full[0] is the name of the first doctor while full[4] is the name of the next. Your for-loop doesn't take that into account, as you (for every node) always access full[0] to full[3] - so, only the properties of the first doctor.
To make your code more readable I'd split it up a bit to first make a list of all the card-elements for each doctor and then select the individual parts within the loop:
HtmlWeb web = new HtmlWeb();
HtmlAgilityPack.HtmlDocument doc = new HtmlAgilityPack.HtmlDocument();
doc = web.Load("https://klinikaikozpont.unideb.hu/doctor_finder");
const string doctorListItem = "div[contains(#class, 'doctor-list-item-model')]";
const string cardContent = "div[contains(#class, 'card-content')]";
var doctorCards = doc.DocumentNode.SelectNodes($"//{doctorListItem}/{cardContent}");
var doctors = new List<Doctor>();
foreach (var card in doctorCards)
{
var name = card.SelectSingleNode("./h3")?.InnerText;
const string departmentNode = "div[contains(#class, 'department-name')]";
var department = card.SelectSingleNode($"./{departmentNode}/p")?.InnerText;
// other proprties...
doctors.Add(new Doctor{NameAndTitle = name, Department = department});
}
// I took the liberty to make this class easier to understand
public class Doctor
{
public string NameAndTitle { get; set; }
public string Department { get; set; }
// Add other properties
}
Check out the code in action.
it's me again. This time, having some issues with XML. I had everything working in VB.NET (I'll show all the code I used later) but now I'm developing something else for VB application except I'm using C# for it. Part of this involves reading an XML and populating something specific into a ListBox and then being able to click on it and get the attributes for use in other controls (description loads to a text box, etc, you'll see).
I can't seem to figure out XML for the life of me in C# however. In VB, I did it like this:
Dim games() As String = xml...<episode>.Select(Function(n) n.Value).ToArray
AvailableEpisodes.DataSource = games
Where "AvailableEpisodes" is the ListBox I wish to populate. This displayed the "This is a test" term: This is a test
And then this is the SelectedIndexChanged code:
Dim node As XElement = xml...<episode>.First(Function(n) n.Value = AvailableEpisodes.Text)
DescriptionTextBox.Text = node.#Description
AuthorTextBox.Text = node.#Author
generatedDownloadLink = node.#DownloadLink
generatedTechName = node.#TechName
IconImage.ImageLocation = node.#IconLoc
What exactly would be the C# equivalent of this? I already tried copy-pasting (just figured I'd try it) and a couple code converters and none of them seem to work.
The goal of this application will be to be able to double click on the selected index and load these strings into another window (which I can work out by attaching the nodes to a string variable) I just need to get started.
Code I've tried:
using (XmlReader reader = XmlReader.Create(testXml))
{
while (reader.Read())
{
if ((reader.NodeType == XmlNodeType.Element) && reader.Name == "episode")
{
listBox1.DataSource = reader.GetAttribute("TechName").ToList();
}
}
}
But that literally just outputs this: http://imgur.com/Naeabf9.png
Any extra information I'll toss in an edit or a reply
Thanks in advanced,
Mike
Its easy: ... corresponds to Descendants. <foo> corresponds to element name. #bar corresponds to attribute name.
var xml = XDocument.Load(path_to_xml);
var node = xml.Descendants("episode")
.First(n => n.Value == AvailableEpisodes.Text);
DescriptionTextBox.Text = (string)node.Attribute("Description");
AuthorTextBox.Text = (string)node.Attribute("Author");
generatedDownloadLink = (string)node.Attribute("DownloadLink");
generatedTechName = (string)node.Attribute("TechName");
IconImage.ImageLocation = (string)node.Attribute("IconLoc");
Note - if there is no matching node, then First will throw exception. Usually you should use FirstOrDefault in such case and then check node for null.
Is "This is a test" supposed to be one single item in the ListBox? If it is, your logic in the C# code is wrong. Indeed, reader.GetAttribute("TechName").ToList() will return an array like this
["T","h","i", "s", " ", "i", "s", " ", "t", "e", "s", "t"]
Therefore, the text gets broken up into many items in the ListBox.
To read all nodes in the xml documents into ListBox, you have to create a temporary list to hold all the results read from the xml, then at the end bind the list to ListBox listBox1.DataSource = results
Why don't you just use XML Serialization?
Assuming your XML looks something like this:
<EpisodeData>
<Episodes>
<Episode Description="..." Author="..." DownloadLink="..." ... />
<Episode Description="..." Author="..." DownloadLink="..." ... />
<Episode Description="..." Author="..." DownloadLink="..." ... />
</Episodes>
</EpisodeData>
Create classes in C# that represents the data. Something like this:
[Serializable]
public class EpisodeData
{
[XmlArray("Episodes")]
[XmlArrayItem(ElementName = "Episode")]
List<Episode> Episodes { get; set; }
}
[Serializable]
public class Episode
{
[XmlAttribute]
public string Description { get; set; }
[XmlAttribute]
public string Author { get; set; }
[XmlAttribute]
public string DownloadLink { get; set; }
...
}
Then you can deserialize and use the data like this:
EpisodeData data;
XmlSerializer serializer = new XmlSerializer(typeof(EpisodeData));
using (StreamReader sr = new StreamReader(fileName))
{
data = (EpisodeData)serializer.Deserialize(sr);
}
// Assuming you only want to see the description. If you want something else
// you might want to use a DataGrid to bind to each property in Episode or
// override ToString in Episode.
List<string> descriptions = new List<string>();
foreach (Episode episode in data.Episodes)
{
descriptions.Add(episode.Description);
}
listBox1.DataSource = descriptions;
More info on XML Serialization here:
http://msdn.microsoft.com/en-us/library/58a18dwa(v=vs.110).aspx
All the values present are not getting added to the dictionary in c#
the following is the code snippet so far :
XmlNodeList Bugs_filter = Bugs.SelectNodes("/criteria/includeFilterSets/filterSet/filter");
if (Bugs_filter != null)
{
foreach (XmlNode BNode in Bugs_filter)
{
string data = BNode.SelectSingleNode("/criteria/includeFilterSets/filterSet/filter/filterName").InnerText;
string aggregate = BNode.SelectSingleNode("/criteria/includeFilterSets/filterSet/filter/value").InnerText;
{
Dict.Add(data,aggregate);
}
}
}
There are total 3 values present in the 'Bugs_Filter' whereas when it comes in loop second time it picks up the first value only.Please suggest what i am doing wrong.
Please refer to the XML from which i am extracting the values :
<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><criteria><includeFilterSets><filterSet><filter><filterId>42</filterId>
<filterName>PRODUCT_ID</filterName><operator><id>1</id><name>Equals</name></operator><value>1113</value></filter><filter><filterId>41</filterId>
<filterName>FAMILY_ID</filterName><operator><id>1</id><name>Equals</name></operator><value>ESG</value></filter><filter><filterId>3</filterId><filterName>VERSION_NAME</filterName>
<operator><id>1</id><name>Equals</name></operator><value>4.5</value></filter></filterSet></includeFilterSets><excludeFilterSets/><ordering/>
<bugGroupSearchMode>0</bugGroupSearchMode><caseSensitive>true</caseSensitive><entityToSearch><id>1</id><name>BUG</name></entityToSearch></criteria>
Please note that i am extracting the PRODUCT_ID,FAMILY_ID and VERSION_NAME.
please notice what are you trying to do here:
in the foreach statement you have the filter nodes, and under them you search for the whole path "/criteria/includeFilterSets/filterSet/filter/filterName". you won't get to the right nodes like that.
instead, use BNode.ChildNodes[1] and BNode.ChildNodes[3] respectfully
string data = BNode.ChildNodes[1].InnerText;
string aggregate = BNode.ChildNodes[3].InnerText;
or even better:
string data = BNode["filterName"].InnerText;
string aggregate = BNode["value"].InnerText;
Have you tried?
string data = BNode.SelectSingleNode("./filterName").InnerText;
string aggregate = BNode.SelectSingleNode("./value").InnerText;
Since you give the full path it starts searching from root element each time. By . you say something like "filterName node under this node" where this is current BNode
Greetings. Im having a small trouble i would like to have some help with. Im having a very large xml file with about 1000 customers with diffrent customer information. And I would like to do methods to retrive this information. Ive been searching everywhere but cant seem to find what im looking for. Currently im trying:
public custInformation getcustInfo(string file) {
//Load the xml file
var xe = XDocument.Load(_server.MapPath(file)).Root;
//Get information
return (from p in xe.Descendants("cust-account").Descendants("cust-info")
select new custInformation
{
firstName = (string)p.Element("cust-fname"),
lastName = (string)p.Element("cust-lname"),
address = (string)p.Element("cust-address1"),
}).(All elements)??
}
(All elements) is where id like to retrive all the information. Using FirstOrDefault will only retrive the first element and LastOrDefault will only retrive the first element. If some one could help me i would be very greatefull.
you want a list of customers. Change the return value to IEnumerable
and transform the query to IEnumerable with ToList()/ToArray():
public IEnumerable<custInformation> getcustInfo(string file) {
//Load the xml file
var xe = XDocument.Load(_server.MapPath(file)).Root;
//Get information
return (from p in xe.Descendants("cust-account").Descendants("cust-info")
select new custInformation
{
firstName = (string)p.Element("cust-fname"),
lastName = (string)p.Element("cust-lname"),
address = (string)p.Element("cust-address1"),
}).ToList();
}
i need to store all the informationen from the xml in an array. My code doesn't work, because I always get just the first item from the xml.
Does anyone know how to fix this?
XDocument xdoc = XDocument.Load("http://www.thefaxx.de/xml/nano.xml");
var items = from item in xdoc.Descendants("items")
select new
{
Title = item.Element("item").Element("title").Value,
Description = item.Element("item").Element("description").Value
};
foreach (var item in items)
{
listView1.Items.Add(item.Title);
}
How about:
var items = from item in xdoc.Descendants("item")
select new
{
Title = item.Element("title").Value,
// *** NOTE: xml has "desc", not "description"
Description = item.Element("desc").Value
};
It is a little hard to be sure without sample xml - but it looks like you intend to loop over all the <item>...</item> elements - which is what the above does. Your original code loops over the (single?) <items>...</items> element(s), then fetches the first <item>...</item> from within it.
edit after looking at the xml; this would be more efficient:
var items = from item in xdoc.Root.Elements("item")
select new {
Title = item.Element("title").Value,
Description = item.Element("desc").Value
};