Extracting XML data with Linq in C# difficulty - c#

So I'm working with reading an xml file to create a dictionary, but I can't figure out how to access the xml fields I want.
Below is the format of the XML I want to read.
<Days>
<Day Name="Monday">
<Task Order="1">TestTask</Task>
<Task Order="2">Test2</Task>
</Day>
</Days>
Below is my code so far. I've tried a lot of variations for finding task and order, such as for task: (string)e, or e.ToString(), or e.Elements("Task").Value.ToString(); And for order e.Attributes("Order").ToString();
string today = DateTime.Now.ToString("dddd");
var allItems = new Dictionary<string, int>();
XElement root = XElement.Parse(_orderxml);
IEnumerable<XElement> address =
from el in root.Elements("Day")
where el.Attribute("Name").Value == today
select el;
foreach (XElement e in address)
{
string task = ???;
string order = ???;
allItems.Add(task, (int)order);
}
So far, none of these have given me the right results, and I'm really unsure of what the proper way to get this data is, so any help would be appreciated!

Add a second loop to iterate the tasks and extract the values
static void Main()
{
string _orderxml = #"<Days> <Day Name=""Wednesday""> <Task Order=""1"">TestTask</Task> <Task Order=""2"">Test2</Task> </Day></Days>";
string today = DateTime.Now.ToString("dddd");
var allItems = new Dictionary<string, int>();
XElement root = XElement.Parse(_orderxml);
IEnumerable<XElement> address =
from el in root.Elements("Day")
where el.Attribute("Name").Value == today
select el;
foreach (XElement e in address)
{
foreach (XElement t in e.Descendants())
{
string task = t.Value.ToString();
int order = int.Parse(t.Attribute("Order").Value.ToString());
allItems.Add(task, (int)order);
}
}
}
Or you can do it with a Linq query like this
var result=root.Descendants("Day").Where(d=>d.Attribute("Name").Value==today).Descendants("Task").Select(x => new {Task=x.Value,Order=x.Attribute("Order") });
Or create a dictionary from the anonymous objects
var result = root.Descendants("Day").Where(d=>d.Attribute("Name").Value==today).Select(x => new { Task = x.Value.ToString(), Order = x.Attribute("Order") }).ToDictionary(c => c.Task, c => c.Order);
Or create a dictionary directly from the linq query
var result = root.Descendants("Day").Where(d=>d.Attribute("Name").Value==today).ToDictionary(c => c.Value.ToString(), c => int.Parse(c.Attribute("Order").Value.ToString()));

Related

C# parsing multiple elements

I have below xml structure.
<Bd>
<Det AccNo="380619034" Zip="344000"></Det>
<Det AccNo="380619022" Zip="345000"></Det>
</Bd>
It's known that there are always 2 elements under <Bd> tag.
I am able to retrieve first element using below code;
string soapResult = rd.ReadToEnd();
var xdoc = XDocument.Parse(soapResult);
var y = xdoc.Descendants("Bd");
foreach (var x in y) {
var AccNo = x.Element("Bd")?.Element("Det")?.Attribute("AccNo")?.Value;
}
However this code is only giving me first element. I want to get the second element as well but not able to do so. What am i missing?
You can use Linq without loop, like the follwing code :
XDocument xDocument = XDocument.Parse(soapResult);
IEnumerable<string> accNoList = xDocument.Descendants("Bd")
.Descendants()
.Select(x => x.Attribute("AccNo").Value);
demo
Console.WriteLine(string.Join(", ", accNoList));
Outcome
"380619034, 380619022"
For your code, you can change it to:
var xdoc = XDocument.Parse(soapResult);
var y = xdoc.Descendants("Bd")
.Descendants();
foreach (var x in y)
{
var AccNo = x.Attribute("AccNo")?.Value;
Console.WriteLine(AccNo);
}
I hope this will help you out.

XML to List in C#

This is the xml:
<Packages>
<Package>
<Id>1</Id>
<Prerequisites>
<Prerequisite>7</Prerequisite>
<Prerequisite>8</Prerequisite>
</Prerequisites>
</Package>
<Package>
<Id>2</Id>
.....
</Package>
....
</Packages>
And the list:
class lista
{
public int id {get; set;}
public List<int> pre{get;set;}
}
How can I add This xml pattern to a list of lista class and this is what i have got so far bot it only put one in the second list.
XDocument xdoc = XDocument.Load("Employee.xml");
var ListPckage =
(from item in xdoc.Descendants("Package")
orderby item.Element("Id").Value
select new
{
Id = item.Element("Id").Value,
Prerequisite = item.Element("Prerequisites").Element("Prerequisite").Value,
}).ToList();
foreach works for shoing them
foreach (var item in ListPckage)
{
Console.WriteLine(item.Id);
foreach (var item1 in ListPckage)
{
Console.WriteLine(item1.Prerequisites);
}
}
As John Sket mentioned in the comment to the question, one way to achieve that is to use Linq To Xml.
//string xcontent = #"xml content here";
//XDocument xdoc = XDocument.Parse(xcontent);
XDocument xdoc = XDocument.Load("FullPathToXml");
List<lista> resultlist = xdoc.Descendants("Package")
.Select(x=> new lista
{
id = Convert.ToInt32(x.Element("Id").Value),
pre = x.Descendants("Prerequisite").Select(y=>Convert.ToInt32(y.Value)).ToList()
})
.ToList();
But, i'd suggest to use XmlSerialization/XmlDeserialization.
First parse the XML to XDocument using XDocument.Parse to get XML in XDocument variable i.e.
var requiredXml = XDocument.Parse("Xml String here")
Then you can use LINQ to Xml (something like) as below:
//may be syntactic error but you can get an idea
var requiredList =
from element in requiredXml.Descandants("Package")
Select new lista { id = element.Element("ID").Value,
pre = element.Elements("Prerequisite").Select(x=> Convert.ToInt32(x.Value)).ToList() }

How to retrieve multiple let linq to xml c#

I am trying to get all the Hoop attributes but its only getting the first values(In this instance it is adding 24 and 4 to the listbox). Is there a way of adding all my result to a list. I usually use .ToList() but it did not work in this instant. The aim was to get the hoops for Home and away separate then store it into an object.
XML:
<League>
<Round>
<Match>
<Team Side="Home" >
<Hoop qtr="1st" player-name="Joe" time-scored="24" />
<Hoop qtr="1st" player-name="Jack" time-scored="54" />
</Team>
<Team Side="Away">
<Hoop qtr="1st" player-name="James" time-scored="4" />
<Hoop qtr="1st" player-name="Brown" time-scored="34" />
</Team>
</Match>
</Round>
</League>
C#:
XDocument xDoc = XDocument.Load("test.xml");
var query = from q in xDoc.Descendants("Team")
where (string)q.Attribute("Side") == "Home"
let d = q.Element("Hoop")
select new
{
Period = d.Attribute("qtr").Value,
Name = d.Attribute("player-name").Value,
Time = d.Attribute("time-scored").Value
};
foreach (var qq in query)
{
listBox.Items.Add(qq.Time);
}
Change let d = q.Element("Hoop") to from d in q.Elements("Hoop")
var xDoc = XDocument.Load("test.xml");
var query = from q in xDoc.Descendants("Team")
where (string)q.Attribute("Side") == "Home"
from d in q.Elements("Hoop")
select new
{
Period = d.Attribute("qtr").Value,
Name = d.Attribute("player-name").Value,
Time = d.Attribute("time-scored").Value
};
foreach (var qq in query)
{
listBox.Items.Add(qq.Time);
}

Parsing xml into anonymous type

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();

XML parse nodes and subnodes with LINQ

I do have a XML similar like this
<?xml version="1.0" encoding="UTF-8"?>
<e_schema>
<schema_name value="shema1">
<contact>
<id>1</id>
<firstName>firstname1</firstName>
<lastName>lastname1</lastName>
<department>IT</department>
<emailAddress>lastname1#mydomain.com</emailAddress>
<lineManagerId>22331470</lineManagerId>
<telephone_number>
<number1>0000000000</number1>
<number2>1111111111</number2>
<number3>2222222222</number3>
<retries1>2</retries1>
<retries2>1</retries2>
<retries3>2</retries3>
<numberType1>Mobile</numberType1>
<numberType2>Fixnet</numberType2>
<numberType3>Fixnet</numberType3>
</telephone_number>
</contact>
<contact>
<id>2</id>
<firstName>firstname2</firstName>
<lastName>lastname2</lastName>
<department>SUPPORT</department>
<emailAddress>lastname2#mydomain.com</emailAddress>
<lineManagerId>22331470</lineManagerId>
<telephone_number>
<number1>3333333333</number1>
<number2>4444444444</number2>
<number3>5555555555</number3>
<retries1>2</retries1>
<retries2>1</retries2>
<retries3>2</retries3>
<numberType1>Mobile</numberType1>
<numberType2>Fixnet</numberType2>
<numberType3>Fixnet</numberType3>
</telephone_number>
</contact>
</schema_name>
</e_schema>
now with this piece of code I read each of the contact node and add them to a list
var xmlcontacts = xmlloaded.Descendants("schema_name").Where(node => (string)node.Attribute("value") == comboSchema.SelectedValue.ToString());
foreach (XElement subelement in xmlcontacts.Descendants("contact")) //element is variable
{
contact.Add(new Contact()
{
id = subelement.Element("id").Value,
firstName = subelement.Element("firstName").Value,
lastName = subelement.Element("lastName").Value,
department = subelement.Element("department").Value,
emailAddress = subelement.Element("emailAddress").Value,
lineManagerId = subelement.Element("lineManagerId").Value,
//_phonenumbers = phones
});
}
but I do not have any Idea how I can read the node with the telephone_number
can someone give a hint or a line of code how I can do that!
I assume that _phonenumbers is some kind of collection, e.g. an IEnumerable<PhoneInfo>:
_phoneNumbers = subelement.Element("telephone_number").Elements()
.Where(e => e.Name.LocalName.StartsWith("number").Select(e =>
new PhoneInfo
{
Number = e.Value,
Retries = subelement.Element("telephone_Number").Element(
"retries" + e.Name.LocalName.SubString(5)).Value,
NumberType = subelement.Element("telephone_Number").Element(
"numbertype" + e.Name.LocalName.SubString(5)).Value
})
The code uses a linq expression to create a PhoneInfo instance for each number, and it looks up the corresponding retries and number type.
As a note: The xml structure is quite bad, it would be much better to have all the numbers in <number> tags with the actual number being the content and type retries and type data being attributes of that node.
_phonenumbers = subelement.Descendants("telephone_number")
.Select(x =>
new List<string>() {
(string)x.Element("number1"),
(string)x.Element("number2"),
(string)x.Element("number3")
});
I think you need to loop thru "telephone_number" element inside "contact" element.
Below is the code you can try:
var xmlcontacts = xmlloaded.Descendants("schema_name").Where(node => (string)node.Attribute("value") == comboSchema.SelectedValue.ToString());
foreach (XElement subelement in xmlcontacts.Descendants("contact")) //element is variable
{
contact.Add(new Contact()
{
id = subelement.Element("id").Value,
firstName = subelement.Element("firstName").Value,
lastName = subelement.Element("lastName").Value,
department = subelement.Element("department").Value,
emailAddress = subelement.Element("emailAddress").Value,
lineManagerId = subelement.Element("lineManagerId").Value,
//_phonenumbers = phones
});
foreach (XElement phoneElement in subelement.Descendants("telephone_number"))
{
//add telephone_number details in list here
}
}
I have just added one more foreach inside the "contact" loop

Categories

Resources