I have an XML which follow the following structure:
<model uir="model1">
<profile>
<profiles>
<timeseries>
<value double="24.7" index="2011-01-01 00:00" />
<value double="24.5" index="2011-01-02 00:00" />
<value index="2011-04-23 00:00" /> //Here for some reason i must have double=null
<value index="2011-04-24 00:00" />
<value double="24.7" index="2011-01-01 00:00" />
<value double="24.5" index="2011-01-02 00:00" />
</timeseries>
</profile>
</profiles>
</model>
<model uir="model2">
<profile>
<profiles>
<timeseries>
<value double="24.7" index="2011-01-01 00:00" />
<value double="24.5" index="2011-01-02 00:00" />
<value index="2011-04-23 00:00" /> //Here for some reason i must have double=null
<value index="2011-04-24 00:00" />
<value double="24.7" index="2011-01-01 00:00" />
<value double="24.5" index="2011-01-02 00:00" />
</timeseries>
</profile>
</profiles>
</model>
This that i want is to take the value of the attribute double and to store it in a list (or vector) for each model. When the node value hasn't an attribute double to insert a null. In first level i tried the following but when it reaches in a node with no attribute double stack.
using System;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
class MyClass
{
static void Main(string[] args)
{
string file = #"C:\RDMS.xml";
XDocment doc = XDocument.Load(file);
foreach (var item in doc.Descendants("value"))
{
Console.WriteLine(item.Attribute("double").Value);
}
Console.ReadKey();
}
}
You did not specify the error, but I am guessing you are getting a NullReferenceException? You need to check if item.Attribute actually returns a value.
var doubleAttr = item.Attribute("double");
if(doubleAttr == null) continue;
Console.WriteLine(item.Attribute("double").Value);
If that is not your problem, then please be more specific as to what your problem is.
Further clarification from trope's comment below:
...you are getting a NullReferenceException because nothing returns from item.Attribute("double") for the elements that lack that attribute, therefore you cannot then call .Value, as item.Attribute("double") is null, and you receive a NullReferenceException. This does not happen with your index attribute, because all of your "value" elements have index attributes...
You can check the result of Attribute(string) if it is null:
var attrib = item.Attribute("double");
if (attrib != null)
Console.WriteLine(attrib.Value);
else
Console.WriteLine("attribute not found!");
Related
Can anyone help on how to process the information within Fields only when the Condition is True ?
I've tried For each and if then but I would like something more graceful.
<?xml version="1.0" encoding="UTF-8" ?>
<root>
<Events>
<Event Id="1">
<Condition>True</Condition>
<Fields>
<Parameter Name="thisOne" Value="1234" />
<Parameter Name="notthisOne" Value="xyz" />
<Parameter Name="thisoneagain" Value="5678" />
<Parameter Name="notthisoneAgain" Value="abc" />
</Fields>
</Event>
<Event Id="2">
<Condition>False</Condition>
<Fields>
<Parameter Name="thisOne" Value="1234" />
<Parameter Name="notthisOne" Value="xyz" />
<Parameter Name="thisoneagain" Value="5678" />
<Parameter Name="notthisoneAgain" Value="abc" />
</Fields>
</Event>
</Events>
</root>
This should do it:
var paramSets = e.Descendants("Event")
.Where(ev => (string)ev.Element("Condition") == "True")
.Select(ev => ev.Descendants("Parameter")
.Select(p => new
{
Name = (string)p.Attribute("Name"),
Value = (string)p.Attribute("Value")
}));
This will select a set of parameters for each Event element where Condition is True. In other words, the type of paramSets is IEnumerable<IEnumerable<T>>, where T is an anonymous type with a Name and Value property.
You can loop through it like this:
foreach (var event in paramSets)
{
foreach (var parameter in event)
{
// Do something with the parameter
Console.WriteLine("Name: {0}, Value: {1}", parameter.Name, parameter.Value);
}
}
Use the Where Clause to restrict the set of data in LINQ to XML.
You can get the value of a particular element by drilling down into the element and calling .Value
This will load all the name and value for all the each parameter that is part of an Event that has a condition element with a value of True:
Dim xdoc As XDocument = XDocument.Parse(str)
Dim parameters = From e In xdoc.Root.Elements("Events").Elements("Event")
From p In e.Elements("Fields").Elements("Parameter")
Where e.Element("Condition").Value = "True"
Select New With {
.Name = p.Attribute("Name").Value,
.Value = p.Attribute("Value").Value
}
This question must be easy, but I faced a problem,
which I can't deal with.
No matter what I try I am unable to parse this xml with linq and get the xml value.
The error is "System.Collections.Generic.IEnumerable' does not contain a definition for 'Element' and no extension method 'Element' accepting a first argument of type 'System.Collections.Generic.IEnumerable' could be found (are you missing a using directive or an assembly reference?)"
I want to find the Xelement attribute.value which children have a concrete attribute.value.
How can I get the attribute.value?
thanks
xml
<submitInfo>
<setting name="file1" file ="example3.c" info ="open it!" serializeAs="String">
<add name="file11" program="example2.c" />
<add name="file12" value="example1.c" />
<value />
<setting name="file2" file ="example23.c" info ="open it!" serializeAs="String">
<add name="file21" program="example22.c" />
<add name="file22" value="example21.c" />
<value />
</setting>
</submitInfo>
code:
var title1 = from q in doc.Element("content").Element("submitInfo").Elements("setting")
select q;
foreach (var t1 in title1)
{
Console.WriteLine(
String.Format(
name = title1.Element("name").Value,
file= title1.Element("file").Value,
info= title1.Attribute("info").Value));
}
//get setting info
var title = from p in doc.Element("content").Element("submitInfo").Element("setting").Elements("add")
select p;
foreach (var t1 in title)
{
Console.WriteLine(
String.Format(
name = title1.Element("name").Value,
value = title1.Element("program").Value));
This is one problem:
name = title1.Element("name").Value,
file= title1.Element("file").Value,
info= title1.Attribute("info").Value));
Look at your XML:
<setting name="file1" file ="example3.c" info ="open it!" serializeAs="String">
<add name="file11" program="example2.c" />
<add name="file12" value="example1.c" />
<value />
</setting>
It doesn't have a name or file element - those are attributes. So you want something like:
string name = t1.Attribute("name");
string file = t1.Attribute("file");
string info = t1.Attribute("info");
Note that this is using t1, not title1 - otherwise you're asking for the data from the query, rather than for the specific element of the query.
Additionally, you really don't need a query expression here. Just use:
var title1 = doc.Element("content").Element("submitInfo").Elements("setting");
Another problem is that you're currently calling string.Format with three assignments inside. I suspect you actually wanted:
Console.WriteLine("{0} {1} {2}", t1.Attribute("name"),
t1.Attribute("file"), t1.Attribute("info"));
My questions is regarding XML to LINQ where i have something as following structure:
<Or>
<value />
<Or>
<value />
<Or> //this is the deepest "or" element i should get in this case
<value />
<value />
</Or>
</Or>
</Or>
Which i basically build programmatically through the recrusion, but my questions i rather how to get the deepest Or element?
If i do:
elements.Element("Or"), it just gets me the first top element Or ....
Waitin for response.
XDocument xDoc = XDocument.Parse(xml); //XDocument.Parse(filename);
var deepestOr = xDoc.Descendants("Or")
.First(or => !or.Descendants("Or").Any());
Try that
var bench = XElement.Parse(#"<Or><value /><Or><value /><Or><value /><value /></Or></Or></Or>");
var lastOne = bench.Descendants("Or").Where( n => n.NodeType == XmlNodeType.Element).Last();
Result:
<Or>
<value />
<value />
</Or>
No matter how deep it is
This will give you the result:
XDocument doc = XDocument.Parse(#"<Or><value /><Or><value /><Or><value /><value /></Or></Or></Or>");
// take 'Or' node which contains no 'Of' nodes
var deepest = doc.Descendants("Or").Where(node => node.Descendants("Or").Count() == 0);
I have some code that I wrote to build up an XML recursively, and it works very good as intended except one thing, that is not so generic.
The array is
string[] countries= string[]{ ..... }
My idea to have is following, if an array contains only 1 string than it should be:
<Where>
<Eq>
<FieldRef />
<Value />
</Eq>
</Where>
If there are more then one, than it should contain <OR>, but for the last string value should be in the same OR: so basically it would be something like that for 4 items:
<Where>
<Or>
<Eq>
<FieldRef Name="Title" />
<Value Type="Text">Canada</Value>
</Eq>
<Or>
<Eq>
<FieldRef Name="Title" />
<Value Type="Text">New Zealand</Value>
</Eq>
<Or>
<Eq>
<FieldRef Name="Title" />
<Value Type="Text">United States</Value>
</Eq>
<Eq>
<FieldRef Name="Title" />
<Value Type="Text">Switzerland</Value>
</Eq>
</Or>
</Or>
</Or>
</Where>
Everything is nested.
Here is my code, it works great for the multi array but not for a single result:
private XElement Recursion(XElement parentElement, int counter)
{
if (counter == 0)
{
return parentElement;
}
XElement orElement = new XElement("Or");
XElement eqElement = new XElement("Eq");
XElement fieldElement = new XElement("FieldRef");
XAttribute nameAttribute = new XAttribute("Name", "Title");
fieldElement.Add(nameAttribute);
XElement valueElement = new XElement("Value", Countries[counter]);
XAttribute typeAttribute = new XAttribute("Type", "Text");
valueElement.Add(typeAttribute);
eqElement.Add(fieldElement);
eqElement.Add(valueElement);
orElement.Add(eqElement);
if (counter == 1)
{
eqElement = new XElement("Eq");
valueElement = new XElement("Value", Countries[0]);
valueElement.Add(typeAttribute);
eqElement.Add(fieldElement);
eqElement.Add(valueElement);
orElement.Add(eqElement);
}
XElement lastOrElement = parentElement.Descendants("Or").FirstOrDefault(or => !or.Descendants("Or").Any());
if (lastOrElement == null)
{
parentElement.Add(orElement);
}
else
{
lastOrElement.Add(orElement);
}
return Recursion(parentElement, --counter);
}
}
You need to test when counter is 1 if this if the first time into the function.
Probably the simplest is to alter your if (counter==1) block to test if the passed parent element was empty (or did not contain any other <OR> elements (it not clear how first you call this function and when the <where> element is added.
Try something like:
if (counter == 1)
{
if (!parentElement.Descendant("Or").Any())
{
//Single array case
return eqElement;
}
// Not single array case, code as before....
eqElement = new XElement("Eq");
...
I have my XML as:
<CONFIGURATION NAME="HDD">
<DECLARATION NAME="1ST_HDD_SATA">
<INSTANCE>
<PROPERTY NAME="ControllerPosition" CHECKPOSITION="true">
<INDEX>
<VALUE>0</VALUE>
<VALUE STEP="1">Std.flg</VALUE>
<VALUE STEP="2">Sas.flg</VALUE>
<VALUE STEP="-1">eS.flg</VALUE>`
<CONFIGURATION NAME="HDD">
<DECLARATION NAME="1ST_HDD_SATA">
<INSTANCE>
<PROPERTY NAME="ControllerPosition" CHECKPOSITION="true">
<INDEX>
<VALUE>0</VALUE>
<VALUE STEP="1">Std_Ctrl.flg</VALUE>
<VALUE STEP="2">Sas_Card.flg</VALUE>
<VALUE STEP="-1">eSAS1.flg</VALUE>
</INDEX>
<INDEX>
<VALUE>0</VALUE>
</INDEX>
</PROPERTY>
</INSTANCE>
</DECLARATION>
</CONFIGURATION>
Got the Values using LINQ and displayed all elements in Textbox, only VALUE elements.
var hdd= from n1 in x.Elements("DECLARATION") where 1.Attribute("NAME").Value.Trim() == "1ST_HDD_SATA_Position" select n1.Elements("INSTANCE").Elements("PROPERTY").Elements("INDEX").Elements("VALUE");
Then Updated the textbox to include new values for VALUE Element. For example lets say I updated it to some string.
foreach (IEnumerable<XElement> elList in hdd)
foreach (XElement el in elList)
{
el.ReplaceNodes("string");
el.Save(m);
}
Isn't this suppose to save my xml file with the update?
There are many problems with your code, starting with the fact that you posted invalid XML. Assuming that your XML was like this:
<CONFIGURATION NAME="HDD">
<DECLARATION NAME="1ST_HDD_SATA">
<INSTANCE>
<PROPERTY NAME="ControllerPosition" CHECKPOSITION="true">
<INDEX>
<VALUE>0</VALUE>
<VALUE STEP="1">Std.flg</VALUE>
<VALUE STEP="2">Sas.flg</VALUE>
<VALUE STEP="-1">eS.flg</VALUE>
</INDEX>
</PROPERTY>
</INSTANCE>
</DECLARATION>
</CONFIGURATION>
Then the following code works:
var doc = XDocument.Load("Configuration.xml");
var hdd = from n1 in doc.Elements("CONFIGURATION").Elements("DECLARATION")
let nameAttribute = n1.Attribute("NAME")
where nameAttribute != null && nameAttribute.Value.Trim() == "1ST_HDD_SATA"
select n1.Elements("INSTANCE")
.Elements("PROPERTY")
.Elements("INDEX")
.Elements("VALUE");
foreach (var elList in hdd)
foreach (var el in elList)
{
el.ReplaceNodes("string");
}
doc.Save("Configuration.xml");