Reputation: 191
I am trying to fill a list of clients from an xml file using linq, but i always get this null exception: Object reference not set to an instance of an object.
. Here is my code, starting with the Client class:
public class Client
{
// Personne Physique
public string IdClient { get; set; }
public string NomClient { get; set; }
public string PrenomClient { get; set; }
public Client(){}
}
The code to fill the list:
var doc = XDocument.Load(pathXml);
List<Client> lc = doc.Descendants("Anzeige")
.FirstOrDefault()
.Descendants("Kunde")
.Select(p => new Client()
{
IdClient = p.Element("KundNr").Value,
NomClient = p.Element("Nachname").Value,
PrenomClient = p.Element("Vorname").Value
}).ToList<Client>();
The xml file looks like this:
<Anzeige>
<Kunde>
<KundNr>111</KundNr>
<Nachname>111</Nachname>
<Vorname>111</Vorname>
</Kunde>
</Anzeige>
Help please! I am pressed by time.
Upvotes: 0
Views: 1081
Reputation: 35012
That code will be fine for the sample Xml you have posted. However you are not handling some scenarios where your code will break. For example:
An Xml document with no <Anzeige>
node will cause a null exception
in doc.Descendants("Anzeige").FirstOrDefault().Descendants("Kunde")
as the result from FirstOrDefault()
will be null.
An Xml document where one of the <Kunde>
nodes doesn't have one of
the value nodes will also cause an exception. For example if there is
no <Vorname>
value then this piece of code will throw an exception
p.Element("Vorname").Value
You can tweak a bit your code to handle these scenarios.
Edit: You can use Elements
instead of Descendants
to force an xml where the Anzeige nodes come directly after the root and Kunde are direct childs of Anzeige. I have also edited my answer to take advantage of the cast operators that you can use directly on an XElement. This way (int?) p.Element("KundNr")
returns either an int
or null
value if the node doesn't exist. Combined with ??
operator its a clean way to read the value. The cast will work with string values and basic value types like int
or decimal
. (Just for the purpose of demonstrating this I changed IdClient
to an int
)
You can still have an error if you try to convert to int?
and the node value is something that cannot be converted on an int
like "ABC"
. However you had all fields as strings so that shouldn't be a problem for you:
var clients = doc.Descendants("Anzeige").Descendants("Kunde").Select(p => new Client()
{
IdClient = (int?) p.Element("KundNr") ?? -1,
NomClient = (string) p.Element("Nachname") ?? String.Empty,
PrenomClient = (string) p.Element("Vorname") ?? String.Empty
}).ToList();
I have put together a small console application testing a few sample xmls:
static void Main(string[] args)
{
var doc = XDocument.Parse(
@"<Anzeige>
<Kunde>
<KundNr>111</KundNr>
<Nachname>111</Nachname>
<Vorname>111</Vorname>
</Kunde>
<Kunde>
<KundNr>222</KundNr>
<Nachname>222</Nachname>
<Vorname>222</Vorname>
</Kunde>
</Anzeige>");
ExtractClients(doc);
var docWithMissingValues = XDocument.Parse(
@"<Anzeige>
<Kunde>
<KundNr>111</KundNr>
<Vorname>111</Vorname>
</Kunde>
<Kunde>
<KundNr>222</KundNr>
<Nachname>222</Nachname>
</Kunde>
<Kunde>
</Kunde>
</Anzeige>");
ExtractClients(docWithMissingValues);
var docWithoutAnzeigeNode = XDocument.Parse(
@"<AnotherNode>
<Kunde>
<KundNr>111</KundNr>
<Vorname>111</Vorname>
</Kunde>
</AnotherNode>");
ExtractClients(docWithoutAnzeigeNode);
var docWithoutKundeNodes = XDocument.Parse(
@"<Anzeige>
<OtherNode></OtherNode>
</Anzeige>");
ExtractClients(docWithoutKundeNodes);
var emptyDoc = new XDocument();
ExtractClients(emptyDoc);
Console.ReadLine();
}
private static void ExtractClients(XDocument doc)
{
var clients = doc.Descendants("Anzeige").Descendants("Kunde").Select(p => new Client()
{
//You can manually get the value like this:
//IdClient = p.Element("KundNr") != null ? p.Element("KundNr").Value : String.Empty,
//NomClient = p.Element("Nachname") != null ? p.Element("Nachname").Value : String.Empty,
//PrenomClient = p.Element("Vorname") != null ? p.Element("Vorname").Value : String.Empty
//Or directly cast the node value to the type (value types or strings) required like:
IdClient = (int?) p.Element("KundNr") ?? -1,
NomClient = (string) p.Element("Nachname") ?? String.Empty,
PrenomClient = (string) p.Element("Vorname") ?? String.Empty
}).ToList();
Console.WriteLine();
foreach (var client in clients)
{
Console.WriteLine("{0},{1},{2}", client.IdClient, client.NomClient, client.PrenomClient);
}
Console.WriteLine(new string('-',30));
}
public class Client
{
// Personne Physique
public int IdClient { get; set; }
public string NomClient { get; set; }
public string PrenomClient { get; set; }
public Client() { }
}
Hope this helps!
Upvotes: 1