I'm fairly new to XML files and I am trying to read from a XML and input some values from a XML and store them in a Combobox. I have looked over many examples and have not found one close enough to figure this out. I have managed read from the XML file and input the data into the Combobox, but for some reason everything gets added to one line.
Here is what the XML file looks like.
<menus>
<addMaterialForm>
<filamentType>
<Type>ABS</Type>
<Type>PETG</Type>
<Type>PLA</Type>
</filamentType>
</addMaterialForm>
</menus>
Here is my C# code:
private void searchButton_Click(object sender, EventArgs e)
{
XmlDocument doc = new XmlDocument();
doc.Load(Globals.xmlFilePath);
foreach (XmlNode node in doc.DocumentElement)
{
foreach (XmlNode child in node.ChildNodes)
{
filamentBox.Items.Add(child.InnerText);
}
}
}
For some reason all of the inner text is added together and this is the result I get.
result
I'm really scratching my head here and any help would really be appreciated.
Thanks!
You are retrieving all type values at once from "filamentType" element, you need to go one more level down.
Another approach would be to use XDocument.Descendants to retrieve all values of particular element.
ComboBox has DataSource property. Retrieve collection of types from xml file and set this collection to the DataSource property.
var document = XDocument.Load(Globals.xmlFilePath);
var types = document.Descendants("Type").Select(element => element.Value).ToList();
filamentBox.DataSource = types;
For "one liner" fans :)
filamentBox.DataSource = XDocument.Load(Globals.xmlFilePath)
.Descendants("Type")
.Select(element => element.Value)
.ToList();
Well you havent used an XPath to reach the right Parent (filamentType) where the required items exists. Hence Inner text gives you all Node values.
Use xpath like:
//menus/addMaterialForm/filamentType
to reach the right Parent XmlNode.
Then loop through all children and add to combobox.
Related
I am looking for a way to query CIM XML files with LINQ, in order to speed up my analysis of data and verify the exports from a database containing the elements of electrical network.
Since I am a newbie in C#, I guessed that it will be easy to load the CIM XML in console application and based on tag values filter some elements. However, all tutorials that I found so far are straight-forward where elements are named like "Student", "Purchase", etc.
My elements are named like "cim:LoadBreakSwitch", where "cim" is actual address defined in root node.
This way, when I try to select all elements named like "cim:LoadBreakSwitch", I get an exception thrown on Run Time because the name of a element cannot contain colon.
Example of element that I want to select from the CIM XML file:
<cim:LoadBreak rdf:ID="101">
<cim:ConductingEquipment.phases>A</cim:ConductingEquipment.phases>
<cim:IdentifiedObject.description>PoleMounted</cim:IdentifiedObject.description>
</cim:LoadBreak>
When I print in the console the names of all elements with
IEnumerable<XElement> elements = xmlDoc.Elements();
foreach (var item in elements)
{
Console.WriteLine(item.Name);
}
I get something like
"{http://[address from the root node]}LoadBreak".
I don't know if there is possibility to do it, but I am just curious did anyone who is experienced developer had need to do something similar.
Your XML is missing a root element with namespace declarations.
Here is a conceptual example for you. It shows how to handle namespaces and query XML with them.
c#
void Main()
{
XDocument xdoc = XDocument.Parse(#"<rdf:RDF xmlns:cim='http://iec.ch/TC57/2008/CIM-schema-cim13#'
xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
<cim:LoadBreak rdf:ID='101'>
<cim:ConductingEquipment.phases>A</cim:ConductingEquipment.phases>
<cim:IdentifiedObject.description>PoleMounted</cim:IdentifiedObject.description>
</cim:LoadBreak>
</rdf:RDF>");
XNamespace cim = xdoc.Root.GetNamespaceOfPrefix("cim");
XNamespace rdf = xdoc.Root.GetNamespaceOfPrefix("rdf");
foreach (XElement xelem in xdoc.Descendants(cim + "LoadBreak").Elements())
{
Console.WriteLine("{0}: {1}"
, xelem.Name.LocalName
, xelem.Value);
}
}
Output
ConductingEquipment.phases: A
IdentifiedObject.description: PoleMounted
I've been playing with LINQ in VB.Net and some other things in an attempt to delete XML nodes based on attribute values. Basically, if any node in my XML documents has an attribute of a particular value, "cats" for example, I want to delete it.
The catch is I won't really know exactly what the XML structures will look like, so I can't give a path. Also, I know some of the attributes that may contain "cats", but I don't want to hard code them if possible.
So, in other words, I don't have a set XML structure, and I want to delete ANY node that has "cats" as an attribute value, like Caption = "cats" or Title = "cats", anywhere in the node. If it has "cats", nuke it.
Is this at all possible? Or do I just need to give up on this project?
BTW, I'm trying to write the solution in VB.Net, but I am quite capable of reading and converting C# if someone happens to know how to accomplish this but can only give C# code.
Thanks a ton for any help!
You can do this using:
XDocument.Descendants() to iterate through all elements in your document.
XElement.Attributes() to loop through all attributes of an element, to see if any have a value of "cats".
Extensions.Remove() to remove all elements that have an attribute value that matches.
In c# this becomes:
var doc = XDocument.Parse(xmlString);
var attributeValue = "cats";
doc.Descendants().Where(e => e.Attributes().Any(a => (string)a == attributeValue)).Remove();
And in VB.NET:
Dim doc = XDocument.Parse(xmlString)
Dim attributeValue = "cats"
doc.Descendants().Where(Function(e) e.Attributes().Any(Function(a) CStr(a) = attributeValue)).Remove()
Example fiddle.
I have XElement object which is my XML tree read from XML file. Now I want to check all the nodes in this tree to get first attribute name and value. Is there any simple way to go through all of the nodes (from root till leaves)? My XML file has got very many different and strange nodes - that's why it's harder to solve this issue. I thought about writing some recursion, but hope it's another way to solve that easier.
Maybe take a look to Xpath. an XPath like this //*[#id=42] could do the job.
It means get all nodes which have an attribute "id" of value 42.
You can do just //* which gonna returns all nodes in a tree.
Xpath :
http://msdn.microsoft.com/en-gb/library/ms950786.aspx
Syntax :
http://msdn.microsoft.com/en-us/library/ms256471.aspx
You can get all children elements using XElement.Elements().
Here's some code using recursion to get all elements of each level:
void GetElements(XElement element){
var elements = element.Elements();
foreach(Element e in elements){
//some stuff here
if(e.Elements() != null)
GetElements(e);
}
}
Given the following XML document for example:
<?xml version="1.0"?>
<UrdaObject>
<Date>
<Year>2011</Year>
<Month>5</Month>
<Day>18</Day>
<Hours>8</Hours>
<Minutes>47</Minutes>
<Seconds>36</Seconds>
</Date>
<random_value>24</random_value>
</UrdaObject>
And the understanding the child::node() - Selects all child nodes of the current node how would I create an XPath (starting from the root) that would select all child nodes EXCEPT text, comments, and other things that are NOT elements. For example, when using this code to create a tree view in WPF:
// x is some XmlDocument, xmlTree is my WPF TreeView
XmlDataProvider provider = new XmlDataProvider();
provider.Document = x;
Binding binding = new Binding();
binding.Source = provider;
binding.XPath = "child::node()";
xmlTree.SetBinding(TreeView.ItemsSourceProperty, binding);
How would I go about creating my XPath statement so I build a treeview with nodes going all the way down and stopping before the raw text? For example it would generate a view of:
UrdaObject
Date
Year
...
Instead of...
UrdaObject
Date
Year
2011 (Don't want this!)
...
The sample XML files is just for me to explain my situation. The expression should be able to navigate any valid XML file and pull the elements, but not the individual text.
How did we fix this? I had switched all references of child::node() to child::*. However, I had NOT corrected one line in my XAML, which was pulling child::node(). Correcting this line made the application behave correctly... and made me feel silly.
child::node() finds all child nodes. child::* finds all element nodes.
it's as simple as *.
(that gets immediate children, however; if you want all descendant elements, it would be descendant::*)
child::* will exclude text nodes and leave only element nodes
child::text() will include only text nodes
child::node() will include both element and text nodes
http://www.w3.org/TR/xpath/#location-paths
Not sure if this is what you want but could it be done this way?
var doc =XDocument.Parse(#"
<UrdaObject>
<Date>
<Year>2011</Year>
<Month>5</Month>
<Day>18</Day>
<Hours>8</Hours>
<Minutes>47</Minutes>
<Seconds>36</Seconds>
</Date>
<random_value>24</random_value>
</UrdaObject>
");
var query = from s in doc.Descendants()
select s.Name;
foreach (var name in query)
{
Console.WriteLine(name);
}
I have the following XML LINQ query from my XDocument.
var totals = (from x in MyDocument.Descendants("TOTALS") select x).FirstOrDefault();
Once I have found my totals node I need to add some elements to that node and push that change to the XDocument.
So just make the change to the returned node... unless you clone it, it will still be part of the document.
Btw, your query expression isn't adding anything - simpler code would be:
var totals = MyDocument.Descendants("TOTALS").FirstOrDefault();
you can use AddAfterSelf() to add new nodes against totals. Those changes will automatically get attached to the main XDocument, since totals is referencing an XElement inside the document.
totals.Add(new XElement("NewNode", "New node value"));