Read XML Attribute - c#

I am actually trying to read this piece of XML.
http://datapoint.metoffice.gov.uk/public/data/val/wxfcs/all/xml/351352?res=3hourly&key=99b9f578-ad3d-446c-9d29-0bbee028b483
I was wondering how I could read only the node Period with the value="2012-11-15Z"
So the one below :
This is the code I use
using (XmlReader reader = XmlReader.Create("http://datapoint.metoffice.gov.uk/public/data/val/wxfcs/all/xml/351352?res=3hourly&key=99b9f578-ad3d-446c-9d29-0bbee028b483"))
{
reader.MoveToContent();
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element
&& reader.Name == "Period")
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element &&
reader.Name == "Rep")
{
first.Text = reader.GetAttribute("T");
}
}
}
}
}
What is the way for me to read only this node ?
Should I write
if (reader.NodeType == XmlNodeType.Element
&& reader.Name == "Period" && reader.GetAttribute("value") == "2012-11-15Z")
This doesn't seem to work ..
Can someone help me ?

You can easily do that with LINQ to XML:
XDocument xdoc = XDocument.Load(path_to_xml);
var period = xdoc.Descendants("Period")
.Where(p => (string)p.Attribute("value") == "2012-11-15Z")
.SingleOrDefault();
It will return XElement, but you can select any data from period. E.g. T attributes:
List<int> tList = xdoc.Descendants("Period")
.Where(p => (string)p.Attribute("value") == "2012-11-15Z")
.SelectMany(p => p.Elements())
.Select(rep => (int)rep.Attribute("T"))
.ToList();
var query = xdoc.Descendants("Period")
.Where(p => (string)p.Attribute("value") == "2012-11-15Z")
.SelectMany(p => p.Elements())
.Select(rep => new {
T = (int)rep.Attribute("T"),
D = (string)rep.Attribute("D") })
.ToList();
Last query will return List of strongly-typed anonymous objects with integer property T and string property D:
foreach(var x in query)
// use x.T and x.D

Try using xpath to lookup the value like this
XmlDocument doc = new XmlDocument();
using (XmlReader reader = XmlReader.Create("http://datapoint.metoffice.gov.uk/public/data/val/wxfcs/all/xml/351352?res=3hourly&key=99b9f578-ad3d-446c-9d29-0bbee028b483"))
{
doc.Load(reader);
XmlNodeList list = doc.SelectNodes("//Period[#value='2012-11-15Z']");
Console.WriteLine(list.Count);
}

Related

XML element with multiple different Text elements

I have the following elements as part of an XML document:
<RegisterEntry>
<EntryNumber>3</EntryNumber>
<EntryDate>2009-01-30</EntryDate>
<EntryType>Registered Charges</EntryType>
<EntryText>REGISTERED CHARGE dated 30 December 2008.</EntryText>
</RegisterEntry>
<RegisterEntry>
<EntryNumber>4</EntryNumber>
<EntryType>Registered Charges</EntryType>
<EntryText>REGISTERED CHARGE dated 30 December 2008.</EntryText>
</RegisterEntry>
I am using XmlReader to iterate through the document. The RegisterEntry is an XMLNodeType.Element and the four enclosed in this element are XmlNodeType.Text. How can I assign each of these Text values to a different variable as the XmlReader returns an empty string for Node.Name on a NodeType.Text. Also, the repeated elements do not always have the same number of text elements. Code below:
XmlTextReader reader = new XmlTextReader(fName);
if(reader.NodeType == XmlNodeType.Element && reader.Name =="RegisterEntry")
{
propEntryNo = "";
propEntryDate = "";
propEntryType = "";
propEntryText = "";
while(reader.Read())
{
if(reader.NodeType == XmlNodeType.Text && reader.Name == "EntryNumber" && reader.HasValue)
{
propEntryNo = reader.Value;
}
if (reader.NodeType == XmlNodeType.Text && reader.Name == "EntryDate" && reader.HasValue)
{
propEntryDate = reader.Value;
}
if (reader.NodeType == XmlNodeType.Text && reader.Name == "EntryType" && reader.HasValue)
{
propEntryType = reader.Value;
}
if (reader.NodeType == XmlNodeType.Text && reader.Name == "EntryText" && reader.HasValue)
{
propEntryText += reader.Value + ",";
}
if(reader.NodeType == XmlNodeType.EndElement && reader.Name == "RegisterEntry")
{
add variable values to list
break;
}
}
}
In each of the if statements above the NodeType returns as Text and the Name as an empty string.
The XML element and the text inside are different nodes!
You have to read the content of the XML element first. Simple example:
switch (reader.Name)
{
// found a node with name = "EntryNumber" (type = Element)
case "EntryNumber":
// make sure it's not the closing tag
if (reader.IsStartElement())
{
// read the text inside the element, which is a seperate node (type = Text)
reader.Read();
// get the value of the text node
propEntryNo = reader.Value;
}
break;
// ...
}
Another option would be ReadElementContentAsString
switch (reader.Name)
{
case "EntryNumber":
propEntryNo = reader.ReadElementContentAsString();
break;
// ...
}
Of course, these simple examples assume that the XML is in the expected format. You should include appropriate checks in your code.
As for the other suggested solutions:
You could XmlDocument or XDocument or instead. The handling is easier, but the memory overhead is bigger (see also).
Deserializing the XML into objects is another option. But I feel handling errors caused by an unexpected format is trickier then.
You can use XDocument to list your RegisterEntry child node like
class Program
{
static void Main(string[] args)
{
XDocument doc = XDocument.Load(#"C:\Users\xxx\source\repos\ConsoleApp4\ConsoleApp4\Files\XMLFile14.xml");
var registerEntries = doc.Descendants("RegisterEntry");
var result = (from e in registerEntries
select new
{
EntryNumber = e.Element("EntryNumber") != null ? Convert.ToInt32(e.Element("EntryNumber").Value) : 0,
EntryDate = e.Element("EntryDate") != null ? Convert.ToDateTime(e.Element("EntryDate").Value) : (DateTime?)null,
EntryType = e.Element("EntryType") != null ? e.Element("EntryType").Value : "",
EntryText = e.Element("EntryText") != null ? e.Element("EntryText").Value : "",
}).ToList();
foreach (var entry in result)
{
Console.WriteLine($"EntryNumber: {entry.EntryNumber}");
Console.WriteLine($"EntryDate: {entry.EntryDate}");
Console.WriteLine($"EntryType: {entry.EntryType}");
Console.WriteLine($"EntryText: {entry.EntryText}");
Console.WriteLine();
}
Console.ReadLine();
}
}
Output:
You can also make certain operations on your list like.
//If you want to get all `EntryText` in xml to be comma separated then you can do like
string propEntryText = string.Join(", ", result.Select(x => x.EntryText));
//Get first register entry from xml
var getFirstRegisterEntry = result.FirstOrDefault();
//Get last register entry from xml
var getLastRegisterEntry = result.LastOrDefault();
//Get register entry from xml with specific condition
var getSpecificRegisterEntry = result.Where(x => x.EntryNumber == 3).SingleOrDefault();

grab xml child of child element

i have successfully accessed itemId, galleryURL, title, viewItemURL value under item collections but problem is i need to access another value which is not directly under item collections it just inside another child. Please have a look on this picture of XML to get better idea. Please advice me how can i access listingInfo->watchCount
public ActionResult Search(string OperationName, string calltype, string page, string keywords, string type, string location, string condition, string min, string max, string negative, string minFeedback, string maxFeedback, string drange, string categoryId)
{
string AppId = "demo-key"; //api configs
string BaseUrl = "http://svcs.ebay.com/services/search/FindingService/v1?OPERATION-NAME="; //base url api end point
if (calltype == "categoryClick")
{
string Url = BaseUrl + OperationName + "&SERVICE-VERSION=1.0.0&SECURITY-APPNAME=" + AppId + "&RESPONSE-DATA-FORMAT=XML&REST-PAYLOAD&categoryId=" + categoryId + "&paginationInput.entriesPerPage=2&paginationInput.pageNumber=" + page + "";
var items = new List<EbayDataViewModel>();
XDocument xdoc = XDocument.Load(Url);
// Since i am only interested in <item> collections within <searchResult>
var searchResultItems = xdoc.Descendants()
.Where(x => x.Name.LocalName == "item");
foreach (var sri in searchResultItems)
{
// Get all child xml elements
var childElements = sri.Elements();
var itemId = childElements.FirstOrDefault(x => x.Name.LocalName == "itemId");
var imageurl = childElements.FirstOrDefault(x => x.Name.LocalName == "galleryURL");
var title = childElements.FirstOrDefault(x => x.Name.LocalName == "title");
var url = childElements.FirstOrDefault(x => x.Name.LocalName == "viewItemURL");
// var numberofwatch = childElements.Where(x => x.Name.LocalName == "listinginfo"); this is one step inside of another element
//add items from xml data to EbayDataViewModel object
items.Add(new EbayDataViewModel
{
ItemId = itemId == null ? String.Empty : itemId.Value,
EbayImageUrl = imageurl == null ? String.Empty : imageurl.Value,
EbayTitle = title == null ? String.Empty : title.Value,
EbayUrl = url == null ? String.Empty : url.Value,
//NumberOfWatch = numberofwatch == null ? String.Empty : numberofwatch.Value,
});
}
var e = Json(items);
return Json(items);
}else{
}
return null;
}
You can use XPathSelectElement (see MSDN XPathSelectElement)
string str =
#"<?xml version=""1.0""?>
<sri>
<item><listingInfo><watchCount>1</watchCount></listingInfo></item>
<item><listingInfo><watchCount>2</watchCount></listingInfo></item>
</sri>";
XDocument xdoc = XDocument.Parse(str);
var searchResultItems = xdoc.Descendants().Where(x => x.Name.LocalName == "item");
foreach (var item in searchResultItems)
{
var wc = item.XPathSelectElement("listingInfo/watchCount");
Console.WriteLine(wc.Value);
}
This line of code solves your problem i think
var nofwatch = childElements.FirstOrDefault(x => x.Name.LocalName ==
"listingInfo").Elements().FirstOrDefault(x => x.Name.LocalName ==
"watchCount");
foreach (var sri in searchResultItems)
{
// Get all child xml elements
var childElements = sri.Elements();
var itemId = childElements.FirstOrDefault(x => x.Name.LocalName == "itemId");
var imageurl = childElements.FirstOrDefault(x => x.Name.LocalName == "galleryURL");
var title = childElements.FirstOrDefault(x => x.Name.LocalName == "title");
var url = childElements.FirstOrDefault(x => x.Name.LocalName == "viewItemURL");
var nofwatch = childElements.FirstOrDefault(x => x.Name.LocalName == "listingInfo").Elements().FirstOrDefault(x => x.Name.LocalName == "watchCount");
//add items from xml data to EbayDataViewModel object
items.Add(new EbayDataViewModel
{
ItemId = itemId == null ? String.Empty : itemId.Value,
EbayImageUrl = imageurl == null ? String.Empty : imageurl.Value,
EbayTitle = title == null ? String.Empty : title.Value,
EbayUrl = url == null ? String.Empty : url.Value,
NumberOfWatch = nofwatch == null ? String.Empty : nofwatch.Value,
});
}

Reading all matches from attribute value XML C#

I have a code which retrieves only one value from the xml file using xmlreader.
<recordset>
<itemidlist>
<itemid idtype = "plant">787484545</itemid>
<itemid idtype = "seed">659988222</itemid>
</itemidlist>
<itemidlist>
<itemid idtype = "plant">90327328</itemid>
<itemid idtype = "seed">099849999</itemid>
</itemidlist>
<itemidlist>
<itemid idtype = "plant">34545488</itemid>
<itemid idtype = "seed">787555444</itemid>
</itemidlist>
</recordset>
And C# coded this entry:(s is the xml file)
using (var reader = XmlReader.Create(s))
{
var nodecount = 0;
var plant = "";
var seed = "";
var typeid = false;
var typeid2 = false;
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element &&
reader.Name == "recordset")
{
nodecount++;
}
if (reader.NodeType == XmlNodeType.Element &&
reader.Name == "itemid")
{
var idtype = reader.GetAttribute("idtype");
typeid = idtype == "plant";
typeid2 = idtype == "seed";
}
while (typeid && reader.NodeType == XmlNodeType.Element &&
reader.Name == "itemid")
{
plant = reader.ReadInnerXml();
}
while (typeid2 && reader.NodeType == XmlNodeType.Element &&
reader.Name == "itemid")
{
seed = reader.ReadInnerXml();
}
}
}
and this happens when a add to datagridview:
Only one record was found:
Plant Seed
787484545 659988222
You're only setting on seed and one plant variable, so if any more than one value is found it will be overwritten. How do you propose to return multiple values?
Any reason not to use LINQ to XML for this?
var doc = XDocument.Load(s);
var plants = doc.Descendants("itemid")
.Where(x => (string) x.Attribute("idtype") == "plant")
.Select(x => x.Value);
var seeds = doc.Descendants("itemid")
.Where(x => (string) x.Attribute("idtype") == "seed")
.Select(x => x.Value);
See this fiddle for a working demo.

XmlReader How to read properly?

what is the best way to read xml like this one:
<Users>
<user name = "mail">
<supplier name = "supp1">
<project name = "proj1">
<subscribe name= "sub1"/>
<subscribe name = "sub2"/>
</project>
</supplier>
<supplier name = "supp2">
<project name = "proj2">
<subscribe name = "sub3"/>
</project>
<project name = "proj3">
<subscribe name= "sub4"/>
<subscribe name = "sub5"/>
</project>
<project name = "proj4"/>
</supplier>
<supplier name = "supp3"/>
<supplier name = "supp5">
<project name = "proj4"/>
<supplier name = "supp4"/>
</user>
</Users>
For now I am using
While(reader.Read())
{
if (((reader.NodeType == XmlNodeType.EndElement) && (reader.Name == "user")))
break;
if ((reader.NodeType == XmlNodeType.Element) && (reader.Name =="supplier"))
{
foreach (TreeNode tree in TreeView1.Nodes)
{
if (reader.GetAttribute(0) == tree.Text)
{
TreeView1.SelectedNode = tree;
TreeView1.SelectedNode.Checked = true;
Get_projects(reader, tree);
break;
}
}
}
}
this is the main after that is get_projects(...):
private void Get_projects(XmlReader reader, TreeNode tree)
{
while (reader.Read())
{
if ((reader.NodeType == XmlNodeType.EndElement) && (reader.Name == "supplier")) break;
//(reader.IsEmptyElement && reader.Name == "supplier")
if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "project"))
{
foreach (TreeNode projTree in tree.Nodes)
{
if (reader.GetAttribute(0) == projTree.Text)
{
TreeView1.SelectedNode = projTree;
TreeView1.SelectedNode.Checked = true;
Get_subscribes(reader, projTree);
break;
}
}
}
}
}
the Get_subscribes(reader, projTree):
private void Get_subscribes(XmlReader reader, TreeNode tree)
{
while (reader.Read())
{
if ((reader.NodeType == XmlNodeType.EndElement) && (reader.Name == "project") ||
(reader.IsEmptyElement && reader.Name == "project")) break;
if ((reader.NodeType == XmlNodeType.Element) && (reader.Name == "subscribe"))
{
foreach (TreeNode subTree in tree.Nodes)
{
if (reader.GetAttribute(0) == subTree.Text)
{
TreeView1.SelectedNode = subTree;
TreeView1.SelectedNode.Checked = true;
break;
}
}
}
}
}
It doesn't work, so I am wondering is there a better way or what am i missing?
I will give you sample to read properly
<ApplicationPool>
<Accounts>
<Account>
<NameOfKin></NameOfKin>
<StatementsAvailable>
<Statement></Statement>
</StatementsAvailable>
</Account>
</Accounts>
</ApplicationPool>
static IEnumerable<XElement> SimpleStreamAxis(string inputUrl, string elementName)
{
using (XmlReader reader = XmlReader.Create(inputUrl))
{
reader.MoveToContent();
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name == elementName)
{
XElement el = XNode.ReadFrom(reader) as XElement;
if (el != null)
{
yield return el;
}
}
}
}
}
}
using (XmlReader reader = XmlReader.Create(inputUrl))
{
reader.ReadStartElement("theRootElement");
while (reader.Name == "TheNodeIWant")
{
XElement el = (XElement) XNode.ReadFrom(reader);
}
reader.ReadEndElement();
}
Source: Reading Xml with XmlReader in C#
Hope this help.
I would consider the reverse approach i.e.: instead of reading XML and checking if a node exists in TreeView I would prefer to use XPath to check if a node exists in XML document.
In order to do so you have to traverse TreeView nodes and for each node build XPath query
e.g.: /Users/user/supplier[#name='supp1']/project[#name='proj1'].
Having XPath query you can create an instance of XPathDocument based on your XMLReader and run the query. If something is found, you will check current node in TreeView.
you can try XPath to read informations you need
XMLDocument doc = new XMLDocument();
doc.Load(your_xml_file_path);
XMLNodeList list = doc.SelectNodes(#"//project"); //get all project element

Read CDATA section based on where clause using LINQ

I am trying to read CDATA section of an xml node based upon the where clause.
<Book>
<BookItem ISBN="SKS84747"><![CDATA[20]]> </BookItem>
<BookItem ISBN="LHKGOI84747"><![CDATA[50]]> </BookItem>
<BookItem ISBN="PUOT84747"><![CDATA[20]]> </BookItem>
</Book>
This code gives me all the CDATA sections,
var value = from v in x.Descendants("BookItem").OfType<XCData>()
select (string)v.Value;
How to put where clause based on ISBN ?
How can I read this CDATA using LINQ to XML.
var value = x.DescendantNodes().OfType<XCData>()
.Where(m => m.Parent.Name == "BookItem" && m.Parent.Attribute("ISBN").Value == "PUOT84747")
.ToList();
or
before ToList()
.Select(cdata => cdata.Value.ToString());
var xml = Resource1.String1;
var doc = XDocument.Parse(xml);
var isbn = "SKS84747";
var query = string.Format("BookItem[#ISBN='{0}']", isbn);
var book = doc.Root.XPathSelectElement(query);
if (book != null)
Console.WriteLine(book.Value);
OR
var book =
doc.Root.Descendants("BookItem").Where(
x => x.Attribute("ISBN") != null && x.Attribute("ISBN").Value == isbn).Select(x => x.Value).
FirstOrDefault();
OR
var book = (from item in doc.Root.Descendants("BookItem")
where item.Attributes("ISBN").Any() && item.Attribute("ISBN").Value == isbn
select item.Value).FirstOrDefault();

Categories

Resources