Resolve string to a property of an object - c#

I have a database table in SQL Server and I need to configure a .NET application to use some of the data in this table, this data is used in a SOAP API call.
For example, the table has these columns
FirstName | LastName | EmailAddress
The values in these columns need to map to certain fields in the API call. This configuration must be flexible, so I cannot just assign FirstName to apiobject.firstname, for example.
Is there a way to derive a string to a property of an object?
For example, for FirstName, would it be possible to resolve it to object.Firstname = value but not doing it like this
if(column == "FirstName")
{
ApiObject.FirstName = columnValue
}
Rather I would want to do something like
ApiObject.[somehow-resolve-which-field] = columnValue
Does anyone know what I am on about?

If you does not have notation of this class and you simply want to create an object with required properties - the right way is to use dynamic, as it noted in other answers.
If you already have a class, for example:
public class MyClass
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string EmailAddress { get; set; }
}
You can do what you want with reflection as following:
var myObject = new MyClass();
var prop = myObject.GetType().GetProperty(column);
prop.SetValue(myObject, columnValue);
Where column and columnValue are from your example. Note that column should be of string type and columnValue should match the type of property.

If I correctly understood you need an object which you don't know how many properties have it and what are these properties. For this situations there is dynamic keyword in c#. You can initialize your ApiObject as dynamic
dynamic ApiObject = new ExpandoObject();
then you can set properties of this object as following:
ApiObject.YourProperty=columnValue;
more details: About ExpandoObject
UPDATE
if you want to know which column is this, there is a ColumnName of DataTable of System.Data namespace. There are some issues for getting columns of table in c#. One of them is:
How do I get column names to print in this C# program?

Related

How to custom mapping fields name or type for method MongoDB.Bson.Serialization.BsonSerializer.Deserialize

I used the method MongoDB.Bson.Serialization.BsonSerializer.Deserialize() to deserialize from MongoDB.Bson.BsonDocument to MyType. But the method always meet System.FormatException since fields in MyType are not 100% match to the fields in BsonDocument.
I've tried to convert a complex json object(let's called mobj) from MongoDB(query result) to C# object(let's called csobj), so that I could deal with the data. The defualt datatype in csobj I use is string. But the mobj is too complex and we know it's schema less.
Once meet datatype like BinData(0,""), BinData(1,""), BinData(2,""), ISODate("") etc, in mobj, the System.FormatException may happen.
Once there are extra new fields in mobj, the System.FormatException may happen.
Once there are space in the field name like "Page one" : "XXXX", the the System.FormatException may happen and I don't know how to fix it till now.
var client = new MongoClient("mongodb://xxxxxx");
var database = client.GetDatabase("xxxxxxxxxx");
var collection = database.GetCollection<BsonDocument>("xxxxxxxxxx");
var results = await collection.Aggregate<BsonDocument>(filterBsonDocumentArray).ToListAsync();
foreach (var doc in results)
{
var model = MongoDB.Bson.Serialization.BsonSerializer.Deserialize<MyType>(doc); // always meet exception here
}
Exception examples:
(mongodb datatype could not map with string)
System.FormatException: An error occurred while deserializing the Id property of class MongoQueryDemo.MyType: Cannot deserialize a 'String' from BsonType 'Binary'. ---> System.FormatException: Cannot deserialize a 'String' from BsonType 'Binary'.
(_id in mongo could not found UserId in C# object auto)
System.FormatException: Element '_id' does not match any field or property of class MongoQueryDemo.MyType.
My questions are list here:
Is there any way to tell the Deserializer, please be case insensitive;
Is there any way to customize the mapping the field name from mobj to csobj, like define "_id" --> UserId, "Ip Addr" --> "IpAddr";
Is there any way to customize the datatype, let the datatype BinData(0,""), BinData(1,""), BinData(2,""), ISODate("") are all could be convert into string without System.FormatException;
Is there any way to dealing whole complex sub-object mapping to C# string regardless its fields? Since its dynamic in schema less mongodb and I could not predefine any unknown field in the sub-ojbects.
This attribute will map field names from mobj to csojo `[BsonElement(#"52WeekChange")]
You have a few options, which are explored here, in the docs. (The following examples come from there.)
You can use a ClassMap:
BsonClassMap.RegisterClassMap<MyClass>(cm => {
cm.MapProperty(c => c.SomeProperty);
cm.MapProperty(c => c.AnotherProperty);
});
If you prefer to define mappings in your c# type declaration, you can use attributes.
BsonContructor can be used to map properties during object construction:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
// You can use this on one or more overloads as well:
[BsonConstructor]
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
}
BsonElement is a property-level solution:
public class MyClass {
// The first parameter ("sp") is an optional mongodb field name mapping.
[BsonElement("sp")]
public string SomeProperty { get; set; }
}

get json object and parse it into a C# object

json:
[{
"PersonsTable":[
{"id":293,"firstname":"jos","lastname":"don"},
{"id":1861,"firstname":"jef","lastname":"dan"},
{"id":1896,"firstname":"janine","lastname":"din"}]
}]
code:
List<Person> persons = new List<Person>();
dynamic dynObj = JsonConvert.DeserializeObject(response);
foreach (var data in dynObj.PersonsTable)
{
Person p = new Person(data.id, data.firstname, data.lastname);
persons.Add(p);
}
Object:
public class Person
{
public Person ()
{
}
public Person (string id, string firstname, string lastname)
{
this.id= id;
this.firstname = firstname;
this.lastname = lastname;
}
public string id{ get; set; }
public string firstname{ get; set; }
public string lastname{ get; set; }
}
I want to put the data under "PersonsTable" into the person list.
I have tried to achieve this with serialize and dynamic variables but i always get a weird error "Missing compiler required member, 'microsoft.CSharp.RUntimeBinder.CSharpArgumentINfo.Create'"..
The NuGet package itself i can't install because my project runs in .Net 3.5 (for some reason).
Can someone help me with my problem? Are there other ways to get a list of persons in result?
Your problem is not related to json parsing I think.
As you are using the keyword "dynamic", you must have in your project a reference to Microsoft.CSharp.dll.
See here for example : C# dynamic compilation and "Microsoft.CSharp.dll" error
update :
I see you have updated your question since I've posted my answer. You now say you are running in .Net 3.5.
To be clear, dynamic is NOT AVAILABLE in .Net 3.5. See Use of Dynamic Keyword in .Net 3.5 for example.
You have a few problems:
The names of the properties of your c# classes do not match the property names in the JSON. (Note - fixed in the edited version of your question.)
Your root JSON container is an array containing a single object, not the object itself. You need to account for the extra level of nesting when parsing.
You say you are running on .Net 3.5 which does not support dynamic.
Rather than using dynamic, you can explicitly parse to a JToken then manually map to your Person type using LINQ to JSON with SelectTokens():
var root = JToken.Parse(response);
var persons = root
// Use the JSONPath wildcard operator to select all entries in the "PersonsTable"
.SelectTokens("[*].PersonsTable[*]")
// And map the individual entry to a Person type.
.Select(data => new Person((string)data["id"], (string)data["firstname"], (string)data["lastname"]))
.ToList();
Even if you were able to use dynamic, by doing so you lose compile-time error checking. Using statically defined methods may lead to fewer unexpected run-time errors.
Sample fiddle.
Create new viewModel with field List PersonsTable {get; set;}, then accept it on endpoint, it will automatically map the model, altought you might have to add [JsonProperty(PropertyName = "id")], to your Person class members for proper mapping.

How to extract an object name

i've got a class filled with lists of subclasses:
public class ClassOfKb
{
public List<Data> KbDatas {get;set;}
public List<Product> KbProducts {get;set}
}
public class Data
{
public Guid ID {get;set;}
public byte[] data {get;set;}
public string Name {get;set;}
}
public class Product
{
public Guid ID {get;set;}
public string Name {get;set;}
public byte[] Image {get;set;}
}
i create an object:
ClassOfKb kb = new ClassOfKb
now i'd like to extract the string "Datas" from the sub-object kb.KbDatas, I tried:
string name = kb.KbDatas.GetType().BaseType.Name.Substring(2);
aswell as:
string name = kb.KbDatas.GetType().Name.Substring(2);
but nothing gave me what I need, is there any way to do this?
EDIT: to specify my question, the string I need is the name of the list, except the first two letters! KbDatas => Datas
EDIT2: i did a mistake, the list-names and class-names are different and i need the list-name
You can use Type.GetGenericArguments to solve this
ClassOfKb kb=new ClassOfKb();
kb.KbData = new List<Data>();
string nameOfData = Type.GetType(kb.KbData.ToString()).GetGenericArguments().Single().Name;
OUTPUT : nameOfData = Data
kb.KbProduct = new List<Product>();
string nameOfProduct = Type.GetType(kb.KbProduct.ToString()).GetGenericArguments().Single().Name;
OUTPUT : nameOfProduct = Product
Since that's a collection it is likely that there are multiple Data objects in it, each with a name. You can use String.Join to concat them with a separator:
string names = string.Join(",", kb.KbData.Select(d => d.Name));
If there's just one object you don't get a comma at the end. If there's no object you get an empty string.
erm, since you have a List of Data there will be a sequence of Names.
IEnumerable<string> names = kb.KbData.Select(d => d.Name);
maybe you want just the first one?
string firstName = kb.KbData.First(d => d.Name);
Try this one
string name = kb.KbData[0].Name.Substring(2);
From the sounds of what you've written, you're looking to get the name of the type in the List instance KbData?
If so, I think this may be what you're looking for: https://stackoverflow.com/a/1043778/775479
If you are trying to get the name of the property. There are several methods for doing so.
Get the name of the generic argument from the property itself - If you know the name of the property.
ClassOfKb kb = new ClassOfKb()
{ KbData = new List<Data>(), KbProduct = new List<Product>() };
Console.WriteLine(kb.KbData.GetType().GetGenericArguments()[0].Name);
Get the name of the property from reflection, if you know the data type of the property.
System.Reflection.PropertyInfo pi = kb.GetType()
.GetProperties()
.FirstOrDefault(p=>p.PropertyType == typeof(List<Data>));
Console.WriteLine(pi.Name.Substring(2)); // ignoring the kb prefix
You can achieve this with reflection. This is example without any checks - just show the mechanism:
PropertyInfo propertyInfo = typeof(ClassOfKb).GetProperty("KbData");
Type propertyType = propertyInfo.PropertyType;
Type genericArgument = propertyType.GenericTypeArguments[0];
string name = genericArgument.Name;
Because property KbData is generic List<Data> you need ask for generic arguments of property type: propertyType.GenericTypeArguments[0] and you should test if the type is really generic by genericArgument.IsGenericType and check generic arguments count
If you need the property name than you can use Expression.
The code below define function for extract name prom a property:
public string GetPropertyName<T>(Expression<Func<T>> property)
{
return ((MemberExpression)property.Body).Member.Name;
}
This converts property to property name string:
GetPropertyName(()=>k.KbDatas).Substring(2)

How to determine the object property name dynamically

I have an object User:
public class User
{
public String Name { get; set; }
public String Name2 { get; set; }
public String Name3 { get; set; }
}
Also I have a Key Value set of strings that I want to assign to the object's properties when I create it:
'Name':'srgrgsdfsdf'
'Name3':'dsfdsfafafd'
'Name2':'dtewtwerwer'
'Name4':'546353452552'
Now I create my object like this
User user = new User();
user.Name = "zzxasdas";
The problem is that I want to be able to assign the Key/Value data dynamically. This means that some of the items can be missing or the order may be different.
How can I check the name of the object properties dynamically and compare it with the Key like this:?
foreach [key] in Key/Value
if [user] has property named [key]
add [value] to [user] property with the name [key]
You would need to use Reflection to do this.
In particular, Type.GetProperties or Type.GetProperty will allow you to discover information about the properties defined on a type, and get or set their values.
This might look something like:
User user = new User();
Type t = user.GetType();
foreach(var kvp in propertyDictionary)
{
var prop = t.GetProperty(kvp.Key);
if (prop != null)
prop.SetValue(user, kvp.Value);
}
There are two things that comes to my mind.
One is obviously using Reflection APIs. For example, if user.GetType().GetProperty() etc.
Second, there might be a chance that AutoMapper is a solution for you which copies property from one object to another with same name

C# - Dynamically generated classes?

I am in need of creating a dynamically extend-able class in C#.
The goal is to create a class what can contain all info from a given contact from an Android SQLite Contacts table. The table's structure is kinda weird, as it does not have set field names, but uses colums of 'field name' and 'field content'.
That's what I want to turn into a usable format where the code reads the database, and for each entry creates the matching sub-variable. Such I want to know the best method to do so (I guess a simple
{
this.(variableNames[i].ToString()) = variableContent[i];
}
will not do it), what is the least resource-eating, but fastest (and easiest) way.
And also if we are here, is there ANY method to call a type's (let's say, I create a new Contact with e-mail, workplace, name, and image tags, but these variables names' are unknown) ALL sub-variables (Contact.image, Contact.FirstName, Contact.Email, etc) dynamically?
Of course there will be standardized fields what should be in ALL contact (one of the three names, phone number, e-mail #work and #home, and such), but these should be called dynamically too.
Use a Dictionary<string,string> instead.
Dictionary<string,string> contactInfo = new Dictionary<string,string>();
public void ImportContact()
{
...
// for each fieldName and fieldValue from your table
contactInfo.Add(fieldName, fieldValue);
...
// check that all standard fields are present, if desired
}
public string FirstName
{
get { return contactInfo["FirstName"]; }
}
If you are willing to go with dynamic typing, you can use the dynamic type in C# 4. You can use ExpandoObject or DynamicObject as a base for your Contact types.
Here is an example of a Contact class that can work both statically typed with some pre-defined properties; and can have properties attached to it at run-time. When treating it statically, you can still get the values by using the indexer:
class Contact : DynamicObject
{
private readonly Dictionary<string, object> bag = new Dictionary<string, object>();
public string FirstName { get; set; }
public string LastName { get; set; }
public object this[string key]
{
get { return bag[key]; }
set { bag[key] = value; }
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (bag.ContainsKey(binder.Name))
{
result = bag[binder.Name];
return true;
}
return base.TryGetMember(binder, out result);
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
bag[binder.Name] = value;
return true;
}
}
Which you can then use like this:
// Contact is statically typed.
Contact c = new Contact();
c.FirstName = "test";
// Treat as dynamic and attach some extra properties:
dynamic dynContact = c;
dynContact.AddressOne = "Somewhere";
dynContact.AddressTwo = "Someplace else";
Console.WriteLine(dynContact.AddressOne);
Console.WriteLine(dynContact.AddressTwo);
Other than using dynamic, you cannot create a new class with dynamically typed properties. After all, how would you consume those properties ? You might be better off creating a class containing the properties that you must have; and put the rest in a Dictionary<string,object>.
If you're using .NET 4.0, there's dynamic support. You can create objects something like this:
var newContact = new object { FirstName = "name", LastName = "name", etc... };
Alternatively, you might want to try using a Dictionary.

Categories