LinqToXML Why this doesn't work? - c#

I have my sample XML file:
<?xml version="1.0" encoding="utf-8" ?>
<contacts>
<contact contactId="2">
<firstName>Barney</firstName>
<lastName>Gottshall</lastName>
</contact>
<contact contactId="3">
<firstName>Armando</firstName>
<lastName>Valdes</lastName>
</contact>
<contact contactId="4">
<firstName>Adam</firstName>
<lastName>Gauwain</lastName>
</contact>
</contacts>
and my Program:
using System;
using System.IO;
using System.Linq;
using System.Xml.Linq;
public class Program
{
public class contact
{
public int contactId { get; set; }
public string firstName { get; set; }
public string lastName { get; set; }
public override string ToString()
{
return firstName+" "+lastName;
}
}
public static void Main()
{
string test;
XDocument xDocument = XDocument.Load("items.xml");
var all = from a in xDocument.Elements("contact")
where a.Attribute("contactId")!=null
select new contact
{
contactId = (int) a.Attribute("contactId"),
firstName = (string) a.Attribute("firstName"),
lastName = (string) a.Attribute("lastName")
};
if (all == null)
Console.WriteLine("Null !");
else
foreach (contact t in all)
{
test = t.ToString();
Console.WriteLine(t.ToString());
}
}
Console.ReadKey();
}
}
This code shows me blank console window. There's no "Null !" and no contact element. I spent a lot of time thinking why is that... could someone help me? when I put breakpoint inside foreach statement it's not working

There are actually a couple of reasons.
Elements only gives you top-level elements of the document (the "contacts" element), so you should use Descendants to get lower-level elements).
firstName and lastName are elements, not attributes.
Here's working code:
var all = from a in xDocument.Descendants("contact")
where a.Attribute("contactId")!=null
select new contact
{
contactId = (int) a.Attribute("contactId"),
firstName = (string) a.Element("firstName").Value,
lastName = (string) a.Element("lastName").Value
};

Try Descendants() instead of Elements()
var all = from a in xDocument.Descendants("contact")
where a.Attribute("contactId") != null
select new contact
{
contactId = (int) a.Attribute("contactId"),
firstName = (string) a.Attribute("firstName"),
lastName = (string) a.Attribute("lastName")
};
or Element("contacts").Elements("contact")
var all = from a in xDocument.Element("contacts").Elements("contact")
where a.Attribute("contactId") != null
select new contact
{
contactId = (int) a.Attribute("contactId"),
firstName = (string) a.Attribute("firstName"),
lastName = (string) a.Attribute("lastName")
};

Related

Read a XML with Linq C# using where condition

I try to make a little Service for my business but it doesn't works.
<item>
<key>12323</key>
<summary></summary>
<reporter username="12313asdf">1232 asdf iii</reporter>
<cusomfields>
<customfield id="customfield_37723" key="xyz">
<customfieldname>First Name</customfieldname>
<customfieldvalues>
<customfieldvalue>Klaus</customfieldvalue>
</customfieldvalues>
</customfield>
//...many customfields
</customfields>
</item>
I created a c# method with this code -> but it doesn't work :(
XDocument doc = XDocument.Load(fileName);
var obj = (from c in doc.Descendants("item")
select new ServiceRequest_NewUser()
{
TicketID = c.Element("key").Value,
Summary = c.Element("summary").Value,
ReporterNT = c.Element("reporter").Attribute("username").Value,
ReporterFull = c.Element("reporter").Value,
FirstName = (from f in c.Descendants("customfields")
where f.Element("customfield")?.Attribute("id")?.Value == "customfield_37723"
select f.Descendants("customfieldvalues").FirstOrDefault()?.Value).FirstOrDefault()
}).ToList();
foreach (var i in obj)
{
var test = i.FirstName;
Console.WriteLine($"{i.TicketID} {i.Summary} {i.ReporterNT} {i.ReporterFull} {i.FirstName}");
}
Where is my fault? I did a alternative version of code in the comment tag. I need to output the value "Klaus".
I thank you in advance for the help.
If you expected to see Klaus in the FirstName, you should write this:
var obj = (from c in doc.Elements("item")
select new
{
TicketID = c.Element("key")?.Value,
Summary = c.Element("summary")?.Value,
ReporterNT = c.Element("reporter")?.Attribute("username")?.Value,
ReporterFull = c.Element("reporter").Value,
FirstName = (from f in c.Descendants("customfields")
where f.Element("customfield")?.Attribute("id")?.Value == "customfield_37723"
select f.Descendants("customfieldvalues").FirstOrDefault()?.Value).FirstOrDefault()
}).ToList();
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = #"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
List<Item> items = doc.Descendants("item").Select(x => new Item()
{
key = (string)x.Element("key"),
summary = (string)x.Element("summary"),
usernameText = (string)x.Element("reporter"),
username = (string)x.Element("reporter").Attribute("username"),
fields = x.Descendants("customfield").Select(y => new Field()
{
id = (string)y.Attribute("id"),
key = (string)y.Attribute("key"),
name = (string)y.Element("customfieldname"),
values = y.Descendants("customfieldvalue").Select(z => (string)z).ToList()
}).ToList()
}).ToList();
List<Item> customfield_37723 = items.Where(x => x.fields.Any(y => y.id == "customfield_37723")).ToList();
foreach (Item item in customfield_37723)
{
Console.WriteLine("Item : key = '{0}', summary = '{1}', username Text = '{2}', username = '{3}'",
item.key, item.summary, item.usernameText, item.username);
foreach (Field field in item.fields)
{
Console.WriteLine(" Field : id = '{0}', key = '{1}', name = '{2}', values = '{3}'",
field.id, field.key, field.name, string.Join(",", field.values));
}
}
Console.ReadLine();
}
}
public class Item
{
public string key { get; set; }
public string summary { get; set; }
public string usernameText { get; set; }
public string username { get; set; }
public List<Field> fields { get; set; }
}
public class Field
{
public string id { get; set; }
public string key { get; set; }
public string name { get; set; }
public List<string> values { get; set; }
}
}

How do i add data from XML to list<>?

I try to read from an xml file, but its very clonky and a lot of the data I get is in bunch from a child. I get the Name, Age, and so on in one and therefor I can't add it to a list.
My xml-file looks like this:
<?xml version="1.0" encoding="UTF-8"?><People>
<Person>
<Age>30</Age>
<Name>Boy</Name>
<Sex>Male</Sex>
</Person>
<Person>
<Age>28</Age>
<Name>Girl</Name>
<Sex>Female</Sex>
</Person>
And in my xaml.cs file I have:
List<listTest> a = new List<listTest>();
var localFolder = ApplicationData.Current.LocalFolder;
XmlDocument xmlDocument;
var file = await localFolder.TryGetItemAsync("FoodData.xml") as IStorageFile;
xmlDocument = await XmlDocument.LoadFromFileAsync(file);
And with that I need to make a setup where I can take data from the XML and put it into list<> like this:
a.add(listTest {Name = "*DATA FROM XML*", Age ="*DATA FROM XML*", Sex="*DATA FROM XML*"});
I have tried to use LINQ and use p.NodeName == "xxx" to make searches, but I don't seem to get any data out.
Can some one show me how to get the data from my xml to a list?
Let's assume you have this class:
public class Person
{
public string Name { get; set; }
public string Sex { get; set; }
public int Age { get; set; }
}
Then, to load your XML file, you could do something like:
var doc = XDocument.Load("path to your file");
var people = doc.Root
.Descendants("person")
.Select(node => new Person
{
Name = node.Element("name").Value,
Sex = node.Element("sex").Value,
Age = int.Parse(node.Element("age").Value)
})
.ToList();
See https://msdn.microsoft.com/en-us/library/bb353813.aspx
Here is a simple example of an XML import. After this code executes, results will reflect if people were found (true or false), and msg will be a list of error messages (or empty if success).
var results = true;
var msg = new List<string>(0);
XDocument aDocument = null;
try
{
aDocument = XDocument.Load("");
}
catch (Exception e)
{
results = false;
msg.Add(string.Format("Unable to open file:{0}", ""));
msg.Add(e.Message);
}
if (aDocument != null)
{
var thePeople = aDocument.Descendants("Person").ToArray();
if (thePeople.Any())
{
// there were people in the file. People is an array of XML Nodes containing your person.
foreach (var pers in thePeople.Select(p => new Person().FromXML(p)))
{
// here is a person
}
}
else
{
results = false;
msg.Add("No people found.");
}
}
Hope this helps.
Addition.
You could do something like this in your Person Class. I've added code to the original to illustrate usage.
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
public string Sex { get; set; }
public XElement ToXML()
{
return new XElement("Person", "Name", Name,
new XElement("Age", Age),
new XElement("Sex", Sex));
}
public Person FromXML(XElement node)
{
try { Name = node.Element("Name").Value; }
catch { Name = "Not Found"; }
try { Age = Convert.ToInt16(node.Element("Age").Value); }
catch { Age = -1; }
try { Sex = node.Element("Sex").Value; }
catch { Sex = ""; }
return this;
}
}

How to parse xml with LINQ to an Object with XDocument

I have an xml file as below:
<Message xsi:schemaLocation ="..">
<Header>..</Header>
<Body>
<History
xmlns="..">
<Number></Number>
<Name></Name>
<Item>
<CreateDate>..</CreateDate>
<Type>..</Type>
<Description></Description>
</Item>
<Item>
<CreateDate>..</CreateDate>
<Type>..</Type>
<Description>1..</Description>
<Description>2..</Description>
</Item>
</History>
</Body>
</Message>
I would like to create and object from this as History object.
public class History
{
public string Name { get; set; }
public string Number { get; set; }
public List<Item> Items { get; set; }
}
var xElement = XDocument.Parse(xmlString);
XElement body = (XElement)xElement.Root.LastNode;
XElement historyElement = (XElement)body.LastNode;
var history = new History
{
Name = (string)historyElement.Element("Name"),
Number = (string)historyElement.Element("Number"),
Items = (
from e in historyElement.Elements("Item")
select new Item
{
CraeteDate = DateTime.Parse(e.Element("CreateDate").Value),
Type = (string)e.Element("Type").Value,
Description = string.Join(",",
from p in e.Elements("Description") select (string)p.Element("Description"))
}).ToList()
};
Why this does not work?
The values are always null.
It seems that "historyElement.Element("Name")" is always null even there is an element and value for the element.
Any idea what am I missing?
Thanks
It's due to the namespace, try doing this:
XNamespace ns = "http://schemas.microsoft.com/search/local/ws/rest/v1";// the namespace you have in the history element
var xElement = XDocument.Parse(xmlString);
var history= xElement.Descendants(ns+"History")
.Select(historyElement=>new History{ Name = (string)historyElement.Element(ns+"Name"),
Number = (string)historyElement.Element(ns+"Number"),
Items = (from e in historyElement.Elements(ns+"Item")
select new Item
{
CraeteDate= DateTime.Parse(e.Element(ns+"CreateDate").Value),
Type = (string) e.Element(ns+"Type").Value,
Description= string.Join(",",
from p in e.Elements(ns+"Description") select (string)p)
}).ToList()
}).FirstOrDefault();
If you want to read more about this subject, take a look this link
A couple of minor things here, the xml was malformed here. So had to take a while to test and make it work.
You have an xsi in the front which I assume should be somewhere mentioned in the xsd.
Turns out you have to append the namespace if your xml node has a namespace attached to it as you are parsing the xml tree here:
My sample solution looked like this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
namespace ConsoleApplication1
{
public class History
{
public string Name { get; set; }
public string Number { get; set; }
public List<Item> Items { get; set; }
}
public class Item
{
public DateTime? CreateDate { get; set; }
public string Type { get; set; }
public string Description { get; set; }
}
class Program
{
static void Main(string[] args)
{
string xmlString =
#"<Message>
<Header>..</Header>
<Body>
<History
xmlns=""http://schemas.somewhere.com/types/history"">
<Number>12</Number>
<Name>History Name</Name>
<Item>
<CreateDate></CreateDate>
<Type>Item 1 Type</Type>
<Description>Item 1 Description</Description>
</Item>
<Item>
<CreateDate></CreateDate>
<Type>Item 2 Type</Type>
<Description>Item 2 Description 1</Description>
<Description>Item 2 Description 2</Description>
</Item>
</History>
</Body>
</Message>";
XNamespace ns = "http://schemas.somewhere.com/types/history";
var xElement = XDocument.Parse(xmlString);
var historyObject = xElement.Descendants(ns +"History")
.Select(historyElement => new History
{
Name = historyElement.Element(ns + "Name")?.Value,
Number = historyElement.Element(ns + "Number")?.Value,
Items = historyElement.Elements(ns + "Item").Select(x => new Item()
{
CreateDate = DateTime.Parse(x.Element(ns + "CreateDate")?.Value),
Type = x.Element(ns + "Type")?.Value,
Description = string.Join(",", x.Elements(ns + "Description").Select(elem=>elem.Value))
}).ToList()
}).FirstOrDefault();
}
}
}
If you dont wan't to care about finding the namespace, you might want to try the following:
var document = XDocument.Parse(xmlString);
var historyObject2 = document.Root.Descendants()
.Where(x=>x.Name.LocalName == "History")
.Select(historyElement => new History
{
Name = historyElement.Element(historyElement.Name.Namespace + "Name")?.Value,
Number = historyElement.Element(historyElement.Name.Namespace+ "Number")?.Value,
Items = historyElement.Elements(historyElement.Name.Namespace + "Item").Select(x => new Item()
{
//CreateDate = DateTime.Parse(x.Element("CreateDate")?.Value),
Type = x.Element(historyElement.Name.Namespace + "Type")?.Value,
Description = string.Join(",", x.Elements(historyElement.Name.Namespace + "Description").Select(elem => elem.Value))
}).ToList()
}).FirstOrDefault();

Extracting detail records of XML via C# LINQ

I have the following XML excerpt. I have no problem extracting the first step of XML but I can't figure out how to get to the second layer and extract each layer. Specifically, the user info in XML below.
Any help will be appreciated...
<?xml version="1.0" encoding="UTF-8" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:getStatusReportResponse xmlns:ns2="http://xxxxxxxxx.com/">
<return>
<statusReport>
<record>
<assessorDate />
<assessorOffice />
<availableDate />
<awardDate>01/01/2014</awardDate>
<awardValue>1000000</awardValue>
<businessSector>SYSTEMS</businessSector>
<user>
<accessGrantedDate />
<emailAddress>john.usda#noemail.mil</emailAddress>
<name>JOHN USDA</name>
<phoneNumber>XXX-XXX-XXXX</phoneNumber>
<role>Focal Point</role>
</user>
<user>
<accessGrantedDate />
<emailAddress>john.usda#noemail.mil</emailAddress>
<name>JOHN USDA</name>
<phoneNumber>XXX-XXX-XXXX</phoneNumber>
<role>Focal Point</role>
</user>
</record>
</statusReport>
</return>
</ns2:getStatusReportResponse>
</S:Body>
</S:Envelope>
I've tried this but it only get's me a list of the first user record and not all of them.
var records = from x in xml.Descendants("record")
select new
{
awardDate = (string) x.Descendants("awardDate").FirstOrDefault().Value
,userList = (List<string>) x.Descendants("user").Elements() //.Elements("accessGrantedDate")
.Select(a => a.Value).ToList()
};
I am assuming this is the model of your User class:
public class User
{
public DateTime? AccessGrantedDate { get; set; }
public string EMailAddress { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
public string Role { get; set; }
}
And this is extracting the User class from the XML:
var records = from x in xml.Descendants("record")
select new
{
AwardDate = (string)x.Element("awardDate"),
UserList = x.Descendants("user").Select(user => new User
{
AccessGrantedDate = string.IsNullOrEmpty((string)user.Element("accessGrantedDate")) ?
(DateTime?) null : DateTime.Parse((string)user.Element("accessGrantedDate")),
EMailAddress = (string)user.Element("emailAddress"),
Name = (string)user.Element("name"),
PhoneNumber = (string)user.Element("phoneNumber"),
Role = (string)user.Element("role")
})
};
As far as I can tell, your code gets the data of all users in the file, but only the values of nodes.
The more organized way to do this would be:
class User
{
public string accessGrantedDate { get; set; }
public string emailAddress { get; set; }
public string name { get; set; }
public string phoneNumber { get; set; }
public string role { get; set; }
}
then:
var records = from x in xml.Descendants("record")
select new
{
awardDate = (string) x.Descendants("awardDate").FirstOrDefault().Value
,userList = x.Descendants("user").Select(a=>new User
{
accessGrantedDate= a.Element("accessGrantedDate").Value,
emailAddress=a.Element("emailAddress").Value,
name=a.Element("name").Value,
phoneNumber=a.Element("phoneNumber").Value,
role = a.Element("role").Value
}).ToList()
};
or, if you don't want to organize your data this way, you may use a list of lists:
var records = from x in xml.Descendants("record")
select new
{
awardDate = (string)x.Descendants("awardDate").FirstOrDefault().Value,
userList = x.Descendants("user").Select(a => a.Elements().Select(b=>b.Value).ToList()).ToList()
};
I solved it by using a 2nd query for detail records, therefore, creating a List as a object inside the outer list.
var records = from x in xml.Descendants("record")
select new
{
awardDate = x.Element("awardDate")
,
userList = from u in x.Descendants("user")
select new
{
accessGrantedDate = (string) u.Element("accessGrantedDate")
,emailAddress =(string) u.Element("emailAddress"
,name = (string) u.Element("name")
,phoneNumber = (string) u.Element("phoneNumber")
,role = (string) u.Element("role")
}
};

C#: XML To Type -LINQ

How to convert XML Elements to of type Person ?
Elements :
XElement persons = XElement.Parse(
#"<persons>
<person>
<id>10001</id>
<name> Daniel </name>
</person>
<person>
<id>10002</id>
<name>Marshal</name>
</person>
<person>
<id>10003</id>
<name>Leo</name>
</person>
</persons>"
);
Person Type:
class Person
{
int personID;
string name;
public int PersonID
{
get {return personID;}
set {personID = value;}
}
public string Name
{
get {return name;}
set {name = value;}
}
}
I tried (incomplete & not sure whether valid approach )
Person[] Prns = from perns in persons.Nodes select new {perns};
var personList =
from p in persons.Elements("person")
select new Person
{
Name = p.Element("name").Value,
PersonID = int.Parse(p.Element("id").Value)
};
Another option would be to use XML serialization, but using LINQ to XML is probably easier:
[XmlType("person")]
public class Person
{
int personID;
string name;
[XmlElement("id")]
public int PersonID
{
get { return personID; }
set { personID = value; }
}
[XmlElement("name")]
public string Name
{
get { return name; }
set { name = value; }
}
}
…
var serializer = new XmlSerializer(typeof(Person[]), new XmlRootAttribute("persons"));
var result = (Person[])serializer.Deserialize(new StringReader(xml));

Categories

Resources