user1503496
user1503496

Reputation: 191

Fill list from Xml file using Linq to xml

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

Answers (1)

Daniel J.G.
Daniel J.G.

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

Related Questions