Hi there I'm trying to parse an XML file into a list of contacts, but having problems:
public List<ContactModel> GetContacts()
{
var doc = XDocument.Load(HttpContext.Current
.Server
.MapPath(#"..\App_Data\Contacts.xml"));
var result = from items in doc.Descendants("Directory")
select new ContactModel()
{
Id = items.Attribute("ID").Value,
FirstName = items.Attribute("FirstName").Value,
LastName = items.Attribute("LastName").Value,
Telephone = items.Attribute("Telephone").Value,
Email = items.Attribute("Email").Value,
Room = items.Attribute("Room").Value,
Building = items.Attribute("Building").Value,
Location = items.Attribute("Location").Value
};
List<ContactModel> contactList = new List<ContactModel>();
foreach (var item in result)
{
contactList.Add(item);
}
return contactList;
}
I get a null exception when it's trying to loop, what am I doing wrong?
This is my XML
<?xml version="1.0" standalone="yes"?>
<ContactDirectory>
<Directory>
<ID>1</ID>
<FirstName>Peter</FirstName>
<LastName>Sutt</LastName>
<Telephone>777888</Telephone>
<Email>pett#gmail.com</Email>
<Room>3.44</Room>
<Building>Westside</Building>
<Location>Leeds</Location>
</Directory>
<Directory>
<ID>2</ID>
<FirstName>Fred</FirstName>
<LastName>West</LastName>
<Telephone>1234</Telephone>
<Email>fred#west.com</Email>
<Room>1.23</Room>
<Building>Cromwell St</Building>
<Location>Gloster</Location>
</Directory>
<Directory>
</ContactDirectory>
Beebul, They are Elements not Attributes
var contactList = (from items in doc.Descendants("Directory")
select new ContactModel()
{
Id = items.Element("ID").Value,
FirstName = items.Element("FirstName").Value,
LastName = items.Element("LastName").Value,
Telephone = items.Element("Telephone").Value,
Email = items.Element("Email").Value,
Room = items.Element("Room").Value,
Building = items.Element("Building").Value,
Location = items.Element("Location").Value
})
.ToList();
PS: you don't need to loop over your result to get a list. You can use ToList()
It looks like one or more of your attributes are missing. items.Attribute(...) returns null, and calling Value on it causes the NPE*.
Since the execution is deferred, the call does not happen until you start looping through the result.
To find the attribute that causes the problem remove all calls to Attribute(...) except for ID, verify that the crash does not happen, and start adding back the attributes one by one until the crash is back.
* After seeing the XML that you added to your question it appears that all attributes are missing! Here is a link to a short article that discusses the differences.
The Linq to XML API has implicit conversions to help with possible null references returned from .Value. Try something like (string) items.Attribute("Room").
Related
how to add value of address and HomeAddress based on condition for Rule1 and Rule2.
Xml File :
<?xml version="1.0" encoding="utf-8"?>
<File>
<Data>
<id></id>
<name></name>
<address></address>
<HomeAddress></HomeAddress>
</Data>
</File>
C# code
[in reality xml is complex file]
XDocument xmlDocFinal = XDocument.Load(#"E:/Test.xml"); string[]
myCondition = {"Rule1","Rule2"};
foreach(string rule in myCondition)
{
var dataList = (from root in xmlDoc.Descendants("File")
from Data in root.Elements("Data")
select new
{
// For
Name = Data.Element("name").Value,
ID = Data.Element("id").Value,
// if rule1 then add Address otherwise ingnor it
Address =Data.Element("address").Value
// if rule2 then add HomeAddress otherwise ingnor it
HomeAddress = Data.Element("HomeAddress").Value
})
.ToList();
}
You can make in-line conditions with the conditional operator. Something like this:
Address = someCondition ? Data.Element("address").Value : string.Empty,
HomeAddress = anotherCondition ? Data.Element("HomeAddress").Value : string.Empty
So if someCondition is true then Address is set to the value, otherwise it's set to a default value (here I use string.Empty, you can use whatever you like). The same is true for setting HomeAddress.
Edit: Based on comments below, if you want to create two separate types then you're going to need two separate queries. Something like this:
var dataListAddress = (from root in xmlDoc.Descendants("File")
from Data in root.Elements("Data")
where someCondition // first condition
select new
{
Name = Data.Element("name").Value,
ID = Data.Element("id").Value,
Address = Data.Element("address").Value
}).ToList();
var dataListHomeAddress = (from root in xmlDoc.Descendants("File")
from Data in root.Elements("Data")
where anotherCondition // second condition
select new
{
Name = Data.Element("name").Value,
ID = Data.Element("id").Value,
HomeAddress = Data.Element("HomeAddress").Value
}).ToList();
This is because the anonymous types in the select clause are different. You could perhaps merge them into the same collection if it's a collection of something like object or maybe dynamic, but that could be unwieldy downstream when you need to use that collection.
I've got a problem with XML unit Testing.
Basically the problem is that there are 2 identical tags under same xpath, and I don't really know how exactly I should compare all of the children inside
...aperson...
.../aperson...
if there is another one identical, but with (different values inside)
Samples are made in list and my guess is i need to make both <aperson> </aperson> from XML Document in to list as well, but I'm not too sure about it and don't really have an idea how to do it.
<Person>
<apersons>
<aperson>
<person_name></person_name>
<person_code></person_code>
<institut></institut>
<post></post>
<rep_type></rep_type>
<persreg></persreg
<state></state>
<address/>
</aperson>
<aperson>
<person_name></person_name>
<person_code></person_code>
<institut></institut>
<post></post>
<rep_type></rep_type>
<persreg></persreg>
<state></state>
<address/>
</aperson>
</apersons>
</person>
C#
c.Personas.Add(new Person
{
Name = "Elga",
Code = "-10317",
Institut = "Valde",
Post = "loceklis",
RepType = "1",
Perseg = "-07-03",
State = "LV"
});
// Person2
c.Personas.Add(new Person
{
Name = "Elga1",
Code = "-10317",
Institut = "Valde",
Post = "loceklis",
RepType = "1",
Perseg = "-07-03",
State = "LV"
});
CODE: pastebin
I'm parsing a lot of XML files using Linq to XML synatx, everything works when I try to access top level elements
var indexroot = (from element in prodcutDocument.Root.Descendants("indexroot")
select new
{
model = (string)element.Element("MODEL"),
}).FirstOrDefault()
The problem occurs when I need to access lower level childs of that document I tried:
var indexroot = (from element in prodcutDocument.Root.Descendants("indexroot")
select new
{
ddName = (string)element.Descendants("DD_NAME").Elements("name").First();
}).FirstOrDefault()
and
var indexroot = (from element in prodcutDocument.Root.Descendants("indexroot").Descendants("DD_NAME")
select new
{
ddName = (string)element.Element("name")
}).FirstOrDefault();
Sadly none of that works and i get same error "Sequence contains no elements". And one more thing sometimes the XML document contains those tags and sometimes not is something like this enough for handling this case?
var indexroot = (from element in prodcutDocument.Root.Descendants("indexroot").Descendants("DD_NAME")
select new
{
ddName = (string)element.Element("name") ?? "-"
}).FirstOrDefault();
Edit:
I don't think is possible to paste short version of XML that would be simple, so here's full version: http://pastebin.com/uDkP3rnR and for the code example:
XDocument prodcutDocument = XDocument.Load(this.ServerPATHData + file);
var indexroot = (from element in prodcutDocument.Root.Descendants("indexroot")
select new
{
modelis = (string)element.Element("MODELIS"),
T2918_0 = (string)element.Descendants("dd_DARBINIS_GRAFIKAS_SPEC").First()
}).FirstOrDefault();
writeTxt.WriteLine("modelis: " + indexroot.modelis);
writeTxt.WriteLine("T2979_0" + indexroot.T2918_0);
In examining the sample XML that you posted on PasteBin, it appears to me that the elements that you mention appear only once. To access them, you can simply specify a path to each as follows:
XElement indexroot = document.Root.Element("indexroot");
XElement modelis = indexroot.Element("MODELIS");
XElement dd_dgs = indexroot.Element("dd_DARBINIS_GRAFIKAS_SPEC");
XElement voltageuv = dd_dgs.Element("VoltageUV");
string t2979_0 = (string)voltageuv.Element("T2979_0");
string t2861_60 = (string)voltageuv.Element("T2861_60");
string t2757_121 = (string)voltageuv.Element("T2757_121");
(Note that you may need to check for null if there is a chance that any of the elements you are trying to access may not be present. Without doing so, you'll encounter a NullReferenceException.)
Here is a snippet of the XML that you posted to give context to the above code:
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<PDB>
<indexroot>
<ed_BENDRA_MAKS_SUV_GALIA>1.45</ed_BENDRA_MAKS_SUV_GALIA>
<ed_BENDRA_MAKS_SROVE>6.48</ed_BENDRA_MAKS_SROVE>
<TIPAS>1</TIPAS>
<MODELIS>RIS 2500 HW EC 3.0</MODELIS>
<dd_DARBINIS_GRAFIKAS_SPEC>
<VoltageUV>
<T2979_0>229,42</T2979_0>
<T2861_60>227,98</T2861_60>
<T2757_121>228,97</T2757_121>
</VoltageUV>
<CurrentIA>
<T2979_0>2,56</T2979_0>
<T2861_60>2,63</T2861_60>
<T2757_121>2,72</T2757_121>
</CurrentIA>
</dd_DARBINIS_GRAFIKAS_SPEC>
</indexroot>
</PDB>
You can just change:
element.Descendants("dd_DARBINIS_GRAFIKAS_SPEC").First()
to this:
element.Descendants("dd_DARBINIS_GRAFIKAS_SPEC").FirstOrDefault() ?? "-"
Hi i am doing as follow
XDocument xmlDoc = XDocument.Load(#"F:\test2.xml");
var q = from c in xmlDoc.Descendants("autoivr.ok")
where c.Element("LS_CZIP4").Value == "1234"
select new
{
name = c.Element("LS_LIN").Value,
state = c.Element("LS_STATE").Value
};
When i use
where c.attribute("LS_CZIP4").Value == "1234"
i get error of object reference not set but when i use c.element there is no such error.
Following is the xml i made which is actually a table in sql converted to xml file
<?xml version="1.0" standalone="yes"?>
<DocumentElement>
<autoivr.ok>
<LS_LIN>abc</LS_LIN>
<LS_STATE>def</LS_STATE>
<LS_TYPE>5</LS_TYPE>
<LS_CZIP4>1234</LS_CZIP4>
<priority>0</priority>
</autoivr.ok>
Can someone let me know the problem and how can i resolve and can i work with element tag only instead of attribute . Thank You
Use casting instead of accessing Value property. Casting to string will return null for non-existing elements. Getting Value will throw an exception
XDocument xmlDoc = XDocument.Load(#"F:\test2.xml");
var q = from c in xmlDoc.Descendants("autoivr.ok")
where (string)c.Element("LS_CZIP4") == "1234"
select new
{
name = (string)c.Element("LS_LIN"),
state = (string)c.Element("LS_STATE")
};
BTW you need closing tag for <DocumentElement>. Also LS_CZIP4 is element, not attribute. See the difference here XML Elements vs. Attributes.
Element: <LS_LIN>abc</LS_LIN>
Attribute: <autoivr.ok LS_LIN="abc">
I can't figure out why my code just taking the first tag and not the rest.
var xml = XDocument.Load(HttpContext.Current.Server.MapPath("~/App_Data/Themes.xml"));
var q = from f in xml.Descendants("themes")
select new ThemesItem
{
Name = f.Element("theme").Element("name").Value,
Description = f.Element("theme").Element("description").Value,
Author = f.Element("theme").Element("author").Value,
};
return q.ToList();
ThemeItem is just a get set with public string
When i write out this data i use a repeater
Thanks for help :)
That is because the Descendants extension method takes all decendants of the xml node, that is named "themes". Since your themes node is the container for the individual theme tags, there is only one, and when you take .Element on that, you get the first occurence.
This code should work:
var q = from f in xml.Descendants("theme")
select new ThemesItem
{
Name = f.Element("name").Value,
Description = f.Element("description").Value,
Author = f.Element("author").Value,
};
<themes>
<theme>
<name>Standard</name>
<description>standard theme</description>
<author>User 1</author>
<folder>standard</folder>
</theme>
<theme>
<name>Standard</name>
<description>standard theme</description>
<author>User 2</author>
<folder>standard</folder>
</theme>
</themes>
Try using XElement.Load() instead of XDocument.Load()
http://msdn.microsoft.com/en-us/library/bb675196.aspx