NullReferenceException in LINQ to XML query [duplicate] - c#

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 10 years ago.
I have xml code that would be something like this:
file named: player.xml
<root>
<person>
<fname>Dwight</fname>
<lname>Howard</lname>
<vertLeap>
<try1>32.33</try1>
<try2>33.33</try2>
<try3>34.33</try3>
</vertLeap>
</person>
<person>
<fname></fname>
<lname>Jordan</lname>
<vertLeap>
<try1>40.33</try1>
</vertLeap>
</person>
</root>
This isn't my real xml, but should work for the example.
Now I want to use linq to xml to read the data. Iam am trying like this.
Class:
public class Player
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Leap1 { get; set; }
public int Leap2 { get; set; }
public int Leap3 { get; set; }
public WritePlayertoDatabase()
{
//do stuff to write
}
}
Query:
XDocument xDoc = XDocument.Load("player.xml");
var player_query = from p xDoc.Desendants("person")
select new Player
{
FirstName = p.Element("fname"),
LastName = p.Element("lname"),
Leap1 = p.Element("try1"),
Leap2 = p.Element("try2"),
Leap3 = p.Element("try3")
};
I'm getting a NullReferenceException. Is there a way to test if the elements exist before I try to use the value? Or is there a way better way to accomplish this?

So there are a few things wrong with your linq query.
1) p.Element("fname") will return the fname XML Element, not a string. So you still need to get the element's value. Similarly, the Leap1-3 properties are int, but you will get the element value as a stirng and need to convert it. But, try1-3 are not ints in the xml, so you probably want to change the type to somehting else in the Player class.
2) try1 - tryx element s are all children of 'vertleap'. You can't directly get element 'try1' from 'person'. It will be null.
So how about something more like this:
var player_query = from p in xDoc.Descendants("person")
select new Player
{
FirstName = p.Element("fname") != null ? p.Element("fname").Value : "",
LastName = p.Element("lname") != null ? p.Element("lname").Value : "",
Leap1 = p.Element("vertLeap") != null ? (p.Element("vertLeap").Element("try1") != null ? Decimal.Parse(p.Element("vertLeap").Element("try1").Value) : 0) : 0,
Leap2 = p.Element("vertLeap") != null ? (p.Element("vertLeap").Element("try2") != null ? Decimal.Parse(p.Element("vertLeap").Element("try2").Value) : 0) : 0,
Leap3 = p.Element("vertLeap") != null ? (p.Element("vertLeap").Element("try3") != null ? Decimal.Parse(p.Element("vertLeap").Element("try3").Value) : 0) : 0,
};

Using the XML you posted, the p.Element("try<X>") calls will ALWAYS return nulll... as I see #SimonC has just pointed out. You'll need to tunnel down the XML tree to get the value out, or use Descendants("try<x>").FirstOrDefault() to get the first descendant with a matching name. This may still be null, which leads us back to the real point of the question.
The issue really is that you are trying to perform an operation on an object which may not exist. Apart from the conditional sequence that #SimonC suggested, you can use helper methods or extensions to detect a missing element and provide a meaningful default value.
public static string AsString(this XElement self)
{
if (self == null)
return null;
return self.Value;
}
public static double AsDouble(this XElement self, double defValue = default(double))
{
if (self == null)
return defValue;
double res = defValue;
try { res = (double)self; }
catch { }
return res;
}
public static int AsInt(this XElement self, int defValue = default(int))
{
if (self == null)
return defValue;
double res = defValue;
try { res = (double)self; }
catch { }
return (int)res;
}
Then your Linq query becomes:
var player_query = from p in xDoc.Descendants("person")
select new Player
{
FirstName = p.Element("fname").AsString(),
LastName = p.Element("lname").AsString()
Leap1 = p.Descendants("try1").FirstOrDefault().AsInt(),
Leap2 = p.Descendants("try2").FirstOrDefault().AsInt(),
Leap3 = p.Descendants("try3").FirstOrDefault().AsInt()
};
If there is no 'try1' node within the 'player' node's descendants, the FirstOrDefault() method will return null. The AsInt() extension method is then called with a null this reference, which it detects and returns default(int) instead of throwing an exception.
You could also write a bunch of ConvertElementToXXX(elem, defValue) methods, but I think this is a reasonably valid use of extensions. It's just a shame that XElement doesn't implement IConvertible.

I know I went overboard with this quesiton, but after fiddling around with it I decided to re-write your example with XmlSerialization instead. Depending on your needs, I would highly recommend you serialize your data into objects, and pass these around.
You will need the using using System.Xml.Serialization; namespace, but I wrote the below complete program if you want to use any of it.
public class Program
{
[XmlRoot("root")]
public class Team
{
private List<Player> players = new List<Player>();
[XmlElement("player")]
public List<Player> Players { get { return this.players; } set { this.players = value; } }
// serializer requires a parameterless constructor class
public Team() { }
}
public class Player
{
private List<int> verticalLeaps = new List<int>();
[XmlElement]
public string FirstName { get; set; }
[XmlElement]
public string LastName { get; set; }
[XmlElement]
public List<int> vertLeap { get { return this.verticalLeaps; } set { this.verticalLeaps = value; } }
// serializer requires a parameterless constructor class
public Player() { }
}
static public void Main(string[] args)
{
Program myProgram = new Program();
myProgram.WritePlayertoDatabase();
myProgram.ReadPlayerDatabase();
}
public void WritePlayertoDatabase()
{
Player p1 = new Player() { FirstName = "dwight", LastName = "howard", vertLeap = new List<int>() { 1, 2, 3 } };
Player p2 = new Player() { FirstName = "dwight", LastName = "howard", vertLeap = new List<int>() { 1 } };
Team players = new Team();
players.Players.Add(p1);
players.Players.Add(p2);
XmlSerializer serializer = new XmlSerializer(typeof(Team));
using (TextWriter textWriter = new StreamWriter(#"C:\temp\temp.txt"))
{
serializer.Serialize(textWriter, players);
textWriter.Close();
}
}
public void ReadPlayerDatabase()
{
Team myTeamData = new Team();
XmlSerializer deserializer = new XmlSerializer(typeof(Team));
using (TextReader textReader = new StreamReader(#"C:\temp\temp.txt"))
{
myTeamData = (Team)deserializer.Deserialize(textReader);
textReader.Close();
}
}
}

Related

Get values out of collections with FastMember

I use FastMember to get values out of objects and nested objects. If a property is a string or int everything works fine. But now I want to get the values also for collections. Here is my code so far:
// Set accessor
var sourceAccessor = ObjectAccessor.Create(source);
if (sourceAccessor.Target.GetType().GetInterface(nameof(ICollection)) != null || sourceAccessor.Target.GetType().GetInterface(nameof(IEnumerable)) != null)
{
foreach (/* idk */)
{
// READ & RETURN VALUES HERE
}
}
An object could look like this:
{
Id: 1,
Surname: Doe,
Prename: John,
Professions: [
{ Name: ab },
{ Name: xy }
]
}
Which means professions would result in a problem.
Any advise how I can solve this problem? Thanks!
It's not obvious from the question what the data type of the source variable is, but you should just be able to check if the value returned by the accessor implements IEnumerable or not and act accordingly.
Here's a quick worked example that iterates over the Professions property of a 'Person' object and just dumps the ToString() representation to the console - if you wanted to dive into each Profession object using FastMember you could construct another ObjectAccessor to do it, I guess - it's not clear what your goal is once you're iterating.
The same tactic will work if you're building the ObjectAccessor directly from an array - you just check if the accessor.Target is IEnumerable and cast-and-iterate in a similar fashion.
class Program
{
static void Main(string[] args)
{
var p = new Person
{
Professions = new List<Profession>
{
new Profession("Joker"),
new Profession("Smoker"),
new Profession("Midnight toker")
}
};
var accessor = ObjectAccessor.Create(p);
var professions = accessor[nameof(Person.Professions)];
if (professions is IEnumerable)
{
foreach (var profession in (IEnumerable)professions)
{
Console.WriteLine(profession);
}
}
}
}
class Person
{
public List<Profession> Professions { get; set; }
}
class Profession
{
public string Name { get; set; }
public Profession( string name)
{
Name = name;
}
public override string ToString()
{
return Name;
}
}

How to deserialize an array containing a list?

I would like to deserialize an array containing some things and 3 List.
The program works fine except for List. The lists are created but they contain nothing !
Can you help me ?
Here is how the xml file looks like :
<blind>
<folder>C:\Users\Michael\Desktop\BT 1 normal\Programme BT\</folder>
<nombre_titres>25</nombre_titres>
<numero></numero>
<theme></theme>
<heure_debut></heure_debut>
<mdp>a</mdp>
<lien></lien>
<playlist>
<extrait>
<artiste>Abba</artiste>
<titre>Take a chance on me</titre>
<PointAT>1.25</PointAT>
<PointA>0.5</PointA>
<PointT>0.5</PointT>
<JoueursAT>
<joueurAT>Ahkayaqua</joueurAT>
<joueurAT>Angelene</joueurAT>
</JoueursAT>
<JoueursA>
<joueurA></joueurA>
</JoueursA>
<JoueursT>
<joueurT></joueurT>
</JoueursT>
</extrait>
<extrait>
....
</extrait>
</playlist>
</blind>
My code to deserialize :
XElement xmle;
xmle = XElement.Load(_folder + "Blind.xml");
textBox1.Text = xmle.Element("numero").Value;
textBox4.Text = xmle.Element("theme").Value;
textBox3.Text = xmle.Element("heure_debut").Value;
textBox5.Text = xmle.Element("lien").Value;
textBox2.Text = xmle.Element("mdp").Value;
extraits = (from ex in xmle.Element("playlist").Elements("extrait")
select new Extrait
(ex.Element("artiste").Value,
ex.Element("titre").Value,
0,
0,
0,
(from jat in ex.Element("JoueursAT").Elements("JoueurAT")
select jat.Element("JoueurAT").Value).ToList(),
(from ja in ex.Element("JoueursA").Elements("JoueurA")
select ja.Element("JoueurA").Value).ToList(),
(from jt in ex.Element("JoueursT").Elements("JoueurT")
select jt.Element("JoueurT").Value).ToList())).ToArray();
And here is my class:
public class Extrait
{
private String _Artiste;
private String _Titre;
private double _PointA;
private double _PointT;
private double _PointAT;
private List<String> _JoueurA;
private List<String> _JoueurT;
private List<String> _JoueurAT;
public String Artiste
{
get { return _Artiste; }
set { _Artiste = value; }
}
public String Titre
{
get { return _Titre; }
set { _Titre = value; }
}
public Double PointA
{
get { return _PointA; }
set { _PointA = value; }
}
public Double PointT
{
get { return _PointT; }
set { _PointT = value; }
}
public Double PointAT
{
get { return _PointAT; }
set { _PointAT = value; }
}
public List<String> JoueurA
{
get { return _JoueurA; }
set { _JoueurA = value; }
}
public List<String> JoueurT
{
get { return _JoueurT; }
set { _JoueurT = value; }
}
public List<String> JoueurAT
{
get { return _JoueurAT; }
set { _JoueurAT = value; }
}
public Extrait(String Artiste, String Titre, Double PointA, Double PointT, Double PointAT, List<String> JoueurAT, List<String> JoueurA, List<String> JoueurT)
{
_Artiste = Artiste;
_Titre = Titre;
_PointA = PointA;
_PointT = PointT;
_PointAT = PointAT;
_JoueurAT = JoueurAT;
_JoueurA = JoueurA;
_JoueurT = JoueurT;
}
}
Well, I've tried many possibilities, but none worked !
If this is your actual xml, then look at the inner tags - they start with lower letter. Your xml have <joueurAT> while you selecting elements with name .Elements("JoueurAT") - node names are case-sensitive.
Your code should look like:
extraits = (from ex in xmle.Element("playlist").Elements("extrait")
select new Extrait
(ex.Element("artiste").Value,
ex.Element("titre").Value,
0,
0,
0,
(from jat in ex.Element("JoueursAT").Elements("joueurAT")
select jat.Value).ToList(),
(from ja in ex.Element("JoueursA").Elements("joueurA")
select ja.Value).ToList(),
(from jt in ex.Element("JoueursT").Elements("joueurT")
select jt.Value).ToList())).ToArray();
This should fix it. Basically the default behaviour is to use two-level nesting for lists (meaning it defaults to [XmlArray] plus [XmlArrayItem]; you only have one-level here, so you need to tell it.
[XmlElement]
public List<String> JoueurA
{
get { return _JoueurA; }
set { _JoueurA = value; }
}
[XmlElement]
public List<String> JoueurT
{
get { return _JoueurT; }
set { _JoueurT = value; }
}
[XmlElement]
public List<String> JoueurAT
{
get { return _JoueurAT; }
set { _JoueurAT = value; }
}
Btw; you might find it more convenient to use something like auto-properties here; an example for both regular properties and lists:
public double PointAT {get;set;}
[XmlElement]
public List<string> JoueurA {get;} = new List<string>();
This is a lot more convenient than messing with all the fields yourself.
You probably also want to make sure you have a public parameterless constructor; frankly I'd just remove the custom constructor (in which case: a public parameterless constructor is included for free), but otherwise - I'd just add:
public Extrait() {}
The change is needed in the blind class
[XmlRoot("blind")]
public class Blind
{
[XmlArray("playlist")]
[XmlArrayItem("extrait")]
public List<Extrait> extrait { get; set; }
}
public class Extrait
{
}
(from jat in ex.Element("JoueursAT").Elements("JoueurAT")
select jat.Element("JoueurAT").Value).ToList()
should become
(from jat in ex.Element("JoueursAT").Elements("joueurAT")
select jat.Value).ToList()
I'm not sure if C#'s XML library is case sensitive when it comes to token names, but when in doubt, it's better to play it safe.
You also tried to access an element "joueurAT" when making your select despite having actually looped over them already, so you can just access the Value property directly.
Also instead of using LINQ the way you do, you could try the extension methods, which tend to be more readable :
xmle.Element("playlist").Elements("extrait")
.Select(ex => new Extrait
{
Artiste = ex.Element("artiste").Value,
Titre = ex.Element("titre").Value,
PointA = 0,
PointT = 0,
PointAT = 0,
JoueurA = ex.Element("JoueursAT").Elements("joueurAT").Select(jat => jat.Value).ToList(),
JoueurT = ex.Element("JoueursA").Elements("joueurA").Select(ja => ja.Value).ToList(),
JoueurAT = ex.Element("JoueursT").Elements("joueurT").Select(jt => jt.Value).ToList()
});

C# Adding a List<int> to a class [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 5 years ago.
I have a class that looks something like this:
public class Parent
{
private string sFirst_name1;
public string sParent1FirstName
{
get { return sFirst_name1; }
set { sFirst_name1 = value; }
}
private string sLast_name1;
public string sParent1LastName
{
get { return sLast_name1; }
set { sLast_name1 = value; }
}
private List<int> lChild_ID;
public List<int> lChildID
{
get { return lChild_ID; }
set { lChild_ID = value; }
}
public Parent(string sP1FN, string sP1LN, List<int> lCID)
{
lChild_ID= new List<int>();
sFirst_name1 = sP1FN;
sLast_name1 = sP1LN;
lChild_ID = lCID;
}
I create an instance of the class and add it to a List of type Parent with the line:
lParents.Add(new Parent(sParent1FNTemp, sParent1LNTemp,lParentChildIDTemp));
Where lParentChildIDTemp is just a null integer List.
Later on, I am trying to update the int list of the object at a specified index by saying:
lParents[iIndex].lChildID.Add(++iIDCounters);
And I get a null reference exception. I'm not too sure why, but I think it has something to do with my constructor for my class. The weird thing is, I have another class that does pretty much the same thing but it doesn't throw the exception. If anyone has any clues, I would be greatful.
Give your property is null, you should make sure you are not access null values to call methods or properties. If you do that, you will get an NullReferenceException. Try this:
var childIds = lParents[iIndex].lChildID;
if (childIds != null)
{
childIds.Add(++iIDCounters);
}
In your constructor, you could check the if the argument is null and define a default list int. If you for to set the argument, even if it is null, you will get null.
public Parent(string sP1FN, string sP1LN, List<int> lCID)
{
sFirst_name1 = sP1FN;
sLast_name1 = sP1LN;
if (lCID == null)
{
lChild_ID = new List<int>();
}
else
{
lChild_ID = lCID;
}
}
Since apparently you want to be able to pass in the lChildID during construction, change the constructor as so:
public Parent(string sP1FN, string sP1LN, List<int> lCID) {
sFirst_name1 = sP1FN;
sLast_name1 = sP1LN;
lChild_ID = lCID ?? new List<int>();
}
Drop the List of int argument in the ctor and why not use auto props:
public class Parent
{
public Parent(string sP1FN, string sP1LN)
{
LChildID = new List<int>();
sFirst_name1 = sP1FN;
sLast_name1 = sP1LN;
}
public List<int> LChildID { get; set; }
If you're not familiar with auto-implemented properties, see https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/auto-implemented-properties
Plus, you could also overload the ctor:
public Parent(string sP1FN, string sP1LN, List<int> lCID)
{
LChildID = lCID;
sFirst_name1 = sP1FN;
sLast_name1 = sP1LN;
}

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.
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!

Replace a collection item using Linq

How do I find and replace a property using Linq in this specific scenario below:
public interface IPropertyBag { }
public class PropertyBag : IPropertyBag
{
public Property[] Properties { get; set; }
public Property this[string name]
{
get { return Properties.Where((e) => e.Name == name).Single(); }
//TODO: Just copying values... Find out how to find the index and replace the value
set { Properties.Where((e) => e.Name == name).Single().Value = value.Value; }
}
}
Thanks for helping out in advance.
Do not use LINQ because it will not improve the code because LINQ is designed to query collection and not to modify them. I suggest the following.
// Just realized that Array.IndexOf() is a static method unlike
// List.IndexOf() that is an instance method.
Int32 index = Array.IndexOf(this.Properties, name);
if (index != -1)
{
this.Properties[index] = value;
}
else
{
throw new ArgumentOutOfRangeException();
}
Why are Array.Sort() and Array.IndexOf() methods static?
Further I suggest not to use an array. Consider using IDictionary<String, Property>. This simplifies the code to the following.
this.Properties[name] = value;
Note that neither solution is thread safe.
An ad hoc LINQ solution - you see, you should not use it because the whole array will be replaced with a new one.
this.Properties = Enumerable.Union(
this.Properties.Where(p => p.Name != name),
Enumerable.Repeat(value, 1)).
ToArray();
[note: this answer was due to a misunderstanding of the question - see the comments on this answer. Apparently, I'm a little dense :(]
Is your 'Property' a class or a struct?
This test passes for me:
public class Property
{
public string Name { get; set; }
public string Value { get; set; }
}
public interface IPropertyBag { }
public class PropertyBag : IPropertyBag
{
public Property[] Properties { get; set; }
public Property this[string name]
{
get { return Properties.Where((e) => e.Name == name).Single(); }
set { Properties.Where((e) => e.Name == name).Single().Value = value.Value; }
}
}
[TestMethod]
public void TestMethod1()
{
var pb = new PropertyBag() { Properties = new Property[] { new Property { Name = "X", Value = "Y" } } };
Assert.AreEqual("Y", pb["X"].Value);
pb["X"] = new Property { Name = "X", Value = "Z" };
Assert.AreEqual("Z", pb["X"].Value);
}
I have to wonder why the getter returns a 'Property' instead of whatever datatype .Value, but I'm still curious why you're seeing a different result than what I am.

Categories