C# ?? null coalescing operator LINQ - c#

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.

Related

Nullable Int Input string was not in a correct format

I am getting the error:
Input string was not in a correct format.
Some of the Parent Id's can be null. If I comment the ParentId it is successful, so I know it is that line.
var mModel = new MyModel
{
//non nullable
Id = int.Parse(dr["Id"].ToString()),
//nullable
ParentId = int.Parse(dr["ParentId"].ToString()), <--- Throwing the error here
//non nullable
ProductService = dr["MyString"].ToString()
};
I have tried Convert.ToInt32(), and TryParse() and event a ToNullableInt extension Method, but that didnt work.
Check for database null in the dr["ParentId"] and then convert to an int or assign null, like this:
ParentId = !dr.IsDBNull(dr.GetOrdinal("ParentId"))
? dr.GetInt32(dr.GetOrdinal("ParentId"))
: null;
Note: The above code assumes that the ParentId variable is a nullable integer (int? or Nullable<int>.
I don't like calling to string on datareader, moreover, it has methods to get data of specific type
Pattern is
if (!dr.IsDBNull(1)) s = dr.GetString(1)
Or for nullable int:
if (!dr.IsDBNull(1)) i = dr.GetInt32(1)
Note: if you know column name and don't know ordinal or vise-a-versa, you can use GetOrdinal and
GetName methods:
var i = dr.GetOrdinal("ParentId")
var name = dr.GetName(1)
You shouldn't use int.Parse on a value that will not parse correctly... it'll throw an exception.
If you're sure the value will be null or have a valid value, you could try this:
ParentId = String.IsNullOrEmpty(Convert.ToString(dr["ParentId"]))
? (int?)null
: int.Parse(dr["ParentId"].ToString());
You may also want to look into Int32.TryParse, which allows you to try parsing and take some alternative action if the parse fails:
int id;
if (Int32.TryParse(Convert.ToString(dr["ParentId"]), out id)
ParentId = id;
else
ParentId = 0; // the parse failed
Incidentally, I like using Convert.ToString() over .ToString() in most cases, as the former converts null to an empty string, while the latter throws an exception. Just something to consider - doesn't help in this case, as int.Parse("") will throw an exception anyway.

Default value and linq

Why this simple code doesnt work?
var abc = new[]{1,2,3,4}.Select(x => default(typeof(x)));
i expect something like array of zeros but get compiler error. So how i can default values in lambda expressions?
In my real application i have meta class with type Type
public class FieldInfo
{
public Type type
}
I get IEnumerable<FieldInfo> as parameter to my method and want return an array of default values for each type (object[])
This is the problem:
typeof(x)
You're using the typeof operator as if it's a method call, taking a value. It's not. It needs a compile-time type name or type parameter (or unbound type such as Dictionary<,>, or void).
The same is try with the default operator.
So that's what's wrong - but without knowing what you're trying to achieve, we can't actually advise you on how to fix it. You might just want something like this:
public static IEnumerable<T> SelectDefault<T>(this IEnumerable<T> source)
{
return source.Select(x => default(T));
}
Then this:
var abc = new[]{1,2,3,4}.SelectDefault().ToArray();
... would give you an int[] with all values zero.
EDIT: Now we have more information, it's easier to provide what you want, which is basically a method to get the default value from a Type, boxing as appropriate:
public static object GetDefaultValue(Type type)
{
// Non-nullable value types should be boxed, basically
if (type.IsValueType && Nullable.GetUnderlyingType(type) == null)
{
// Not the swiftest approach in the world, but it works...
return Activator.CreateInstance(type);
}
// Everything else defaults to null
return null;
}
So you'd have something like:
var defaults = fields.Select(field => GetDefaultValue(field.type));
(Where field is a FieldInfo as per the question - I'm deliberately not calling GetType() here.)
Do you mean this?
var abc = new[]
{
1, 2, 3, 4
}
.Select(x = > {
var xType = x.GetType();
if (xType.IsValueType)
{
return Activator.CreateInstance(xType);
}
else
{
return null;
}
})
You can't get the default value at runtime this way, default() and typeof() are compile time features. But you can try this suggestion. It uses Activator.CreateInstance to get the default value for a value type at runtime.
The compiler needs a compile-time type:
var abc = new[]{1,2,3,4}.Select(x => default(Int32));
will generate all 0's. I have no idea what you are trying to achieve with this, though, because all this does is return an empty array with the default value.

Linq / XML - How do you handle non existing nodes?

I am trying to figure out how to handle nodes that do not exist for all of my "card" elements. I have the following linq query:
FinalDeck = (from deck in xmlDoc.Root.Element("Cards")
.Elements("Card")
select new CardDeck
{
Name = deck.Attribute("name").Value,
Image = deck.Element("Image").Attribute("path").Value,
Usage = (int)deck.Element("Usage"),
Type = deck.Element("Type").Value,
Strength = (int)deck.Element("Ability") ?? 0
}).ToList();
with the Strength item, I had read another posting that the ?? handles the null. I am getting the following error though:
Operator '??' cannot be applied to operands of type 'int' and 'int'
How do I handle this issue?
Thanks!
Rather than use the Value property, cast to string... and for the int, cast to int? instead. The user defined conversions to nullable types will return null if the source XAttribute/XElement is null:
FinalDeck = (from deck in xmlDoc.Root.Element("Cards")
.Elements("Card")
select new CardDeck
{
Name = (string) deck.Attribute("name"),
Image = (string) deck.Element("Image").Attribute("path"),
Usage = (int?) deck.Element("Usage"),
Type = (string) deck.Element("Type"),
Strength = (int?) deck.Element("Ability") ?? 0
}).ToList();
Note that this won't help for the case where the Image element is missing, as then it'll try to dereference a null element to find the path attribute. Let me know if you want a workaround for that, but it'll be a bit of a pain, relatively speaking.
EDIT: You can always create an extension method for this yourself:
public static XAttribute NullSafeAttribute(this XElement element, XName name)
{
return element == null ? null : element.Attribute(name);
}
Then call it like this:
Image = (string) deck.Element("Image").NullSafeAttribute("path"),

Avoid object null reference exception when accessing sub-elements that may or may not exist

I have:
An XML with some elements.
A sub-element that may or may not be defined inside this XML.
Need to extract the value of the sub-element when it does exist.
How do I get the value without throwing object-reference errors?
For example:
string sampleXML = "<Root><Tag1>tag1value</Tag1></Root>";
//Pass in <Tag2> and the code works:
//string sampleXML = "<Root><Tag1>tag1value</Tag1><Tag2>tag2Value</Tag2></Root>";
XDocument sampleDoc = XDocument.Parse(sampleXML);
//Below code is in another method, the 'sampleDoc' is passed in. I am hoping to change only this code
XElement sampleEl = sampleDoc.Root;
string tag1 = String.IsNullOrEmpty(sampleEl.Element("Tag1").Value) ? "" : sampleEl.Element("Tag1").Value;
//NullReferenceException:
//Object reference not set to an instance of an object.
string tag2 = String.IsNullOrEmpty(sampleEl.Element("Tag2").Value) ? "" : sampleEl.Element("Tag2").Value;
You can use the null-coalescing-operator for a shortcut:
string tag1= (string)sampleEl.Element("Tag1") ?? string.Empty;
This also uses the fact that LINQ to XML allows the cast operation to get the value of the element (in this case cast to string), but returns null if the element does not exist.
You'll need to check for null in order to prevent them. Given the repeated pattern you're using I would just factor this off into an extension method.
public static string GetElementValue(this XElement parent, string elementName) {
if (parent == null) {
return string.Empty;
}
var element = parent.Element(elementName);
if (element == null || element.Value == null) {
return string.Empty;
}
return element.Value;
}
Now your above code can be replaced with the following
string tag1 = sampleEl.GetElementValue("Tag1");
string tag2 = sampleEl.GetElementValue("Tag2");
The C# ternary operator is pretty good for this:
string tag2 = sampleEl.Element("Tag2") == null ? "" : sampleEl.Element("Tag2").Value;
First you should check if the document is null, remember you are accessing the .Value and this will throw a null reference exception so before you apply .value do a test:
if (sampleEl != null)
//now apply .value
Or ternary:
string tag2 = sampleEl.Element("Tag2") != null ? sampleEL.Element("Tag2").Value : String.Empty
Your code then becomes:
string sampleXML = "<Root><Tag1>tag1value</Tag1></Root>";
//Pass in <Tag2> and the code works:
//string sampleXML = "<Root><Tag1>tag1value</Tag1><Tag2>tag2Value</Tag2></Root>";
XDocument sampleDoc = XDocument.Parse(sampleXML);
//Below code is in another method, the 'sampleDoc' is passed in. I am hoping to change only this code
XElement sampleEl = sampleDoc.Root;
string tag1 = sampleEl.Element("Tag1") != null ? sampleEl.Element("Tag1").Value : String.Empty;
//NullReferenceException:
//Object reference not set to an instance of an object.
string tag2 = sampleEl.Element("Tag2") != null ? sampleEL.Element("Tag2").Value : String.Empty
string tag1 = sampleEl.Element("Tag1") != null ? sampleEl.Element("Tag1").Value : string.Empty;
I came up with this extension method. It requires you to specify the property to access as a lambda, and a default value to use if the actual value or anything up the chain is null:
public static TOut ValueOrDefault<TIn, TOut>(this TIn input, Func<TIn, TOut> projection, TOut defaultValue)
where TOut : class
{
try
{
return projection(input) ?? defaultValue;
}
catch (NullReferenceException)
{
return defaultValue;
}
catch (InvalidOperationException)
{
return defaultValue;
}
}
Usage:
var value = topObject.ValueOrDefault(x=>x.ChildObject.AnotherChild.ChildProperty, String.Empty);
value will be the empty string if topObject, ChildObject, AnotherChild, or ChildProperty are null. If all of those are valid references, the return will be whatever ChildProperty actually is (which could still be the empty string). The catch for the NullReferenceException handles references of child members of a null reference. For nullable types, InvalidOperationException is thrown when accessing the Value property of a null nullable type.
C# 6.0 allows us to make the expression shorter and simpler:
string uniqueIdentifier = myNode.Document?.Element("elem1")?.Element("elem2")?.Attribute("attribute1")?.Value;
Just for the fun of it, here's a solution using LINQ to XML which
requires only one statement and
does not need the ternary operator, so you don't need to specify the name of the tag twice:
string tag1 = sampleEl.Elements("Tag1").Select(x => x.Value).FirstOrDefault();
string tag2 = sampleEl.Elements("Tag2").Select(x => x.Value).FirstOrDefault();
Add ?? "" if you want the empty string instead of null if the tag does not exist.

C# How to check is there any value before converting ToString()

I have a dataset of values, which I need to put into a textbox. However, there are some decimal and double type values in my dataset, so I need to cast them toString(). Furthermore, sometimes the dataset values are empty, so before casting toString() I need to check whether there is actually a value there or not.
This is sample line:
I need code that does something like this...
if(ds.Tables[0].Rows[0].Field<decimal>("IndexPrethodni") !=null or something){
Convert.ToString(ds.Tables[0].Rows[0].Field<decimal>("IndexPrethodni"));
}
I known decimal is not a nullable type. Is there any easy solution to achieve my desired result?
Personally, I'd wrap it like so:
var col = ds.Tables[0].Columns["IndexPrethodni"];
var row = ds.Tables[0].Rows[0];
if (!row.IsNull(col))
{
string s = row[col].ToString();
...
}
(the "via a column object" is the most direct (= fastest) indexer)
Try
if(ds.Tables[0].Rows[0]["IndexPrethodni"] != DBNull.Value)
You can also check the value using Convert.IsDBNull().
I normally use a method such like:
public T GetValue<T>(object source)
{
if (Convert.IsDBNull(source))
return default(T);
return (T)source;
}
E.g.
using (var reader = command.ExecuteReader())
{
if (reader.Read())
{
return GetValue<string>(reader["SomeColumn"]);
}
}
check for DBNull
if(ds.Tables[0].Rows[0].Field("IndexPrethodni") != DBNull.Value) {
//convert to string
}
TO use Convert.ToString you need not to check for null value, because if there will null then also it will not give any error and return blank..
You need to check the value isnt DBNull, so something like this would work
object columnValue = ds.Tables[0].Rows[0].Field<decimal>("IndexPrethodni");
if (object != System.DBNull.Value) Convert.ToString(columnValue);
Why don't you use the nullable type to check for a value?
if( ds.Tables[ 0 ].Rows[ 0 ].Field<decimal?>( "IndexPrethodni" ).HasValue )
{
Convert.ToString( ds.Tables[ 0 ].Rows[ 0 ].Field<decimal>( "IndexPrethodni" ) );
}
If we are checking for decimal value is null or not before converting to other type
decimal? d;
if (!d.HasValue)
{
//d is null
}

Categories

Resources