Handle null element in linq - c#

I'm reading data from an XML. I'm running into an issue where a value is null and I'm not sure the best way to handle it. Below is a snippet of code. The Street Address 2 does not exist in this XML but does in others so I need to make it dynamic enough to handle both instances.
var storeInfo = storeRows.Descendants("Store").Select(s => new
{
storeName = s.Element("StoreName").Value,
streetAddress1 = s.Element("StreetAddress1").Value,
streetAddress2 = s.Element("StreetAddress2").Value
});
{
foreach (var st in storeInfo)
{
alStoreName.Add(st.storeName.ToString());
alStreet1.Add(st.StreetAddress1.ToString());
alStreet2.Add(st.StreetAddress2.ToString());
}
}

Use explicit cast instead of accessing Value property
var storeInfo = storeRows.Descendants("Store").Select(s => new
{
storeName = (string)s.Element("StoreName"),
streetAddress1 = (string)s.Element("StreetAddress1"),
streetAddress2 = (string)s.Element("StreetAddress2")
});
This will return null if the element does not exist.
In addition I recommend you to create a class to encapsualate store info instead of storing information in different lists. Then just have a list of storeInfo's instead of a list of anonymous type:
var storeInfo = storeRows.Descendants("Store").Select(s => new StoreInfo
{
storeName = (string)s.Element("StoreName"),
streetAddress1 = (string)s.Element("StreetAddress1"),
streetAddress2 = (string)s.Element("StreetAddress2")
});

You should use (string)XElement explicit cast instead of Value property. It will return null when element doesn't exist.
streetAddress2 = (string)s.Element("StreetAddress2")
You can cast XElement to most of the primitive types, string, DateTime, Guid and Nullable<T> of above. See full list here: XElement Type Conversions
The same rule applies to XAttribute.

Related

Create same LINQ anonymous type from different XML element

I have an XML file like this:-
Notice that each <Field></Field> can have different element like the highlighted <I32> or <String>. I want to show the element name in a datagridview like this which Type is for the name of element (either I32 or String or other child element of <Field>) :-
So far, I've tried this code but it return An unhandled exception of type 'System.NullReferenceException'.
XDocument doc = XDocument.Load("GetLotDetails.xml");
var data = doc.Descendants("Document").Where(x => (String)x.Attribute("name") == "DATA").SelectMany(x => x.Elements("Field"));
var query = from d in data
let str = d.Element("String").Name
let other = d.Element("I32").Name
select new
{
Name = d.Attribute("name").Value,
Type = str.Equals("String") ? "String" : (other.Equals("I32") ? "I32" : null),
Value = d.Value,
};
dataGridView1.DataSource = query.ToList();
So the idea is to let the anonymous Type = *whatever element name under field*. How can I extracted different name of element in the LINQ select statement and give it to the same unknown type variable?
It has nothing to do with anonymous types. You are missing a null check
var query =
from d in data
let element = d.Element("String") ?? d.Element("I32")
select new
{
Name = d.Attribute("name").Value,
Type = element?.Name,
d.Value
};
In your original query, you unconditionally read the Name from both possible nodes, but for any given d, one of the nodes will be null. I could have written it using the null conditional operator, d.Element("String")?.Name, but the above is more readable in this context as the additional projection in your original query adds noise and potential confusion.

How to get the Id of the selectedvalue?

I have DropDownList with the following values:
ddl.SelectedValue = { Id = 234, Name = "ABC Name" }
How can I get the value of the Id?
I use WinForms and RadDropDownList
Try this one:
public int GetId(object obj)
{
var anon = new { Id = 0, Name = string.Empty };
var obj2 = MakeSameType(obj, anon);
return obj2.Id;
}
public static T MakeSameType<T>(object obj, T anonymous)
{
return (T)obj;
}
use it like:
int id = GetId(ddl.SelectedValue);
If it works, it is thanks to how the equivalent anonymous types are "condensed" in single types by the C# compiler.
Note that this solution is as brittle as you can have
If you add/remove/rename a property of the anonymous type, the GetId will break (you'll have to keep the var anon = new {...} perfectly aligned). If you move the method that creates the collection of anonymous types to another assembly it will break too (because only anonymous types inside the same assembly are "unified" by the compiler).
In general you shouldn't have anonymous types that "escape" a method. An anonymous type should remain in the method that it is defined. Assigning it directly as the DataSource of a control is asking for problems.
If you feel lazy and don't want to create a type for a key-value, use the Tuple:
var tuple = Tuple.Create(something, somethingelse, somethingstillelse);
var value1 = tuple.Item1;
var value2 = tuple.Item2;
var value3 = tuple.Item3;
and so on.
As your are using anonymous types, it gets little complicated. But, you can cast SelectedValue to dynamic, and exstract 'Id' from there:
dynamic selectedValue = ddl.SelectedValue;
int id = selectedValue.Id;
But i would recommend to declare your own class or struct for such cases.
I have changed the LINQ query
from:
var query = (from q in tableq where ...
select new {Id = q.Id, Name = q.Name});
to:
var query = (from q in tableq where ...
select q);
... and then change it to:
table1.Id = (ddl.SelectedValue as tableq).Id == null ? table1.Id : (ddl.SelectedValue as tableq).Id;

Creating lists from XML with optional elements

I am working on some XML documents and trying to parse them into Lists.
I have read the following answers and articles but, I couldn't find a way to parse optional "Values" tag and it's elements for the code below.
http://blogs.msdn.com/b/ericwhite/archive/2009/05/14/working-with-optional-elements-and-attributes-in-linq-to-xml-queries.aspx ,
LINQ to XML optional element query ,
Can XML with optional elements be used with LINQ and Anonymous Types? ,
How to Parse Optional or Null XML Elements & Attributes with LINQ?
The code:
XDocument xdoc1 = XDocument.Load("sample.xml");
List<Message> messages = new List<Message>();
messages = (from _message in xdoc1.Element("Messages").Elements("Message")
select new Message
{
Id = _message.Element("Id").Value,
Alias = _message.Element("Alias").Value,
Fields = (from _field in _message.Element("Fields").Elements("Field")
select new Field
{
FieldName = _field.Element("FieldName").Value,
StartIndex = Convert.ToInt16(_field.Element("StartIndex").Value),
StopIndex = Convert.ToInt16(_field.Element("StopIndex").Value),
DefaultBits = _field.Element("DefaultBits").Value,
CalculationMethod = (CalculationMethods.Types)Enum.Parse(typeof(CalculationMethods.Types), _field.Element("CalculationMethod").Value),
Values = (from _value in _field.Element("Values").Elements("Value")
select new Value
{
Bits = _value.Element("Bits").Value,
Meaning = _value.Element("Meaning").Value
}).ToList()
}).ToList()
}).ToList();
Some "Fields" has "Field"s with "FieldName", "StartIndex", "StopIndex", "DefaultBits", "CalculationMethod" and optionally "Values". How can I fix the code for optional "Values" tags in XML?
Instead of using .Value, use the explicit conversions in XElement. So for example:
FieldName = (string) _field.Element("FieldName"),
StartIndex = (short?) _field.Element("StartIndex")
You'll end up with null values for absent elements, because that's what the custom conversions on XElement do when the operand is null (and the return type is a reference type or a nullable value type).
For Values, it's even easier - you just need to change _field.Element("Values") to _field.Elements("Values"):
Values = (from _value in _field.Elements("Values").Elements("Value")
select new Value
{
Bits = _value.Element("Bits").Value,
Meaning = _value.Element("Meaning").Value
}).ToList()
That way, _field.Elements("Values") will return an empty sequence if there's no Values element.
you can use the conditional oprator in linq.
FieldName = _field.Element("FieldName")!=null?_field.Element("FieldName").Value:string.Empty;

Attempting to obtain properties / fields from an (anonymous class) object linq is creating

I'm having trouble figuring out what I'm doing wrong here. I have some LINQ that returns an IQuery object, and later in the code, I'm attempting to list out the attributes returned. This is best explained by this abbreviated code (the actual LINQ is a lot more complex and involves joins - the LINQ itself works fine):
public IQueryable<Object> FindAll()
{
var query = from user in _db.Users
select new
{
id = user.Id,
is_active = user.IsActive,
email = user.Email,
dob = user.Dob,
user.user_type,
};
return query;
}
Elsewhere in the code I have:
query.ConvertToCsv();
Although I have attempted to insert a .ToList() in that call as well.
The ConvertToCsv has:
public static string ConvertToCSV<TSource>(this IEnumerable<TSource> source)
{
StringBuilder sb = new StringBuilder();
var properties = typeof(TSource).GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
var enumerable = source as IList<TSource> ?? source.ToList();
if (!enumerable.Any()) return "";
string headerString = "";
foreach (var prop in properties)
{
headerString += (headerString.Length > 0 ? "," : "") + prop.Name;
}
sb.AppendLine(headerString);
foreach (TSource item in enumerable)
{
string line = string.Join(",", properties.Select(p => p.GetValue(item).ToCsvValue()).ToArray());
sb.AppendLine(line);
}
return sb.ToString();
}
Note I have also tried to pull out the property names with this code:
PropertyInfo[] pi = typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance);
var properties = pi.OrderBy(x => x.MetadataToken);
foreach (PropertyInfo p in properties)
{ etc etc }
In all cases, the property or field list returns an empty list, and as such, I can't iterate through the object to spit out a header row or data rows. Tracing through all the code and inspecting the variables indicates that everything is fine until I get to the GetProperties/GetFields line and the code fails.
What rookie mistake am I making? Should I be replacing <Object> with something else?
To pass an anonymous type, or a collection that contains anonymous
types, as an argument to a method, you can declare the parameter as
type object. However, doing this defeats the purpose of strong typing.
If you must store query results or pass them outside the method
boundary, consider using an ordinary named struct or class instead of
an anonymous type.
by Anonymous Types (C# Programming Guide)
Create your own class and change method declaration to be IQueryable<MyClass> instead of object
Did you consider doing something like: db.Users.Select(u => new UserDto() { Id = user.Id, Name = ..., where UserDto is dedicated class that has all the properties you'll need in the future? I think you lose information about properties when you cast from anonymous class to an Object. Although, I never tried to obtain member info from anonymous class

Get value of c# dynamic property via string

I'd like to access the value of a dynamic c# property with a string:
dynamic d = new { value1 = "some", value2 = "random", value3 = "value" };
How can I get the value of d.value2 ("random") if I only have "value2" as a string? In javascript, I could do d["value2"] to access the value ("random"), but I'm not sure how to do this with c# and reflection. The closest I've come is this:
d.GetType().GetProperty("value2") ... but I don't know how to get the actual value from that.
As always, thanks for your help!
Once you have your PropertyInfo (from GetProperty), you need to call GetValue and pass in the instance that you want to get the value from. In your case:
d.GetType().GetProperty("value2").GetValue(d, null);
public static object GetProperty(object target, string name)
{
var site = System.Runtime.CompilerServices.CallSite<Func<System.Runtime.CompilerServices.CallSite, object, object>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.GetMember(0, name, target.GetType(), new[]{Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0,null)}));
return site.Target(site, target);
}
Add reference to Microsoft.CSharp. Works also for dynamic types and private properties and fields.
Edit: While this approach works, there is almost 20× faster method from the Microsoft.VisualBasic.dll assembly:
public static object GetProperty(object target, string name)
{
return Microsoft.VisualBasic.CompilerServices.Versioned.CallByName(target, name, CallType.Get);
}
Dynamitey is an open source .net std library, that let's you call it like the dynamic keyword, but using the a string for the property name rather than the compiler doing it for you, and it ends up being equal to reflection speedwise (which is not nearly as fast as using the dynamic keyword, but this is due to the extra overhead of caching dynamically, where the compiler caches statically).
Dynamic.InvokeGet(d,"value2");
The easiest method for obtaining both a setter and a getter for a property which works for any type including dynamic and ExpandoObject is to use FastMember which also happens to be the fastest method around (it uses Emit).
You can either get a TypeAccessor based on a given type or an ObjectAccessor based of an instance of a given type.
Example:
var staticData = new Test { Id = 1, Name = "France" };
var objAccessor = ObjectAccessor.Create(staticData);
objAccessor["Id"].Should().Be(1);
objAccessor["Name"].Should().Be("France");
var anonymous = new { Id = 2, Name = "Hilton" };
objAccessor = ObjectAccessor.Create(anonymous);
objAccessor["Id"].Should().Be(2);
objAccessor["Name"].Should().Be("Hilton");
dynamic expando = new ExpandoObject();
expando.Id = 3;
expando.Name = "Monica";
objAccessor = ObjectAccessor.Create(expando);
objAccessor["Id"].Should().Be(3);
objAccessor["Name"].Should().Be("Monica");
var typeAccessor = TypeAccessor.Create(staticData.GetType());
typeAccessor[staticData, "Id"].Should().Be(1);
typeAccessor[staticData, "Name"].Should().Be("France");
typeAccessor = TypeAccessor.Create(anonymous.GetType());
typeAccessor[anonymous, "Id"].Should().Be(2);
typeAccessor[anonymous, "Name"].Should().Be("Hilton");
typeAccessor = TypeAccessor.Create(expando.GetType());
((int)typeAccessor[expando, "Id"]).Should().Be(3);
((string)typeAccessor[expando, "Name"]).Should().Be("Monica");
Much of the time when you ask for a dynamic object, you get an ExpandoObject (not in the question's anonymous-but-statically-typed example above, but you mention JavaScript and my chosen JSON parser JsonFx, for one, generates ExpandoObjects).
If your dynamic is in fact an ExpandoObject, you can avoid reflection by casting it to IDictionary, as described at http://msdn.microsoft.com/en-gb/library/system.dynamic.expandoobject.aspx.
Once you've cast to IDictionary, you have access to useful methods like .Item and .ContainsKey
The GetProperty/GetValue does not work for Json data, it always generate a null exception, however, you may try this approach:
Serialize your object using JsonConvert:
var z = Newtonsoft.Json.JsonConvert.DeserializeObject(Convert.ToString(request));
Then access it directly casting it back to string:
var pn = (string)z["DynamicFieldName"];
It may work straight applying the Convert.ToString(request)["DynamicFieldName"], however I haven't tested.
d.GetType().GetProperty("value2")
returns a PropertyInfo object.
So then do
propertyInfo.GetValue(d)
To get properties from dynamic doc
when .GetType() returns null, try this:
var keyValuePairs = ((System.Collections.Generic.IDictionary<string, object>)doc);
var val = keyValuePairs["propertyName"].ToObject<YourModel>;
This is the way i ve got the value of a property value of a dinamic:
public dynamic Post(dynamic value)
{
try
{
if (value != null)
{
var valorCampos = "";
foreach (Newtonsoft.Json.Linq.JProperty item in value)
{
if (item.Name == "valorCampo")//property name
valorCampos = item.Value.ToString();
}
}
}
catch (Exception ex)
{
}
}
Some of the solutions were not working with a valuekind object that I obtained from a json string, maybe because I did not have a concrete type in my code that was similar to the object that I would obtain from the json string, so how I went about it was
JsonElement myObject = System.Text.Json.JsonSerializer.Deserialize<JsonElement>(jsonStringRepresentationOfMyObject);
/*In this case I know that there is a property with
the name Code, otherwise use TryGetProperty. This will
still return a JsonElement*/
JsonElement propertyCode = myObject.GetProperty("Code");
/*Now with the JsonElement that represents the property,
you can use several methods to retrieve the actual value,
in this case I know that the value in the property is a string,
so I use the GetString method on the object. If I knew the value
was a double, then I would use the GetDouble() method on the object*/
string code = propertyCode.GetString();
That worked for me
In .Net core 3.1 you can try like this
d?.value2 , d?.value3
Similar to the accepted answer, you can also try GetField instead of GetProperty.
d.GetType().GetField("value2").GetValue(d);
Depending on how the actual Type was implemented, this may work when GetProperty() doesn't and can even be faster.
In case you have a dynamic variable such as a DapperRow for example you can first build up an ExpandoObject, then cast the Expando into an IDictionary<string, object>. From then on, getting a value via the name of a property is possible.
Helper method ToExpandoObject:
public static ExpandoObject ToExpandoObject(object value)
{
IDictionary<string, object> dapperRowProperties = value as IDictionary<string, object>;
IDictionary<string, object> expando = new ExpandoObject();
if (dapperRowProperties == null)
{
return expando as ExpandoObject;
}
foreach (KeyValuePair<string, object> property in dapperRowProperties)
{
if (!expando.ContainsKey(property.Key))
{
expando.Add(property.Key, property.Value);
}
else
{
//prefix the colliding key with a random guid suffixed
expando.Add(property.Key + Guid.NewGuid().ToString("N"), property.Value);
}
}
return expando as ExpandoObject;
}
Sample usage, I have marked in bold the casting which gives us access in the example, I have marked the important bits with the ** letters:
using (var transactionScope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
foreach (var dynamicParametersForItem in dynamicParametersForItems)
{
var idsAfterInsertion = (await connection.QueryAsync<object>(sql, dynamicParametersForItem)).ToList();
if (idsAfterInsertion != null && idsAfterInsertion.Any())
{
**var idAfterInsertionDict = (IDictionary<string, object>) ToExpandoObject(idsAfterInsertion.First());**
string firstColumnKey = columnKeys.Select(c => c.Key).First();
**object idAfterInsertionValue = idAfterInsertionDict[firstColumnKey];**
addedIds.Add(idAfterInsertionValue); //we do not support compound keys, only items with one key column. Perhaps later versions will return multiple ids per inserted row for compound keys, this must be tested.
}
}
}
In my example, I look up a property value inside a dynamic object DapperRow and first convert the Dapper row into an ExpandoObject and cast it into a dictionary property bag as shown and mentioned in other answers here.
My sample code is the InsertMany method for Dapper extension I am working on, I wanted to grab hold of the multiple ids here after the batch insert.
Use dynamic with Newtonsoft.Json.JsonConvert.DeserializeObject:
// Get JSON string of object
var obj = new { value1 = "some", value2 = "random", value3 = "value" };
var jsonString = JsonConvert.SerializeObject(obj);
// Use dynamic with JsonConvert.DeserializeObject
dynamic d = JsonConvert.DeserializeObject(jsonString);
// output = "some"
Console.WriteLine(d["value1"]);
Sample:
https://dotnetfiddle.net/XGBLU1

Categories

Resources