I am trying to read and store data from an xml file. I have been reading about various methods to read the data such as XmlReader, XmlTextReader, LinQ, etc.
My XML file is
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<circuit name="local">
<Device>device1</Device>
<Point>point1></Point>
</circuit>
<circuit name ="remote">
<Device>device2</Device>
<Point>point2</Point>
</circuit>
</configuration>
I am trying to extract Device and Point set so I can pass those along to be used in a database query. I used this code and the foreach loop to verify the contents, but it only gets the first set.
XDocument msrDoc = XDocument.Load("BNOC MSR.config");
var data = from item in msrDoc.Descendants("circuit")
select new
{
device = item.Element("Device").Value,
point = item.Element("Point").Value
};
foreach (var p in data)
Console.WriteLine(p.ToString());
I have also tried this, but my arrays were all null
String[] deviceList = new String[1];
String[] pointList = new String[1];
int n = 0;
XmlDocument msrDoc = new XmlDocument();
msrDoc.Load("BNOC MSR.config");
var itemNodes = msrDoc.SelectNodes("circuit");
foreach (XmlNode node in itemNodes)
{
var circuit = node.SelectNodes("circuit");
foreach (XmlNode cir in circuit)
{
deviceList[n] = cir.SelectSingleNode("Device").InnerText;
pointList[n] = cir.SelectSingleNode("Point").InnerText;
}
}
Any help would be greatly appreciated.
Are you sure you don't want to use the built-in Properties.Settings for this?
Circuit local = Properties.Settings.Default.localCircuit;
Circuit remote = Properties.Settings.Default.remoteCircuit;
https://learn.microsoft.com/en-us/dotnet/framework/winforms/advanced/using-application-settings-and-user-settings
I believe there is something wrong with the way you are testing the result. The code:
void Main()
{
var fileLocation = #"C:\BrianTemp\input.txt";
var xml = File.ReadAllText(fileLocation);
XDocument msrDoc = XDocument.Load(fileLocation);
var data = from item in msrDoc.Descendants("circuit")
select new
{
device = item.Element("Device").Value,
point = item.Element("Point").Value
};
foreach (var p in data)
{
//It is best practice to use statement blocks {} to prevent silly errors.
//Sometimes you want to execute multiple statements, especially as code changes later
Console.WriteLine($"{p}");
}
}
Produces the expected output:
{ device = device1, point = point1 }
{ device = device2, point = point2 }
You said:
I used this code and the foreach loop to verify the contents, but it
only gets the first set.
As you can see the code produces 2 results as it should.
Note: I corrected the XML file to remove the extra >
<Point>point1></Point><==
I see two problems in your code (and I only tried the second method you posted):
Your string arrays are too small, change to:
String[] deviceList = new String[1];
String[] pointList = new String[1];
The line var itemNodes = msrDoc.SelectNodes("circuit"); should be
var itemNodes = msrDoc.SelectNodes("configuration");
Related
public string[] UnpackXML(string xml_string)
{
string response = xml_string;
string[] return_values = new string[6];
XmlDocument xml = new XmlDocument();
xml.LoadXml(response);
return_values[0] = xml.GetElementsByTagName("meas name=\"mt1\"")[0].InnerText.ToString();
return_values[1] = xml.GetElementsByTagName("meas name=\"mt2\"")[0].InnerText.ToString();
return_values[2] = xml.GetElementsByTagName("meas name=\"mt3\"")[0].InnerText.ToString();
return_values[3] = xml.GetElementsByTagName("meas name=\"mt4\"")[0].InnerText.ToString();
return_values[4] = xml.GetElementsByTagName("meas name=\"mt5\"")[0].InnerText.ToString();
return_values[5] = xml.GetElementsByTagName("meas name=\"mt6\"")[0].InnerText.ToString();
return return_values;
}
When running the above code, it fails with the given error code:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
In the first line where I try to give return_values[0] a new value:
return_values[0] = xml.GetElementsByTagName("meas name=\"mt1\"")[0].InnerText.ToString();
The input to the UnpackXML is just an API response given as an XML string. The XML document has the following format:
<response location='location1'>
<meas name='mt1'>14</meas>
<meas name='mt2'>23</meas>
<meas name='mt3'>65</meas>
<meas name='mt4'>31</meas>
<meas name='mt6'>32</meas>
</response>
Any ideas on how to fix this? I basically want to append specific fields from the XML file to an array. The XML attribute "name" exists with each line and has different values for the attribute. Is there a way to directly access the attribute values by giving the correct name = "nameValue", without looping through attributes and checking each specific value?
The .GetElementsByTagName() is not suitable for your scenario since you are querying with XPath but not only with tag name.
Instead, you should use .SelectNodes() or .SelectSingleNode().
return_values[0] = xml.SelectNodes("//meas[#name=\"mt1\"]")[0]?.InnerText.ToString();
return_values[1] = xml.SelectNodes("//meas[#name=\"mt2\"]")[0]?.InnerText.ToString();
return_values[2] = xml.SelectNodes("//meas[#name=\"mt3\"]")[0]?.InnerText.ToString();
return_values[3] = xml.SelectNodes("//meas[#name=\"mt4\"]")[0]?.InnerText.ToString();
return_values[4] = xml.SelectNodes("//meas[#name=\"mt5\"]")[0]?.InnerText.ToString();
return_values[5] = xml.SelectNodes("//meas[#name=\"mt6\"]")[0]?.InnerText.ToString();
Or
xml.SelectSingleNode("//meas[#name=\"mt1\"]")?.InnerText.ToString();
Demo # .NET Fiddle
Also work with the null-conditional operator ?. to handle the returned node is possible null to prevent Null Reference Exception (NRE).
For your further question for dynamic way instead of hard code (assume 6 names you need to write the statements for 6 times), you may work with loop and LINQ to get the node's InnerText by matching the attribute value as below:
string[] return_values = new string[6];
string[] names = new string[6] { "mt1", "mt2", "mt3", "mt4", "mt5", "mt6" };
XmlDocument xml = new XmlDocument();
xml.LoadXml(response);
for (int i = 0; i < return_values.Count(); i++)
{
var node = xml.DocumentElement
.ChildNodes
.Cast<XmlNode>()
.FirstOrDefault(x => x.Attributes["name"].Value == names[i]);
return_values[i] = node?.InnerText.ToString();
}
Or
for (int i = 0; i < return_values.Count(); i++)
{
return_values[i] = xml.SelectSingleNode($"//meas[#name=\"{names[i]}\"]")?.InnerText.ToString();
}
Demo # .NET Fiddle
In case you have multiple entries for the response, it's recommended to load the return_values dynamically instead of writing every possible entry manually:
public string[] UnpackXML(string xml_string)
{
XmlDocument xml = new XmlDocument();
xml.LoadXml(xml_string);
var response_childrens = xml.SelectSingleNode("response").ChildNodes;
string[] return_values = new string[response_childrens.Count];
for (int i = 0; i < response_childrens.Count; i++)
{
return_values[i] = response_childrens[i].InnerText;
}
return return_values;
}
I am putting together a map of all the inline styles on elements in a large project. I would like to show the line number where they are located similar the example below.
Is it possible to get the line number of an element in AngleSharp?
foreach (var file in allFiles)
{
string source = File.ReadAllText(file.FullName);
var parser = new HtmlParser();
var doc = parser.ParseDocument(source);
var items = doc.QuerySelectorAll("*[style]");
sb.AppendLine($"{file.Name} - inline styles({items.Count()})");
foreach (var item in items)
{
sb.AppendLine($"\t\tstyle (**{item.LineNumber}**): {item.GetAttribute("style")}");
}
}
Yes this is possible.
Quick example:
var parser = new HtmlParser(new HtmlParserOptions
{
IsKeepingSourceReferences = true,
});
var document = parser.ParseDocument("<html><head></head><body>&foo</body></html>");
document.QuerySelector("body").SourceReference?.Position.Dump();
The output looks as follows:
The important part is to use the IsKeepingSourceReferences option, as this will allow you to use SourceReference. Some (by the parser / spec inserted) elements may not have a source reference, so keep in mind that this may be null.
I have an problematic piece of code and it's something really peculiar and nothing I've ever experienced before!
I'm calling a Sharepoint SOAP function and the results are being returned absolutely fine., many XML records of data are being retruned.
Now I have tried to convert the results into an XmlDocument, which I then use to load into a DataSet.
However when it gets inserted into the DataSet it's only inserting 1 record, which happens to be the very first record of the Xml.
The problematic code is the below:
Lists list = new Lists();
list.Url = URL + "_vti_bin/Lists.asmx";
list.UseDefaultCredentials = true;
//Gets the entire Lists attached to the Sharepoint Site
XmlNode Results = list.GetListCollection();
//Writes the entire results into an XmlDocument.
doc.AppendChild(doc.ImportNode(Results, true));
using (StringReader xmlSR = new StringReader(doc.InnerXml))
{
ds.ReadXml(xmlSR, XmlReadMode.Auto);
}
The Xml from 'doc.InnerXml' is all valid and is pastable into Xml Notepad 2007, so i'm a bit at a lost.
I hope someone can shed some light onto this, be much appreciated
The following example works for me:
Lists list = new Lists(); //SharePoint Lists SOAP service
//Perform request
XmlNode result = list.GetListCollection();
//Process result
var ds = new DataSet("ListsResults");
using (var reader = new StringReader(result.OuterXml))
{
ds.ReadXml(reader, XmlReadMode.Auto);
}
//print List Titles
foreach (DataRow row in ds.Tables[0].Rows)
{
Console.WriteLine(row["Title"]);
}
Another common approach is to utilize LINQ to XML:
Lists list = new Lists(); //SharePoint Lists SOAP service
//Perform request
XmlNode result = list.GetListCollection();
var docResult = XDocument.Parse(result.OuterXml);
XNamespace s = "http://schemas.microsoft.com/sharepoint/soap/";
var listEntries = from le in docResult.Descendants(s + "List")
select new
{
Title = le.Attribute("Title").Value
};
foreach (var e in listEntries)
{
Console.WriteLine(e.Title);
}
I am using Umbraco and I would like to add two variables together that will display articles in both children.
var nodes = Model.NodeById(1195).Children();
var nodes2 = Model.NodeById(1201).Children();
var test = Model.NodesById(nodes, nodes2);
It's not working and throwing an error. Is there another way to do this?
I found this on the forum board, but It doesn't seem to work for me.
link: http://our.umbraco.org/forum/developers/razor/47078-how-to-merger-DynamicNode?p=0#comment168589
Something like this perhaps?
DynamicNodeList nodes = Model.NodeById(1195).Children();
DynamicNodeList nodes2 = Model.NodeById(1201).Children();
var allNodes = nodes.Concat(nodes2);
A bit primitive (but without knowing more of the context) something like this should work:
string parentIds = "1195,1201";
string[] parentArray = parentIds.Split(',');
DynamicNodeList allNodes = new DynamicNodeList();
foreach (var x in parentArray);
foreach (var y in Library.NodeById(x).Children()) {
var thisNode = Library.NodeById(y);
if (thisNode.Id != 0) {
allNodes.Add(thisNode);
}
}
}
I'm having a problem with my XML document.
I want my program to find all values of the items in my XML file, but only if the handlingType is of a certain character bunch.
Code (C#) :
string path = "//files//handling.meta";
var doc = XDocument.Load(path);
var items = doc.Descendants("HandlingData").Elements("Item");
var query = from i in items
select new
{
HandlingName = (string)i.Element("handlingName"),
HandlingType = (string)i.Element("HandlingType"),
Mass = (decimal?)i.Element("fMass")
};
foreach (var HandlingType in items)
{
if (HandlingType.ToString() == "HANDLING_TYPE_FLYING")
{
MessageBox.Show(HandlingType.ToString());
}
}
The above code demonstraights a short version of what I want to happen, but fails to find this handlingType (does not show the messageBox)
Here's the XML :
<CHandlingDataMgr>
<HandlingData>
<Item type="CHandlingData">
<handlingName>Plane</handlingName>
<fMass value="380000.000000"/>
<handlingType>HANDLING_TYPE_FLYING</handlingType>
</Item>
<Item type="CHandlingData">
<handlingName>Car1</handlingName>
<fMass value="150000.000000"/>
<handlingType>HANDLING_TYPE_DRIVING</handlingType>
</Item>
</HandlingData>
</CHandlingDataMgr>
I would like the output to show the handlingName if it contains a certain HandlingType
For e.g.
if (handlingType == "HANDLING_TYPE_FLYING")
{
messageBox.Show(this.HandlingName);
}
My problem in short : Program does not find item's handling type, it does find the tag but when asked to display, returns empty/shows as nothing.
Edit: Also in the XML handling_type_flying contains extra elements such as thrust that cannot be found in each item (such as car), I would like the program to also find these elements. (this is a second problem I'm facing, maybe should ask 2nd ques?)
Several things that need fixing.
you are not using your query in your foreach loop. foreach (var item in query)
Your element has an upercase "H" but should be lowercase "handlingType". HandlingType = (string)i.Element("handlingType"),
You are not pulling the Attribute value of your fMass element.Mass = i.Element("fMass").Attribute("value").Value
Once you adjust your Query in your foreach loop you then need to adjust the loop to account for looping over your newly made object.
NOTE that I removed (decimal) from Mass = i.Element("fMass").Attribute("value").Value
here is the code with all the fixes.
class Program
{
static void Main()
{
const string path = "//files//handling.meta";
var doc = XDocument.Load(path);
var items = doc.Descendants("HandlingData").Elements("Item");
var query = from i in items
select new
{
HandlingName = (string)i.Element("handlingName"),
HandlingType = (string)i.Element("handlingType"),
Mass = i.Element("fMass").Attribute("value").Value
};
foreach (var item in query)
{
if (item.HandlingType == "HANDLING_TYPE_FLYING")
{
//Remove messagebox if consoleapp
MessageBox.Show(item.HandlingType);
MessageBox.Show(item.HandlingName);
Console.WriteLine(item.HandlingType);
Console.WriteLine(item.HandlingName);
}
}
}
}
I would recommend looking into serializing your xml to an object.
If you look at http://msdn.microsoft.com/en-us/library/system.xml.linq.xelement(v=vs.110).aspx the ToString() method doesn't return the name of the tag, but the indented XML.
You should instead be using the Value property. Also you should use .equals("...") instead of ==
if (handlingType.Value.equals("HANDLING_TYPE_FLYING")
{
messageBox.Show(this.handlingname);
}