This question already has answers here:
How can i check if this XElement is not null before adding it to an anonymous object?
(4 answers)
Closed 9 years ago.
I have a simple XML document that looks like this:
<Person>
<LastName>LastName1</LastName>
<FirstName>FirstName1</FirstName>
<MiddleName>MiddleName1</MiddleName>
<Suffix>Suffix1</Suffix>
</Person>
However I have a constraint where I am not allowed to add empty tags. Therefore if the Suffix value does not exist, I cannot use <Suffix /> or validation will fail.
I'm composing the XML structure using XElement objects from different classes that return their respective XML via a returned XElement object from a .ToXML() method. I need to check per element to see if the XElement being returned is null. If that's the case it has to be like that line never existed. I'm trying to use the ?? operator but I'm getting the error that ?? left operand is never null. I had the code as follows:
public XElement ToXML()
{
return new XElement("Employee",
new XElement(this.LastName.ToXML()) ?? null,
new XElement(this.FirstName.ToXML()) ?? null,
new XElement(this.MiddleName.ToXML()) ?? null,
new XElement(this.Suffix.ToXML()) ?? null);
}
How can I check per XML node to see if the XElement object being returned is null and if so ignore adding/composing that node all together? Any help is appreciated, thanks!
You should be using this code instead:
public XElement ToXML()
{
var children = new[]
{
this.LastName.ToXML(),
this.FirstName.ToXML(),
this.MiddleName.ToXML(),
this.Suffix.ToXML()
};
return new XElement("Employee", children.Where(x => x != null));
}
Please note that your code has several issues:
The null-coalescing operator (??) is an operator that returns the right hand value when the left hand value is null. Making the right hand value null is completely useless.
Mixing a new statement with the ?? operator is completely useless, too, because the result of new will never be null.
The new XElement part also seems pretty useless. I assume that ToXML already returns an XElement, so why create a new instance?
A constructor in C# will either return a non-null reference to the object or will throw an exception. It will not return null*.
As for your problem, why not:
return new XElement("Employee",
this.LastName.ToXML(),
this.FirstName.ToXML(),
this.MiddleName.ToXML(),
this.Suffix.ToXML());
And just have each of those ToXML methods return null if none exist?
Or if your case is the properties themselves are null:
return new XElement("Employee",
this.LastName != null ? this.LastName.ToXML() : null, /* null is ignored */
this.FirstName != null ? this.FirstName.ToXML() : null,
this.MiddleName != null ? this.MiddleName.ToXML() : null,
this.Suffix != null ? this.Suffix.ToXML() : null);
I also just realized perhaps you always get back an XElement, but it may be empty, in that case:
var elements = new[] { this.LastName.ToXML(), this.FirstName.ToXML(), ...
// use IsEmpty to filter out those that are <Element/>
return new XElement("Employee",
elements.Where(ee => ee != null && !ee.IsEmpty));
*I believe there is an interesting edge case where you could get this from a COM interface instantiation, but we'll ignore all "strange" coding.
Related
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 5 years ago.
I am trying to get a list item that contains a certain string and I am using this code:
string myListLine= myList.FirstOrDefault(stringToCheck => stringToCheck.Contains("mystring "));
All works well but if the list does not contain a particular string it throws an error:
object reference not set to an instance of an object
I think that I should somehow validate if the string first exists but not sure what would be the best approach.
if the list does not contain a particular string it throws an error:
That is not correct. The exception is thrown, because you have a null value inside your list! If all of your items in the list are valid strings then there will be no exception thrown. In this case FirstOrDefault will simply return null.
I think that I should somehow validate if the string first exists
You can check whether it is null the oldscool way first and combine it with an logical AND && with the Contains method
string myListLine= myList.FirstOrDefault(stringToCheck =>
stringToCheck != null && stringToCheck.Contains("mystring "));
This way the second Contains will not be evaluated if the first condition evaluates to FALSE
You can use the safe navigation operator and the null-coalescing operator to fix this:
System.Collections.Generic.List<string> myList = new System.Collections.Generic.List<string> { null, "Test123" };
string myListLine = myList.FirstOrDefault(stringToCheck => stringToCheck?.Contains("mystring ") ?? false);
The problem occurs if you're calling a method on a null list entry.
Note: If the list is empty FirstOrDefault returns null.
Hope that helps.
Your code simply goes through myList, for each item, it checks if it contains "mystring ".
Only reason you may get a null reference while running that line is your list myList is null. It wouldn't get any error if it is empty.
if you get a null reference after that line, that would mean that myListLine is null, which would mean myList did not contain any items that matched "mystring ".
To make sure you do not get a null reference error with myListLine you should check if it is null before accessing it by using something like this;
if( myListLine != null ){
<Do something to myListLine>
} else {
<list doesnt contain the correct string, alert user.>
}
I think this sample code of mine is one of the safest way by accessing the myList object. If it is null then return an indicator that value was not found or empty.
List<string> myList = new List<string>()
{
"name","adress","phoneNumber"
};
myList.RemoveAll(item => item == null);
string myListLine = myList.FirstOrDefault(stringToCheck => stringToCheck.Contains("myString")) ?? "Not found/Empty";
A variation:
var myList = new List<string>(){"foo","Bar", null,"the mystring"};
string myListLine = myList.FirstOrDefault(s => s == null? false : s.Contains("mystring"));
Same notes as already mentioned regarding FirstOrDefault being null if empty list or no match (does not Contains)
I've jumped into such a thing in my code and I don't really know how to read this. Would be nice if somebody could help me with this :)
return Company?.Call?.SingleOrDefault(cf => cf.Name == Client?.CallID)
?? Company?.Call?.SingleOrDefault(cf => cf.IsDefault)
?? new CallData();
The ?. operator is called a Null-conditional operator and is a new feature from C# 6, and was announced in October 2014. https://msdn.microsoft.com/en-us/library/dn986595.aspx
The first part: Company?.Call?.SingleOrDefault can be seen as similar to this:
if (Company == null)
{
return null;
}
else if (Company.Call == null)
{
return null;
}
else
{
return Company.Call.SingleOrDefault(....
}
But then the original coder uses the ?? Null-coalesce operator, which has been a part of C# since at least 2007. It means that if whatever is left of the ?? is null, evaluate what is right of the ?? and return that instead.
So the code basically means:
If Company is null, return new CallData()
If Company.Call is null, return new CallData()
If the first call to Company.Call.SingleOrDefault returns a CallData instance (having a certain value for the Name property) , return that instance
If the first call returns null, but the second call returns a CallData instance (being the default one) , return that instance
If both calls to SingleOrDefault return null, return new CallData()
This piece of code has some issues.
First of all, it is unreadable and should be refactored into something that is easier to understand. New language features are nice, but only when used responsibly.
Second: If there are more than one CallData instances with the same Name, the SingleOrDefault will throw an exception, but there might be a unique index in the database the prevents this. The same goes for IsDefault property - if there are more than one records with IsDefault = true, the SingleOrDefault call will throw an exception.
Split it to 3 expressions first, separated by the ??:
Company?.Call?.SingleOrDefault(cf => cf.Name == Client?.CallID)
??
Company?.Call?.SingleOrDefault(cf=>cf.IsDefault)
??
new CallData();
The returned value of the entire expression would be the first expression that is returning a non-null value.
Within each segment, once any of the properties accessed using ?. is null, the entire expression will be evaluated to null.
See Null-conditional Operators
Well, using .NET 3.5 and XDocument I am trying to find <table class='imgcr'> element. I created the code below but it crashes, of course, because e.Attribute("class") may be null. So... I have to put null check everywhere? This will double e.Attribute("class"). Not laconic solution at all.
XElement table =
d.Descendants("table").
SingleOrDefault(e => e.Attribute("class").Value == "imgcr");
If you are sure you exception is thrown because you table element may come without class attribute, then you could do this instead:
XElement table =
d.Descendants("table").
SingleOrDefault(e => ((string)e.Attribute("class")) == "imgcr");
In that case you are casting a null value to string, which is null at the end, so you are comparing null == "imgcr", what is false.
You can check this msdn page if you need more info about how to retrieve the value of an attribute. There you will find this affirmation:
You can cast an XAttribute to the desired type; the explicit
conversion operator then converts the contents of the element or
attribute to the specified type.
I guess this is quite short
XElement table =
d.Descendants("table").
SingleOrDefault(e => { var x = e.Attribute("class"); return x==null ? false: x.Value == "imgcr";});
this is shorter (but not much -- unless you can re-use t variable.)
XAttribute t = new XAttribute("class","");
XElement table =
d.Descendants("table").
SingleOrDefault(e => (e.Attribute("class") ?? t).Value == "imgcr");
I have an nullexception issue when trying to get the value of an xml tag, which is under a subtree that may not be there.
The extension handler works great when it can't find a tag on an existing subtree, but seems to not be able to handle when looking for a tag in a subtree that doesn't exist.
In this case, the subtree is summaryData, which may or not be there, and trying to get addressLine1 is where it doesn't handle the null, and I get the error
System.NullReferenceException occurred, Message=Object reference not
set to an instance of an object.
Here is the xml, cut down for clarity, but structure is correct:
<record>
<accounts>
<account >
</account >
</accounts>
<summaryData>
<Date>2013-02-04</Date>
<address >
<city>Little Rock</city>
<postalCode>00000</postalCode>
<state>AR</state>
<addressLine1>Frank St</addressLine1>
</serviceAddress>
</summaryData>
</record>
My C# code is:
xmlDoc.Descendants("account")
//select (string)c.Element("account") ;
select new
{
//this works fine
Stuffinxml = c.Element("stuffinxml").Value,
//this field may not be there, but the handler handlers the exception correctly here when it as the correct root (account)
otherstuff = CustXmlHelp.GetElementValue(mR.Element("otherstuff")),
//this is the problem, where the summaryData root does not exist (or moved somewhere else)
street_address = GetElementValue(c.Element("summaryData").Element("serviceAddress").Element("addressLine1"))
};
My extension method to handle a null is:
public static string GetElementValue(this XElement element)
{
if (element != null)
{
return element.Value;
}
else
{
return string.Empty;
}
}
Any help would be appreciated, as I can't see why it fails when the subtree does not exist.
The summary data may or may not be there
That's why. As you're nesting calls, you'll have to null check them all.
Any one of these could be null:
c.Element("summaryData").Element("serviceAddress").Element("addressLine1")
Without a complex conditional, there's not a nice way around it:
street_address = c.Element("summaryData") != null
? c.Element("summaryData").Element("serviceAddress") != null
? GetElementValue(c.Element("summaryData").Element("serviceAddress").Element("addressLine1"))
: string.Empty
: string.Empty;
If the summaryDate element does not exist then
c.Element("summaryData").Element("serviceAddress").Element("addressLine1")
will throw a NullReferenceException because you're trying to call Element() on a null reference (c.Element("summaryData"))
As had been stated, your exception is due to the fact that you are passing multiple nested queries
c.Element("summaryData").Element("serviceAddress").Element("addressLine1")
is the equivalant of writing:
var x1 = c.Element("summaryData");
var x2 = x1.Element("serviceAddress")
var x3 = x2.Element("addressLine1")
So if any of c, x1, or x2 are null, you are going to get a NullReferenceException.
One possible alternative to a pure LINQ solution using multiple null checks, is to use XPath to build the expression.
With XPath, rather than doing a null check at every level, you can instead write your expression:
street_address = GetElementValue(c.XPathSelectElement("/summaryData/serviceAddress/addressLine1"))
This will evaluate the entire expression and if it does not exist in its entirety, it will return null, but will not thrown an exception like your pure LINQ query.
I am trying to prevent having NULL values when I parse an XML file to a custom object using LINQ.
I found a great solution for this on Scott Gu's blog, but for some reason it does not work for integers with me. I think I have used the same syntax but it seems I am missing something. Oh and for some reason it works when the node is not empty.
Below is an extract of my code.
List<GrantAgresso> lsResult = (from g in xml.Element("root").Elements("Elementname")
select new GrantAgresso()
{
Year = (int?)g.Element("yearnode") ?? 0,
Subdomain = (string)g.Element("domainnode") ?? ""
}).ToList();
The errormessage is:
Input string was not in a correct format.
If anyone has a clue as to what I'm doing wrong, please help :)
Edit: piece of XML (strange names but it's not by choice)
<Agresso>
<AgressoQE>
<r3dim_value>2012</r3dim_value>
<r0r0r0dim_value>L5</r0r0r0dim_value>
<r7_x0023_province_x0023_69_x0023_V005>0</r7_x0023_province_x0023_69_x0023_V005>
<r7_x0023_postal_code_x0023_68_x0023_V004 />
<r7_x0023_country_x0023_67_x0023_V003>1004</r7_x0023_country_x0023_67_x0023_V003>
<r7_x0023_communitydistrict_x0023_70_x0023_V006>0</r7_x0023_communitydistrict_x0023_70_x0023_V006>
</AgressoQE>
</Agresso>
It is this expression which is throwing:
(int?)g.Element("yearnode")
That's because if the actual value of the element's text node is String.Empty and not null, since the empty string is not a valid format for Int32.Parse, the attempted cast fails.
If the element is missing completely from your XML, this works as you expect, but if there is an empty tag <yearnode/> or <yearnode></yearnode>, you'll get the exception.
The following extension method will return 0 both if the element is not present, the element is empty or it contains a string that cannot be parsed to integer:
public static int ToInt(this XElement x, string name)
{
int value;
XElement e = x.Element(name);
if (e == null)
return 0;
else if (int.TryParse(e.Value, out value))
return value;
else return 0;
}
You could use it like this:
...
Year = g.ToInt("r3dim_value"),
...
Or if you're ready to consider the cost of reflection and to accept the default value of any value type, you may use this extension method:
public static T Cast<T>(this XElement x, string name) where T : struct
{
XElement e = x.Element(name);
if (e == null)
return default(T);
else
{
Type t = typeof(T);
MethodInfo mi = t.GetMethod("TryParse",
BindingFlags.Public | BindingFlags.Static,
Type.DefaultBinder,
new Type[] { typeof(string),
t.MakeByRefType() },
null);
var paramList = new object[] { e.Value, null };
mi.Invoke(null, paramList);
return (T)paramList[1]; //returns default(T), if couldn't parse
}
}
and use it:
...
Year = g.Cast<int>("r3dim_value"),
...
You can add Where operator
.....
.Where(a => ! string.IsNullOrEmpty(a)).ToList();
If year is null or empty string that you will get an "Input string was not in a correct format" exception. You may write an extension method to read values. I haven't tested code below, but it may give you some hints.
public static ReadAs<T>(this XElement el, T defaultValue) {
var v = (string)el; // cast string to see if it is empty
if (string.IsNullOrEmpty(v)) // test
return defaultValue;
return (T)el; // recast to our type.
}
The message Input string was not in a correct format looks like the one thrown by int.parse () so it could be that you have a yearnode with a value (not null) but which cannot be successfully parsed to an integer value.
Something like this may fix it:
List<GrantAgresso> lsResult = (from g in xml.Element("root").Elements("Elementname")
let yearNode = g.Element("yearnode")
select new GrantAgresso
{
Year = string.IsNullOrWhiteSpace(yearNode.Value) ? 0 : int.Parse(yearNode.Value),
Subdomain = g.Element("domainnode").Value
}).ToList();
A couple of things to note:
select new GrantAgresso - you don't need parenthesis for a default constructor with object initializers.
string.IsNullOrWhiteSpace - was introduced in .net 4.0, use string.IsNullOrEmpty if you're on 3.5 or earlier
g.Element("domainnode").Value - will always return a string
if you want a null for Year instead of 0, use (int?)null instead of 0
Your problem is that the cast from XElement to int? uses int.Parse method on the string value of the XElement, which in your case is String.Empty. The following results in the same error:
XElement x = new XElement("yearnode", String.Empty);
int? a = (int?)x; // throws FormatException
You can avoid this by first casting the XElement to string and checking if it is null or empty and only if not doing the cast to int?. To do this, replace
Year = (int?)g.Element("yearnode") ?? 0,
with
Year = !string.IsNullOrEmpty((string)g.Element("yearnode")) ? (int?)g.Element("yearnode") : 0,
Its not pretty and will still throw if the string is otherwise not legal, but it works if you can assume that the element is always an integer or null/empty.