Parsing xml into anonymous type - c#

I am trying to parse the xml below to load the id_name/rel_no pairs into an anonymous type collection. I am having a problem when looping through the collection and when element is missing in one of the elements. Is there a way not to load a particular pair when one of the elements id_name or rel_no is missing?
I get InvalidOperationException (sequence contains no elements) when the loop gets to that particular pair with missing element.
Thanks for any suggestions.
XDocument xdata = XDocument.Parse(data);
var query = from dox in xdata.Descendants("Inc")
select new
{
IDName= dox.Element("id_name").Value,
RelNo= dox.Descendants("rel_no").First().Value
};
XML
<Data>
<Inc>
<id_name>test</id_name>
<Relationships>
<Relationship>
<rel_no>004</rel_no>
</Relationship>
</Relationships>
</Inc>
<Inc>
<id_name>test2</id_name>
<Relationships>
<Relationship>
</Relationship>
</Relationships>
</Inc>
<Inc>
<id_name>test3</id_name>
<Relationships>
<Relationship>
<rel_no>006</rel_no>
</Relationship>
</Relationships>
</Inc>
</Data>
Accessing in a loop
foreach (var record in query)
{
}

var xdata = XDocument.Parse(data);
var items = xdata.Descendants("Inc")
.Select(d => new
{
DName = (string)d.Element("id_name"),
RelNo = ((string)d.Descendants("rel_no").FirstOrDefault() ?? "")
})
.ToList();

Related

C# XML linq query

Hi I have the following XML:
<EPICORTLOG>
<POS>
<ClientId>WkStn.90.1</ClientId>
<Id>POS.90.20140819.251.8279</Id>
<StartTime>2014-08-25T05:12:34</StartTime>
<Store>90</Store>
<SysDate>2014-08-19T00:00:00</SysDate>
<TillNo>1</TillNo>
<Time>2014-08-25T05:12:34</Time>
<Tran>1093</Tran>
<WkStn>1</WkStn>
<WORKSTATION>
<IsAutoLock>1</IsAutoLock>
</WORKSTATION>
<TRADE>
<ITEM>
<Class>102499</Class>
<Desc>NIKE RACER</Desc>
<FinalPrice>82.77</FinalPrice>
<Status>ACTV</Status>
<Style>EV0615</Style>
<Tag>1</Tag>
</ITEM>
</TRADE>
</POS>
</EPICORTLOG>
There are many POS nodes like above in the actual XML. I am trying to fetch the POS node with ID=POS.90.20140819.251.8279 and then the details of Item from that particular node. I have written the following query:
XDocument xdoc = XDocument.Load(XMLFile);
var item = from items in xdoc.Element("EPICORTLOG").Descendants("POS")
where items.Attribute("Id").Value == strSelectedPOSID
select new
{
desc=items.Element("ITEM").Attribute("Desc")
};
But it is not yielding any result for me. Here strSelectedPOSID=POS.90.20140819.251.8279. Please let me know where i am going wrong.
Id and Desc are not an Attributes. they are Elements so you should use
var item = from items in xdoc.Descendants("POS")
where (string)items.Element("Id") == strSelectedPOSID
select new
{
desc = (string)items.Element("ITEM").Element("Desc")
};
I got the value at last!! Following is what i used:
var item = from items in xdoc.Element("EPICORTLOG").Descendants("POS")
where (string)items.Element("Id") == strSelectedPOSID
select new
{
desc = items.Element("TRADE").Element("ITEM").Element("Desc").Value.ToString()
};
Thanks for the inputs.

Extracting XML values from two nodes

I want to extract the value in the moduleId attibute and the value from the Field node. For example, in this first node I want to extract the 447 in the moduleId and the 124694 from the Field node. I have the XML loaded in an XDocument. The end result will be a Tuple where the first item is the value from the moduleId attribute and the second item is the value from the Field node. Is there a way I can do this using one XLinq statement?
As a bonus...I only want to do it for nodes where the guid = "07a188d3-3f8c-4832-8118-f3353cdd1b73". This part I can probably figure out if someone can tell me how to extract information from two nodes, but a bonus would be to add the WHERE clause in there for me :)
<Records>
<Record moduleId="447">
<Field guid="07a188d3-3f8c-4832-8118-f3353cdd1b73">124694</Field>
</Record>
<Record moduleId="447">
<Field guid="07a188d3-3f8c-4832-8118-f3353cdd1b73">124699</Field>
</Record>
<Records>
I have gotten as far as extracting the Field value using this...
IEnumerable<string> c = from p in sourceDocument.Descendants("Field")
where p.Attribute("guid").Value == "07a188d3-3f8c-4832-8118-f3353cdd1b73"
select p.Value;
But I have no idea how to get information from both the Record node and the Field node.
Give this a try:
var doc = XDocument.Parse(xml);
var r = doc.Descendants("Record")
.Where(n => n.Element("Field").Attribute("guid").Value == "07a188d3-3f8c-4832-8118-f3353cdd1b73")
.Select(n => new { ModuleId = n.Attribute("moduleId").Value, Field = n.Element("Field").Value });
var a = r.ToArray();
Here is a solution that uses LINQ Query Syntax:
XDocument document = XDocument.Parse(xml);
var query =
from el in document.Root.Elements("Record")
where (string)el.Element("Field").Attribute("guid") ==
"07a188d3-3f8c-4832-8118-f3353cdd1b73"
select new
{
ModuleId = (string)el.Attribute("moduleId"),
Field = (string)el.Element("Field")
};
foreach (var item in query)
{
Console.WriteLine
("ModuleId:[{0}]\nField:[{1}]\n--",
item.ModuleId,
item.Field);
}

C# reading values of elements from an XML tree

I am trying to read some values of specific elements in an XML document, such as the values of <main><alpha>, <main><beta><epsilon> and <main><gama><delta>.
<?xml version="1.0" ?>
<main>
<alpha>One</alpha>
<beta>
<delta>DeltaValueFromBeta</delta>
<epsilon>EpsilonValueFromBeta</epsilon>
<phi>PhiValueFromBeta</phi>
</beta>
<gamma>
<delta>DeltaValueFromGamma</delta>
<epsilon>EpsilonValueFromGamma</epsilon>
<phi>PhiValueFromGamma</phi>
</gamma>
</main>
I can get the values using code like this:
XDocument doc = XDocument.Load("Sample.xml");
var quiz = from elements in doc.Elements("main").Elements("beta").Elements("epsilon")
select elements;
foreach (var item in quiz)
{
string sValue = (string) item.Value;
textBox1.AppendText(sValue);
}
Is there a more direct way to select the element values I need, without having to use a foreach loop?
Thanks
Nick
XPath is another option for direct access to a node. This reference may assist you.
How to get values from an XML file matching XPath query in C#
Edit to add to text box without foreach where result = XPath query result collection:
textbox1.AppendText(result.Select(x => x.Value));
XPath allows for dynamic path building a little easier than LINQ queries.
You can do it in your linq query:
var quiz = (from elements in doc.Elements("main").Elements("beta").Elements("epsilon")
select (string)elements).ToList();
It will give you value list.And you can append your text without using foreach:
StringBuilder sb = new StringBuilder();
var values = quiz.Select(x => sb.Append(x));
textBox1.AppendText(sb.ToString());
Or better way:
var text = doc.Descendants("epsilon")
.Select(x => (string)x)
.Aggregate((x,y) => x + y);
textBox1.AppendText(text)
textBox1.AppendText(quiz.Select(x => x.Value).Aggregate((s, s1) => s + s1));
Use the following so you do not have to use a foreach:
IEnumerable<string> values = quiz.Select(x => x.Value);
Or all in one:
IEnumerable<string> values = doc.Elements("main").Elements("beta").Elements("epsilon").Select(x => x.Value);
textBox1.AppendText(values.Aggregate((i, j) => i + j));

How properly work with LINQ to XML?

I have generated such xml file
<?xml version="1.0" encoding="utf-8"?>
<Requestes>
<Single_Request num="1">
<numRequest>212</numRequest>
<IDWork>12</IDWork>
<NumObject>21</NumObject>
<lvlPriority>2</lvlPriority>
<NumIn1Period>21</NumIn1Period>
</Single_Request>
</Requestes>
My aim is to get IDWork,numRequest and etc elements. I tried to get them this way:
foreach (XElement el in doc.Root.Elements())
{
if (el.Name == "Single_Request")
{
string num = el.Elements("numRequest").Value;
// but he says, that he cant do this .Value because it doest exist at all
}
}
How to fix this?
You have this error, because Elements("numRequest") returns collection of elements, not single element. You should use Element("numRequest") instead.
Also I suggest you to use query for getting elements by name instead of enumerating all elements and verifying their names:
var request = doc.Root.Element("Single_Request");
var num = (int)request.Element("numRequest");
Usually you use anonymous types or custom objects to group values parsed from xml:
var query = from r in doc.Root.Elements("Single_Request")
where (int)r.Attribute("num") == 1 // condition
select new {
NumRequest = (int)request.Element("numRequest"),
IdWork = (int)request.Element("IDWork"),
NumObject = (int)request.Element("NumObject")
};
var request = query.SinlgleOrDefault();
// use request.IdWork

Reading Xml file using LINQ in C#

I have a list of String
List<String> lst=new List<String>{"A","B","C"}
And an xml file like
<Root>
<ChildList>
<Childs>
<Child Name="a1" Val="A"/>
<Child Name="a2" val="A"/>
<Child Name="b1" val="B"/>
</Childs>
</ChildList>
</Root>
i need to read contets of the xml file and add to a dictionary
Dictionary<String,List<String>> dict
where the dictionary key is the items in the "lst" and value is the attribute value of "Name" from the file
So the result will be like
Key(String) Value(List<String>)
"A" "a1","a2"
"B" "b1"
"C" null
now i'm using nested for loop for this
Is there any wau to do this using LINQ to XML
Thanks in advance
I think this will do it:
XDocument doc = XDocument.Load("foo.xml");
ILookup<string, string> lookup = doc.Descendants("Childs")
.First()
.Elements("Child")
.ToLookup(x => (string) x.Attribute("Val"),
x => (string) x.Attribute("Name"));
var dictionary = lst.ToDictionary(x => x,
x => lookup[x].ToList().NullIfEmpty());
Using a helper method:
public static List<T> NullIfEmpty<T>(this List<T> list)
{
return list.Count == 0 ? null : list;
}
If you don't mind having an empty list instead of null for items which aren't in the XML file, the second statement can be simplified, with no need for the helper method:
var dictionary = lst.ToDictionary(x => x, x => lookup[x].ToList());
Note that I've structured this so that it only needs to go through the XML file once, instead of searching through the file once for each element in the list.
var xml = #"<Root>
<ChildList>
<Childs>
<Child Name=""a1"" Val=""A""/>
<Child Name=""a2"" Val=""A""/>
<Child Name=""b1"" Val=""B""/>
</Childs>
</ChildList>
</Root>";
var lst= new List<String> { "A", "B", "C" };
var doc = XDocument.Parse(xml);
var dict = (from item in lst
select new
{
Key = item,
Value = (from elem in doc.Root.Element("ChildList").Element("Childs").Elements("Child")
where (string)elem.Attribute("Val") == item
select (string)elem.Attribute("Name")).ToList()
}).ToDictionary(i => i.Key, i => i.Value);
This can be made more efficient. I iterate over the elements once for every item in lst. I'll properly come up with another solution later if others don't come up with one.

Categories

Resources