I can't get value from xml - c#

I have xml
<?xml version="1.0" encoding="UTF-8"?>
<info lang="ru" xmlns:x="http://www.yandex.ru/xscript">
<region id="213" lon="37.617671" lat="55.755768" zoom="10">
<title>Москва</title>
</region>
<traffic lon="37.617671" lat="55.755768" zoom="10"region="213">
<length>489164.0</length>
<level>6</level>
<icon>yellow</icon>
<timestamp>1365162401</timestamp>
<time>15:46</time>
<url>http://maps.yandex.ru/moscow_traffic</url>
<title>Москва</title>
</traffic>
</info>
And I need to get value from "level"
public void GetText(string filename)
{
try
{
XDocument xDocument = LoadPage(filename);
if (xDocument.Root == null) return;
XElement elem = xDocument.Root.Element("info");
if (elem != null)
foreach (var el in elem.Elements("traffic"))
{
Name = el.Element("level").Value;
};
}
catch (Exception e)
{
MessageBox.Show(e.Message);
}
}
This block of code works good with another xml. It can't find "info", and elem=null. What's wrong with this code. Or how can I get this value in other way. Thanks!

This is the problem:
XElement elem = xDocument.Root.Element("info");
In the XML you've given us, xDocument.Root is the info element. Just change that to:
XElement elem = xDocument.Element("info");
and that will check that the root element really is info.
Another alternative would be:
foreach (var el in xDocument.Elements("info")
.Elements("traffic"))
That way you just won't go into the loop body if Elements(info) returns an empty collection.
EDIT: If you need it to work on documents where sometimes info is the root element and sometimes it's not, you might want to use:
foreach (var el in xDocument.Descendants("info")
.Take(1)
.Elements("traffic"))
(It's pretty odd to be in that situation though.)

Related

.NET search sub-elements in xml

I'm trying to load contents of a XML file into a list of custom types using Linq following the instructions in the official microsoft dotNet api:
https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.find?view=netframework-4.8
My xml file looks like this:
<directives>
<dir directive="Question" response="Response"></dir>
<dir directive="Q2" response="Response2"></dir>
<dir directive="Q3" response="Response3"></dir>
</directives>
and the importing code is pretty much the same as in the example linked above.
public static void ImportDirectives()
{
// Create XML elements from a source file.
XElement xTree = XElement.Load(AppDomain.CurrentDomain.BaseDirectory + "directives.xml");
// Create an enumerable collection of the elements.
IEnumerable<XElement> elements = xTree.Elements();
// Evaluate each element and set set values in the book object.
foreach (XElement el in elements)
{
Directive dir = new Directive();
dir.directive = el.Attribute("directive").Value;
IEnumerable<XElement> props = el.Elements();
foreach (XElement p in props)
{
if (p.Name.ToString().ToLower() == "response")
{
dir.response = p.Value;
}
}
Dir.Add(dir);
}
}
The code works fine if I remove the root element but only add the root element into my list if I add one.
I'd prefer having a root element just to make my XML look proper.
How would I access the elements within the root element using this code?
When you add root then xml like
<Root>
<directives>
<dir directive="Question" response="Response"></dir>
<dir directive="Q2" response="Response2"></dir>
<dir directive="Q3" response="Response3"></dir>
</directives>
</Root>
When you fetch first Elements() then
<directives>
<dir directive="Question" response="Response"></dir>
<dir directive="Q2" response="Response2"></dir>
<dir directive="Q3" response="Response3"></dir>
</directives>
Again fetch Elements() then you'll get Node
<dir directive="Question" response="Response"></dir>
Then you access attribute and values
public static void ImportDirectives()
{
// Create XML elements from a source file.
XElement xTree = XElement.Load(AppDomain.CurrentDomain.BaseDirectory + "directives.xml");
// Create an enumerable collection of the elements.
IEnumerable<XElement> elements = xTree.Elements();
// Evaluate each element and set set values in the book object.
foreach (XElement el in elements.Elements())
{
Directive dir = new Directive();
dir.directive = el.Attribute("directive").Value;
IEnumerable<XElement> props = el.Elements();
foreach (XElement p in props)
{
if (p.Name.ToString().ToLower() == "response")
{
dir.response = p.Value;
}
}
Dir.Add(dir);
}
}
If you are sure that your XML Schema is going to be fixed, you can access the attribute values of the XML elements directly.
I have attached a sample code.
public static void ImportDirectives()
{
string fileName = AppDomain.CurrentDomain.BaseDirectory + "directives.xml";
// Create XML elements from a source file.
XElement xTree = XElement.Load(fileName);
// Create an enumerable collection of the elements.
IEnumerable<XElement> elements = xTree.Elements();
// Evaluate each element and set set values in the book object.
foreach (XElement el in elements)
{
string directive = el.Attribute("directive").Value;
string response = el.Attribute("response").Value;
Console.WriteLine(directive + ":" + response);
}
}

Unable to cast object XText to XElement

I'm a bit stuck, currently getting a an error being caught in the exception 'ex' at the start of the second run through of the foreach loop.
Why is this happening and what am I doing wrong?
{"Unable to cast object of type 'System.Xml.Linq.XText' to type 'System.Xml.Linq.XElement'."} System.Exception {System.InvalidCastException}'
My code:
public static void LoadMyThing()
{
string configfp = #"\ConfigFiles\config.xml";
string basefp = #"\ConfigFiles\";
try
{
List<string> xmlNodes = new List<string>();
XDocument Xdoc = XDocument.Load(configfp);
XElement XmlLst = Xdoc.Element("mylistener");
foreach (XElement node in XmlLst.Element("parent").DescendantNodes())
{
thingy = (basefp + node.Value);
Thingylist.Add(thing);
}
}
catch(Exception ex)
{
WriteErrorLog("Thingy Loading error " + ex.ToString());
}
}
The XML it's calling to:
<?xml version="1.0" encoding="utf-8"?>
<mylistener>
<config>
<mailuser>b#c.com</mailuser>
<mailpass>H</mailpass>
<mailip>190</mailip>
<mailport>2</mailport>
<recipient>m</recipient>
</config>
<parent>
<b>\RFC16</b>
<b>\RFC122</b>
<b>\RF1</b>
<b>\RF32</b>
<b>\R33</b>
<b>\RFCIB</b>
</parent>
</mylistener>
Hard to be sure without seeing your XML, but if the parent node has non XElement descendants then I'd expect to see this. For example
<parent>
<some node />
some text
</parent>
You need to know how you want to handle this case, but one way to avoid an exception would be to iterate XNodes rather than XElements
foreach (XNode node in XmlLst.Element("parent").DescendantNodes())
{
if (node is XElement)
{
thingy = (basefp + ((XElement)node).Value);
Thingylist.Add(mibbler);
}
else
{
// do something else
}
}

How to iterate a xml file with XmlReader class

my xml stored in xml file which look like as below
<?xml version="1.0" encoding="utf-8"?>
<metroStyleManager>
<Style>Blue</Style>
<Theme>Dark</Theme>
<Owner>CSRAssistant.Form1, Text: CSR Assistant</Owner>
<Site>System.ComponentModel.Container+Site</Site>
<Container>System.ComponentModel.Container</Container>
</metroStyleManager>
this way i am iterating but some glitch is there
XmlReader rdr = XmlReader.Create(System.IO.Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath) + #"\Products.xml");
while (rdr.Read())
{
if (rdr.NodeType == XmlNodeType.Element)
{
string xx1= rdr.LocalName;
string xx = rdr.Value;
}
}
it is always getting empty string xx = rdr.Value;
when element is style then value should be Blue as in the file but i am getting always empty....can u say why?
another requirement is i want to iterate always within <metroStyleManager></metroStyleManager>
can anyone help for the above two points. thanks
Blue is the value of Text node, not of Element node. You either need to add another if to get value of text nodes, or you can read inner xml of current element node:
rdr.MoveToContent();
while (rdr.Read())
{
if (rdr.NodeType == XmlNodeType.Element)
{
string name = rdr.LocalName;
string value = rdr.ReadInnerXml();
}
}
You can also use Linq to Xml to get names and values of root children:
var xdoc = XDocument.Load(path_to_xml);
var query = from e in xdoc.Root.Elements()
select new {
e.Name.LocalName,
Value = (string)e
};
You can use the XmlDocument class for this.
XmlDocument doc = new XmlDocument.Load(filename);
foreach (XmlNode node in doc.ChildNodes)
{
if (node.ElementName == "metroStyleManager")
{
foreach (XmlNode subNode in node.ChildNodes)
{
string key = subNode.LocalName; // Style, Theme, etc.
string value = subNode.Value; // Blue, Dark, etc.
}
}
else
{
...
}
}
you can user XDocument xDoc = XDocument.Load(strFilePath) to load XML file.
then you can use
foreach (XElement xeNode in xDoc.Element("metroStyleManager").Elements())
{
//Check if node exist
if (!xeNode.Elements("Style").Any()
//If yes then
xeNode.Value
}
Hope it Helps...
BTW, its from System.XML.Linq.XDocument

Removing XML node

I have got yet another task I am not able to accomplish: I am supposed to parse the XML from this site, remove all the nodes that don't have "VIDEO" in their name and then save it to another XML file. I have no problems with reading and writing, but removing makes me some difficulties. I have tried to do the Node -> Parent Node -> Child Node work-aroud, but it did not seem useful:
static void Main(string[] args)
{
using (WebClient wc = new WebClient())
{
string s = wc.DownloadString("http://feeds.bbci.co.uk/news/health/rss.xml");
XmlElement tbr = null;
XmlDocument xml = new XmlDocument();
xml.LoadXml(s);
foreach (XmlNode node in xml["rss"]["channel"].ChildNodes)
{
if (node.Name.Equals("item") && node["title"].InnerText.StartsWith("VIDEO"))
{
Console.WriteLine(node["title"].InnerText);
}
else
{
node.ParentNode.RemoveChild(node);
}
}
xml.Save("NewXmlDoc.xml");
Console.WriteLine("\nDone...");
Console.Read();
}
}
I have also tried the RemoveAll method, which does not work as well, because it removes all the nodes not satisfying the "VIDEO" condition.
//same code as above, just the else statement is changed
else
{
node.RemoveAll();
}
Could you help me, please?
I find Linq To Xml easier to use
var xDoc = XDocument.Load("http://feeds.bbci.co.uk/news/health/rss.xml");
xDoc.Descendants("item")
.Where(item => !item.Element("title").Value.StartsWith("VIDEO"))
.ToList()
.ForEach(item=>item.Remove());
xDoc.Save("NewXmlDoc.xml");
You can also use XPath
foreach (var item in xDoc.XPathSelectElements("//item[not(starts-with(title,'VIDEO:'))]")
.ToList())
{
item.Remove();
}

looping through items using xPath

I am tryng to loop through an xml doc and I am still getting the first element in the second iteration, not sure what I am missing. Can anyone help? Pretty new with Xpath
string file = HttpContext.Current.Server.MapPath("~/XML/Locations.xml");
Dictionary<string, Location> locationCollection = new Dictionary<string, Location>();
XPathDocument xDocument = new XPathDocument(file);
XPathNavigator xPathNavigator = xDocument.CreateNavigator();
foreach (XPathNavigator node in xPathNavigator.Select("//locations/*"))
{
string value = node.SelectSingleNode("/locations/location/cell").Value;
}
<?xml version="1.0" encoding="utf-8" ?>
<locations>
<location>
<locationName>Glendale</locationName>
<street>3717 San Fernando Road</street>
<city>Glendale</city>
<state>CA</state>
<zipcode>91204</zipcode>
<generalManager>DJ Eldon</generalManager>
<phone>(818) 552‐6246</phone>
<tollFree>(888) 600‐6011</tollFree>
<fax>(818) 552‐6248</fax>
<cell>(347) 834‐2249</cell>
<counterEmail>BUR#Eaglerider.com</counterEmail>
<directEmail>DJ#Eaglerider.com</directEmail>
</location>
<location>
<locationName>Chicago</locationName>
<street>1301 S. Harlem Ave.</street>
<city>Chicago</city>
<state>IL</state>
<zipcode>60402</zipcode>
<generalManager>Dave Schnulle</generalManager>
<phone>(708) 749‐1500</phone>
<tollFree>(888) 966‐1500</tollFree>
<fax>(818) 552‐6248</fax>
<cell>(708) 749‐3800</cell>
<counterEmail>ORD#Eaglerider.com</counterEmail>
<directEmail>Dave#Eaglerider.com</directEmail>
</location>
</locations>
You're effectively ignoring the value of node by using a leading slash to get back to the document root. Try this instead:
// This assumes that there are only location nodes under locations;
// You may want to use //locations/location instead
foreach (XPathNavigator node in xPathNavigator.Select("//locations/*"))
{
string value = node.SelectSingleNode("cell").Value;
// Use value
}
Having said that, is there any reason you're not doing it in a single XPath query?
// Name changed to avoid scrolling :)
foreach (XPathNavigator node in navigator.Select("//locations/location/cell"))
{
string value = node.Value;
// Use value
}
Try the following:
XPathNodeIterator ni = xPathNavigator.Select("//locations/*");
while (ni.MoveNext())
{
string value = ni.Current.Value);
}
Just a quick blurt, hope it helps you.
you should do:
string value = node.SelectSingleNode("./cell").Value;
When you do xPathNavigator.Select("//locations/*")), you're already inside /locations/location so you need to move just one element down the node, cell in your example.

Categories

Resources