In my code, I am downloading an xml file, and because one of the nodes is variable (both name and count of them), I use code like this:
XmlNodeList arrivals = airplanes.SelectNodes("/myXml/flights/*/arrivals");
Now what I need to do, is saving names of the nodes skipped by "*" into an array, or arraylist, something like that. Later I will need to use some foreach to do something with each of the nodes, now saved as strings. I have tried
foreach(* in MyArrayList)
and that doesnt work, I get a number of errors there, assuming I cant use the " * " here.
Each XmlNode in the XmlNodeList has a ParentNode property, you should be able to use that to navigate back up from the arrivals node in the xml to the * node.
The following Linq query should get the names:
var names = arrivals.Cast<XmlNode>().Select(x => x.ParentNode.Name).ToList();
The Cast<XmlNode> is needed because XmlNodeList doesn't implement the generic IEnumerable interface.
Related
I have been trying to deserialize the InnerXML into a class and for some reason the XML keeps changing shape and however many times I try to get the class right it seems to change shape again.
So I have given up and decided to try another method.
Is it possible to retrieve the value of a parameter within the InnerXML manually using c#?
Say for example, my XML innerXML looked like this:
<Timestamp>2014-08-22T21:45:00Z</Timestamp>
<Subscriber>https://www.dogdoza.co.uk</Subscriber>
<Order>
<OrderID>111867</OrderID>
<InvoiceNumber>DOZA-9725410</InvoiceNumber>
<CustomerID>4542</CustomerID>
Is it possible to pull out say the value of Subscriber
If this is possible I can just pull out the values I want manually. Not ideal, but there are only about 10...
I have looked around but not managed to find any code I can get working..
Can anyone please give me any guidance?
Thanks
You can do achieve what you want using LINQ to XML:
XElement myXml = XElement.Load(#"XmlLocationHere");
XElement subscriber = myXml.Descendants("Subscriber").FirstOrDefault();
XElement.Descendants returns a collection of the descendant elements for this document or element, in document order. This method will return an IEnumerable<XElement>, since there might be more than one "Subscriber" element, but in your case, we choose FirstOrDefault, which returns the first occurrence.
Try loading your XML into an XDocument. Then try to use XPathSelectElement to find the specific value you want.
It could be that you need to wrap your inner xml into a root element, because it doesn't accept multiple roots.
Pseudo example:
// set up your xml document
string xml = "<rootelement>" + myInnerXml + "</rootelement>";
XDocument doc = new XDocument();
doc.Parse(xml);
XElement subscriber = doc.XPathSelectElement("/rootelement/Subscriber");
string value = subscriber.Value;
I have a XML structured like this:
<airports>
<airport code="code">
Airport name
<location>Airport location</location>
</airport>
...
</airports>
And I am trying to parse its code and name:
List<string> list = new List<string>();
XmlDocument xDoc = new XmlDocument();
xDoc.Load("file.xml");
foreach (XmlNode node in xDoc.GetElementsByTagName("airport"))
{
list.Add(node.Attributes["code"] + " " + node.Value);
}
But instead of the value I am not getting anything. When debugging, it says, the value of the node in null. Yet, I can see the text in .InnerText. Can you tell me, where is the problem and how can I get the value?
Try replacing node.Value with node.FirstChild.Value.
That should return something like:
"\r\n Airport name\r\n "
Well you could have probably just used innertext, but as Grant Winney alluded to the "value" of the airport node is a child node of type (text) of the airport node.
It seems strange but it was a way of dealing with xml like this
<NodeA>Fred<NodeB>Bloggs</NodeB></NodeA>
ie NodeA has two children, one of type text and another of type elements. Other node types also fit in nicely.
What Grant Winney said will solve your issue. But is there any reason why you're not using LINQ 2 XML instead of XmlDocument?
You can achieve what you're doing very quickly and easily with minimal code:
XDocument.Load("file.xml")
.Root
.Elements("airport")
.Select (s => s.Attribute("code").Value + " " + s.FirstNode)
.ToList<string>();
Ideally though, if you have the opportunity, you should put 'airport name' into it's own element inside <airport>. Like this:
<airports>
<airport code="code">
<name>Airport name</name>
<location>Airport location</location>
</airport>
...
</airports>
The issue is that an XmlElement, which is a specialization of XmlNode where NodeType is Element, has no "value" and thus always returns null.
It has Attributes, and it has Child nodes. XmlElement.InnerText works because it recursively builds the result from the Children and grandchildren, etc (some of which are Text nodes).
Remember that text sections in XML are really just Nodes themselves.
Ideally, the XML would be fixed such that the name was an attribute (or even the sole [Text] node in an element).
I have xml document like this:
<level1>
<level2>
<level3>
<attribute1>...</attribute1>
<attribute2>false</attribute2>
<attribute3>...</attribute3>
</level3>
<level3>
<attribute1>...</attribute1>
<attribute2>true</attribute2>
<attribute3>...</attribute3>
</level3>
</level2>
<level2>
<level3>
<attribute1>...</attribute1>
<attribute2>false</attribute2>
...
...
...
I'm using c#, and I want to go thru all "level3", and for every "level3", i want to read attribute2, and if it says "true", i want to print the corresponding attribute3 (can be "level3" without these attributes).
I keep the xml in XmlDocument.
Then I keep all the "level3" nodes like this:
XmlNodeList xnList = document.SelectNodes(String.Format("/level1/level2/level3"));
(document is the XmlDocument).
But from now on, I don't know exactly how to continue. I tried going thru xnList with for..each, but nothing works fine for me..
How can I do it?
Thanks a lot
Well I'd use LINQ to XML:
var results = from level3 in doc.Descendants("level3")
where (bool) level3.Element("attribute2")
select level3.Element("attribute3").Value;
foreach (string result in results)
{
Console.WriteLine(result);
}
LINQ to XML makes all kinds of things much simpler than the XmlDocument API. Of course, the downside is that it requires .NET 3.5...
(By the way, naming elements attributeN is a bit confusing... one would expect attribute to refer to an actual XML attribute...)
You can use LINQ to XML and reading this is a good start.
You can use an XPath query. This will give you a XmlNodeList that contains all <attribute3> elements that match your requirement:
var list = document.SelectNodes("//level3[attribute2 = 'true']/attribute3");
foreach(XmlNode node in list)
{
Console.WriteLine(node.InnerText);
}
You can split the above xpath query in three parts:
"//level3" queries for all descendant elements named <level3>.
"[attribute2 = 'true']" filters the result from (1) and only keeps the elements where the child element <attribute2> contains the text true.
"/attribute3" takes the <attribute3> childnode of each element in the result of (2).
I have a heavier XML file with lots and lots of tree nodes. I need to pick-up some particular node (for example say Diet), under which there are multiple sections.
ie. Diet node occurs randomly in the XML, so i need to find the node as Diet and get its child elements and save it to DB.
Assume that Diet is not only one line, it has 10-12 entries underneath it (may be i can get its contents using InnerXML, but really can't get line by line nodes)
Make sure you have added a reference to "System.xml.Linq'.
Suck out all the Diet elements:
XElement wholeFile = XElement.Load(#"C:\DietSampleXML.xml");
IEnumerable<XElement> dietElements = wholeFile.Descendants("Diet");
If you set a breakpoint and hover the mouse over "dietElements" and click "Results View", you will see all the Diet elements and their inner xml.
Now iterate through dietElements to add each element and/or children to your database: "foreach (XElement x in dietElements) { ... }"
I tested this with the following xml:
<?xml version="1.0" encoding="utf-8" ?>
<TestElement>
<Diet>
<Name>Atkins</Name>
<Colories>1000</Colories>
</Diet>
<TestElement2>
<Diet>
<Name>Donuts Only</Name>
<Calories>1500</Calories>
</Diet>
</TestElement2>
<TestElement3>
<TestElement4>
<Diet>
<Name>Vegetarian</Name>
<Calories>500</Calories>
</Diet>
</TestElement4>
</TestElement3>
</TestElement>
Depending on the structure of your XML file, you might try loading it into a DataSet (DataSet.ReadXML()) and see what DataTable it puts your Diet nodes into ... if it parses it ok then it is pretty simple to loop through the DataTable and get all your Diet node values.
I wrote a little toy app that opens XML like that, listing all the DataTables in a tree view then showing the table content in a grid. The VS project file for it is here or just an MSI to install it is here, if you want to see how a DataSet parses your XML file.
In XPath, it's just //Diet
To say more, I'd need to know more about your environment.
var doc = XDocument.Load("yourfile.xml");
var nodes = from d in doc.Desendants("Diet")
select d;
foreach(var node in nodes)
{ // do stuff with node
}
The pseudo code below, contains the XPath statement that would get you all elements who have a 'Diet' as parent. Since it produces a XmlNodeList you can walk every node and save it to the DB. For performance i would consider consolidating what you want to save, and then save it, not per line (round trip for every entry is sub-optimal)
XmlNodeList list = xDoc.DocumentElement.SelectNodes("//*[parent::Diet]");
foreach (XmlNode entry in list)
{
DAL.SaveToDatabase(entry);
}
Hope this helps,
I'm writing one of my first C# programs. Here's what I'm trying to do:
Open an XML document
Navigate to a part of the XML tree and select all child elements of type <myType>
For each <myType> element, change an attribute (so <myType id="oldValue"> would become <myType id="newValue">
Write this modified XML document to a file.
I found the XmlDocument.SelectNodes method, which takes an XPath expression as its argument. However, it returns an XmlNodeList. I read a little bit about the difference between an XML node and an XML element, and this seems to explain why there is no XmlNode.SetAttribute method. But is there a way I can use my XPath expression to retrieve a list of XmlElement objects, so that I can loop through this list and set the id attributes for each?
(If there's some other easier way, please do let me know.)
Simply - it doesn't know if you are reading an element or attribute. Quite possibly, all you need is a cast here:
foreach(XmlElement el in doc.SelectNodes(...)) {
el.SetAttribute(...);
}
The SelectNodes returns an XmlNodeList, but the above treats each as an XmlElement.
I am a big fan of System.Xml.Linq.XDocument and the features it provides.
XDocument xDoc = XDocument.Load("FILENAME.xml");
// assuming you types is the parent and mytype is a bunch of nodes underneath
IEnumerable<XElement> elements = xdoc.Element("types").Elements("myType");
foreach (XElement type in elements)
{
// option 1
type.Attribute("id").Value = NEWVALUE;
// option 2
type.SetAttributeValue("id", NEWVALUE);
}
Option 1 or 2 works but I prefer 2 because if the attribute doesn't exist this'll create it.
I'm sitting at my Mac so no .NET for me...
However, I think that you can cast an XmlNode to an XmlElement via an explicit cast.
You should be able to cast the XmlElement to an XmlNode then and get it's children Nodes using something like XmlNode.ChildNodes.