Printing array from XML - C# - c#

Im in the process of working with XML data and what Im trying to do is;
User selects name value from a dropdownlist
for each node with above name, value is added to a list
list then converted to string array
**printing the array to test, no output is given
string array converted to int array (to allow computation on
array)
**Print provides no output again
Here is the code I've produced, I get no errors just no output so I cant tell if the arrays have been populated.
if (DropDownList1.SelectedItem.Text=="Cabin")
{
//Load XML document
XmlDocument xml = new XmlDocument();
xml.LoadXml(Server.MapPath("~/Upload/" + FileUpload1.FileName));
// xnList = nodes -> rows with Cabin
XmlNodeList xnList = xml.SelectNodes("/root/row[#name='Cabin']");
//create a string list
List<string> strvalues = new List<string>();
//populate list with values # node Cabin
foreach (XmlNode xn in xnList)
{
strvalues.Add(xn["value"].InnerText);
}
//convert list to array
strvalues.ToArray();
for (int i = 0; i < 2; i++)
{
//print array
Console.WriteLine(strvalues[i]);
}
//convert string array to int array for data manipulation
int[] values = strvalues.Select(x => int.Parse(x)).ToArray();
for (int i = 0; i < values.Length; i++)
{
//print array
Console.WriteLine(values[i]);
}
}
Here is a section of the XML file also;
<root>
<row>
<var name="Name" value="Garcia" />
<var name=" Surname" value=" Jose" />
<var name=" Country" value=" Cuba" />
<var name=" Job" value="Software Developer" />
<var name=" Cabin" value="345" />
</row>
<row>
<var name="Name" value="Lenon" />
<var name=" Surname" value="Tim" />
<var name=" Country" value="USA" />
<var name=" Job" value="SoftwareDeveloper" />
<var name=" Cabin" value="444" />
</row>
</root>

Your XPath expression is wrong, as the <row> element has no attribute named name (and it's also missing the space in the actual name attribute value).
Did you mean /root/row/var[#name=' Cabin'] or possibly /root/row[var/#name=' Cabin'] (if you wanted the <row> element; though looking at your code that's not the case).
Additionally, the line strvalues.ToArray() does nothing, because you don't assign the result to anything. ToArray doesn't modify the list, it returns a new array with the same contents as the list. There is no real reason to convert the list to an array in this code either.

The attribute value for name in the sample XML is " Cabin" - note the leading space.
The attribute value tested against name in the XPath query is "Cabin" - no leading space.
So you never find any nodes.
The generic answer to looking at this would be to set a breakpoint immediately after you create the node list and to check the content at that point.
As a further aside, Linq to XML is much more pleasant to work with if that's available to you.

Do you have a console window associated with your solution? I think you're using a UI and there is no console window (so Console.WriteLine won't output anything). You can attach one, but for debug I can suggest Debug.WriteLine:
http://www.dotnetperls.com/debug-write
Also, you should check if your arrays being populated in debug mode.

Related

Query XML stored in C# string

I have to read some values from XML,below is my sample XML
<?xml version="1.0" encoding="utf-16"?>
<ParentNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ChildNode>
<GrandChild Name="title" Value="Mr" />
<GrandChild Name="Name" Value="Test" />
<GrandChild Name="Age" Value="25" />
<GrandChild Name="Gender" Value="Male" />
</ChildNode>
</ParentNode>
I have to read values of Name and Age nodes, this is how I am doing
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(myXMLstring);
var nodes = xmlDoc.SelectNodes("/ParentNode/ChildNode");
foreach (XmlNode childrenNode in nodes)
{
}
but this code is running for only once, I tried this inside loop,buts its not running
var gchild= childrenNode.SelectNodes("/GrandChild");
foreach (XmlNode namevalue in gchild)
{
}
How can I get the values of Name and Age node?
Your XML contains only a single ChildNode so the XPATH expression /ParentNode/ChildNode will return only a single result. If you wanted to iterate over the grandchildren you should use /ParentNode/ChildNode/GrandChild or //GrandChild, eg:
var nodes = xmlDoc.SelectNodes("/ParentNode/ChildNode/GrandChild");
The result will be the same.
A single slash at the start of an XPath expression means that the path starts from the root, so /GrandChild returns nothing because there is no GrandChild note at the root level. A double slash // means wherever in the hierarchy, so //GrandChild will return all GrandChild nodes in the file
SelectNodes uses XPath expressions. In XPath, if the expression stars with / it'll start selecting relative to root.
Just use a relative xpath expression. In your case:
var gchild = childrenNode.SelectNodes("./GrandChild");
Or the equivalent:
var gchild = childrenNode.SelectNodes("GrandChild");
Or, if you only aim to iterate over those GrandChild elements, there's no reason to select the ChildNode first, you could iterate directly:
var nodes = xmlDoc.SelectNodes("/ParentNode/ChildNode/GrandChild");

insert element after selected node

Am trying to add a new element called entity after the last entity but it keeps adding it inside the selected entity. To understand better this is my xml sample.
<Root>
<Class Name="ECMInstruction" Style="Top">
<Entity Id="1" Name="DocumentInformation" />
<Entity Id="2" Name="CustomerInformation" />
<Property Id="1" Name="DocumentTitle">
</Property>
<Property Id="2" Name="DateCreated">
<Lists>
<ListName>ws_Users</ListName>
<ListName>dfdfdfd</ListName>
</Lists>
</Property>
<Property Id="3" Name="Deadline">
</Property>
</Class>
</Root>
This is how it looks like after is inserted. I've tried using insertAfter but it gives me error.
<Entity Id="1" Name="DocumentInformation">
<Entity Id="2" Name="sds" />
</Entity>
The code:
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load("sample.xml");
XmlNodeList cnode = xmldoc.DocumentElement.SelectNodes("//Class[#Name='" + CurrentClass + "']/Entity");
foreach (XmlNode c in cnode)
{
int value = 0;
String entitycount = cnode.Count.ToString();
int.TryParse(entitycount, out value);
value = value + 1;
XmlElement root = xmldoc.CreateElement("Entity");
root.SetAttribute("Id", value.ToString());
root.SetAttribute("Name", EntityNametxt.Text);
c.AppendChild(root);
xmldoc.Save("sample.xml");
}
"I've tried using insertAfter but it gives me error. "
As per documentation, InsertAfter() should be called on parent node of referenced XmlNode (the 2nd argument of the method), otherwise ArgumentException will be thrown :
//instead of this : c.AppendChild(root);
//..you could do as follow :
c.ParentNode.InsertAfter(root, c);

C# XmlDocument: Nested objects

I have a XML file that has this following format:
<datasources>
<datasource name="...">
<column caption="..." name="..."> ... </column>
<column caption="..." name="..."> ... </column>
</datasource>
<datasource name="...">
<column caption="..." name="..."> ... </column>
<column caption="..." name="..."> ... </column>
</datasource>
</datasources>
...
and I want to get a caption-name map for columns that meets specific criteria (that is, has an expected caption and is a column of an expected datasource).
Currently I have this code but I'm not confident of it.
XmlReader reader = XmlReader.Create(new StringReader(content));
while (reader.ReadToFollowing("datasource"))
{
reader.MoveToAttribute("name");
if (reader.Value == dsName)
{
while (reader.ReadToFollowing("column"))
{
reader.MoveToAttribute("caption");
if (captions.Contains(reader.Value))
{
String cap = reader.Value;
reader.MoveToAttribute("name");
local_caps.Add(new KeyValuePair<String, String>(reader.Value, cap));
}
}
}
}
My concern is that, would the inner while loop (looking for all columns) read all the way towards the end of the file and hence go out of its supposed scope (within its datasource)? If so, how should I avoid it? Thanks a lot!
This is ideal job for XPath:
doc.Load("file.xml");
var nodes = doc.SelectNodes("/datasources/datasource[#name='DS1']/column[#caption='C1']");
Of course, to get your map, you have to improve this sample a little:
string expectedDatasource = "DS1";
string expectedCaption = "C1";
string xpath = string.Format("/datasources/datasource[#name='{0}']/column[#caption='{1}']",
expectedDatasource, expectedCaption)
var local_caps = doc.SelectNodes(xpath)
.Cast<XmlElement>()
.ToDictionary(x => x.GetAttribute("name"),
x => x.GetAttribute("caption"));

Reading <property> tag

I was asked today to look at a new project - reading in some XML and doing some analysis. I know a little C#. I have gotten this far with this code that so far works. I get the 4 node lists successfully. I have a couple problems. First I am not sure how to access what is in the tag on any of the nodes in any of the lists. Second, I'd prefer to be able to use LINQ queries but XmlNodeList doesn't seem to support that syntax. In the sample XML below, I'd like to be able to get all the vdisks that belong to a particular IO Group or mdisk as determined by io_group_name or mdisk_grp_name property. Most of what I looked at gave examples for accessing the [Attribute] list and searches all used properties/atttributes interchanged.
What I tried is also below, it gave a null value exception. The Attributes list only has one attribute. I can't find any examples to do what I want and it isn't clear from inspecting the node in the debugger what I need to access to do what I want.
//this works
XmlTextReader reader = new XmlTextReader(_InputFile);
XmlDocument doc = new XmlDocument();
doc.Load(reader);
XmlNodeList clusterlist = doc.SelectNodes("//object[#type='cluster']");
XmlNodeList controllerlist = doc.SelectNodes("//object[#type='controller']");
XmlNodeList mdisklist = doc.SelectNodes("//object[#type='mdisk']");
XmlNodeList vdisklist = doc.SelectNodes("//object[#type='vdisk']");
// this did not work - got null value exception
foreach (XmlNode vdisknode in vdisklist)
{
string str = vdisknode.Attributes["mdisk_grp_name"].Value;
}
A sample of the XML:
<object type="vdisk">
<property name="id" value="0" />
<property name="name" value="nim01_vd06_gmt" />
<property name="IO_group_id" value="0" />
<property name="IO_group_name" value="ossvc06_iogrp0" />
<property name="status" value="online" />
<property name="mdisk_grp_id" value="0" />
<property name="mdisk_grp_name" value="T1_OSIBM06_MDG1" />
<property name="capacity" value="644245094400" />
<property name="type" value="striped" />
</object>
object node has only one attribute: type
string type = vdiskNode.Attributes["type"].Value;
property node has two attributes: name and value:
string name = propertyNode.Attributes["name"].Value;
string value = propertyNode.Attributes["value"].Value;
What you need I deem is to extend the XPath query:
"//object[#type='vdisk']/property[#name='mdisk_grp_name']/#value"
Or use LINQ to XML:
from obj in doc.Load(xml).Root.Elements("object")
where (string)obj.Attribute("type") == "vdisk"
from prop in obj.Elements("property")
//where (string)prop.Attribute("name") == name
select prop.Value

Linq to XML: Returning attributes from Children of the same name and Parents of the same name

So, the noobie question of the day...
I have an XML file with the following structure:
<result>
<rowSet name="name">
<row name="name1" />
<row name="name2" />
<row name="name3" />
</rowSet>
<rowSet name="type">
<row type="type1" />
<row type="type2" />
<row type="type3" />
</rowSet>
etc..
</result>
I've been trying, and rather unsuccessfully, to get the row attributes from a single rowSet based off the rowSet:attribute name. In other words, I'm trying to pull the attributes from the "row"s which are only contained under a rowSet:Attribute of a specific name.
I've tried using:
var rowSet = from rs in xmlDoc.Descendants("result")
where rs.Descendants("rowset").Attributes("name").Any(a => a.Value == "name")
select new
{
row = from r in xmlDoc.Descendants("row")
select new
{
skillID = r.Attribute("name"),
}
};
However, this doesn't give more than a single result. Been rather frustrating, and yet again...after trying 15 different suggestions I have been unable to remember the original code for the reader.
An answer was given:
var rowNames = (from i in xmlDoc.Descendants("rowSet")
where (string)i.Attribute("name") == "name"
select i.Descendants("row")) // Had to move this last ) to...
.Select(r => (string)r.Attribute("name")); // Here in order to get it to work
I believe this worked but I'm too noobish to find out how to iterate through it, even though I can get to the data through the "Result View" of the debugger when the program is running.
Ok, so I'm able to get that query to run right (by moving a ")" first) and when I start stepping through the code, the rowNames has values in it. However, I've been very unsuccessful in displaying the information in any way. I'm playing with this in a console app, fyi.
So that's the first problem.
The second is, if I start making my XML a bit more complicated, by adding multiple attributes to the row elements, like:
<result>
<rowSet name="name">
<row fName="Ted" lName = "Happy" />
<row name="Billy" lName = "Maddison"/>
<row name="John" lName = "Doe"/>
</rowSet>
<rowSet name="type">
<row type="type1" type2 ="Hero"/>
<row type="type2" type2 ="Villain" />
<row type="type3" type2 ="Neutral"/>
</rowSet>
</result>
How then do I retrieve all attributes in an element?
This seems so basic, which is why I'm feeling quite retarded at the moment.
You can get the names alone easily enough:
var rowNames = from i in xmlDoc.Descendants("rowSet")
where (string)i.Attribute("name") == "name"
from r in i.Descendants("row")
select (string)r.Attribute("name");
Just adjust the strings to get the other. (You might throw this in a method and use parameters, for example.)
This is a bit guesswork, but do you mean:
from rs in xmlDoc.Descendants("result").Descendants("rowSet")
where (string)rs.Attribute("name") == "name"
from r in rs.Descendants("row")
select new { skillID = (string)r.Attribute("type") }

Categories