.NET - Whitelisting properties to anonymous object - c#

I want to create an anonymous object dynamically based on an existing object and a whitelist.
For example I have the following class:
class Person
{
string FirstName;
string LastName;
int Age;
}
Now I have created a function FilterObject to create a new anonymous obj based on the whitelist parameter like this:
public static class Filter
{
public static object FilterObject(Person input, string[] whitelist)
{
var obj = new {};
foreach (string propName in whitelist)
if (input.GetType().GetProperty(propName) != null)
// Pseudo-code:
obj.[propName] = input.[propName];
return obj;
}
}
// Create the object:
var newObj = Filter.FilterObject(
new Person("John", "Smith", 25),
new[] {"FirstName", "Age"});
The result should be like below. I want to use this object for my web API.
var newObj = new
{
FirstName = "John",
Age = 25
};
Is there any way to achieve this?

You can try with ExpandoObject (.net 4 or superior):
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
static class Filter
{
public static object FilterObject(Person input, string[] whitelist)
{
var o = new ExpandoObject();
var x = o as IDictionary<string, object>;
foreach (string propName in whitelist)
{
var prop = input.GetType().GetProperty(propName);
if (prop != null)
{
x[propName] = prop.GetValue(input, null);
}
}
return o;
}
}
This is only a sample based on your code, but it's a good starting point.

What about using a Dictionary?
public static object FilterObject(Person input, string[] whitelist)
{
var obj = new Dictionary<string, object>();
foreach (string propName in whitelist)
{
var prop = input.GetType().GetProperty(propName);
if(prop != null)
{
obj.Add(propName, prop.GetValue(input, null));
}
}
return obj;
}
Another thing, do you really need to return an object? Because if you are always checking for properties in the whitelist that exists in the Person type, why just not return an instance of the Person class?

Related

How to pass Object as a generic function parameter?

I need to write a function which takes whichever Object as a parameter, iterate through its properties and write it all to the console. Here is an example:
Equipment.cs
public class Equipment
{
public string SerialNo { get; set; }
public string ModelName { get; set; }
}
People.cs
public class People
{
public string Name { get; set; }
public string Age{ get; set; }
}
Here is example of what I return to above models from API:
var equipment_res = responseObject?.items?.Select(s => new Equipment
{
SerialNo = s.serial_number,
ModelName = s.model.name,
});
var people_res = responseObject?.items?.Select(s => new Equipment
{
SerialNo = s.serial_number,
ModelName = s.model.name,
});
And now I'm struggling to write a function which can take any object and writes its properties to the console. I don't know how to properly pass objects to function in this case:
public void WriteProps(Object obj1, Object obj2)
{
foreach (Object obj1 in obj2)
{
Object obj1 = new Object();
foreach (PropertyInfo p in obj1)
{
Console.WriteLine(p.Name);
Console.WriteLine(p.GetValue(obj1, null));
}
}
}
Function call:
WriteProps(Equipment, equipment_res)
EDIT: below there's a working example but when I explicitly pass named object. It works fine but now I would like to make this function more genric:
foreach (Equipment item in equipment)
{
Equipment eq = new Equipment();
eq = item;
foreach (PropertyInfo p in eq)
{
Console.WriteLine(p.Name);
Console.WriteLine(p.GetValue(eq, null));
}
}
make your method generic and then use reflection (System.Reflection):
void WriteProps<T>(T obj)
{
foreach (var prop in typeof(T).GetProperties())
{
Console.WriteLine(prop.Name);
Console.WriteLine(prop.GetValue(obj));
}
}
Use:
WriteProps(new People
{
Name = "Test",
Age = "11"
});
WriteProps(new Equipment
{
ModelName = "test",
SerialNo = "test"
});
UPDATE:
I'd add this method to work with IEnumerable objects:
void WritePropsList<T>(IEnumerable<T> objects)
{
foreach (var obj in objects)
{
WriteProps(obj);
}
}

How do I use reflection to access an IEnumerable member within a type?

How do I access IEnumerable members within a type.
In this case, the Likes property is never exposed in the Retrieve method of the References class.
[Test]
public void Test1()
{
// Setup
var person = new Person("some_id", "John", "Doe", new List<string>{ "Dogs" });
var persons = new List<Person> () { person };
var valuePredicates = new List<Predicate<string>>() { v => v.EndsWith("Id") };
// Test
var references = References.Retrieve(person, new List<KeyValuePair<string,string>>(), valuePredicates);
}
}
public static class References
{
public static IEnumerable<KeyValuePair<string, string>> Retrieve(object obj, List<KeyValuePair<string, string>> entries, IEnumerable<Predicate<string>> predicates)
{
var type = obj.GetType();
if (type.IsPrimitive || type == typeof(string)) return entries;
var structure = type.Name;
foreach (PropertyInfo property in type.GetProperties(
BindingFlags.GetProperty |
BindingFlags.Public |
BindingFlags.Instance).Where(
x => x.CanRead &&
x.GetGetMethod(false) != null &&
x.GetIndexParameters().Length == 0))
{
var getMethod = property.GetGetMethod(true);
if (getMethod != null)
{
foreach (var isPropertyOfName in predicates)
{
if (isPropertyOfName(property.Name))
{
var identifier = $"{structure}::{property.Name}";
var textValue = property.GetValue(obj) as string;
entries.Add(new KeyValuePair<string, string>(identifier, textValue));
}
}
Retrieve(getMethod.Invoke(obj, null), entries, predicates);
}
}
return entries;
}
Appendix:
public class Person
{
public Person(string id, string firstName, string lastName, IEnumerable<string> likes) =>
(Id, FirstName, LastName, Likes) = (id, firstName, lastName, likes);
public string Id { get; }
public string FirstName { get; }
public string LastName { get; }
public IEnumerable<string> Likes { get; }
}

C# Reflection Get List of Object

I have a problem fetching object from the array object that I made. It seems it didn't fetch the object see my code below:
Product Model
public class Product
{
public string Id { get; set; }
public List<ExcelName> ShortDesc { get; set; } // I want to get the object from here
}
Short Description Model
// get this object and the properties inside it.
public class ExcelName
{
public string Name { get; set; }
public string Language { get; set; }
}
My Code
private static T SetValue<T>(Dictionary<string, object> objectValues)
{
var type = typeof(T);
var objInstance = Activator.CreateInstance(type);
if (!type.IsClass) return default;
foreach (var value in objectValues)
{
if (value.Key.Contains(":Language="))
{
var propName = value.Key.Split(':')[0];
// propName is ShortDesc object
var propInfo = type.GetProperties(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(e => e.Name.ToLower() == propName.ToLower().Replace(" ", ""));
if (propInfo == null) continue;
if (propInfo.PropertyType.IsGenericType)
{
// I want to get the type and properties from T generic using reflection instead static
var name = typeof(ExcelName);
var excelNameObjectInstance = Activator.CreateInstance(name);
foreach (var propertyInfo in name.GetProperties())
{
propertyInfo.SetValue(excelNameObjectInstance, value.Value, null);
}
// add excelNameObjectInstance object to the list in ShortDesc
}
}
}
}
How to fetch the object from the list of ShortDesc to get the ExcelName objects.
I'm not quite sure what you're trying to do, but it seems like you want a function that instantiates a T and sets its properties according to a dictionary.
Half your code doesn't make sense to me, but my guess is correct, you shouldn't need anything more complicated than this:
private static T SetValue<T>(Dictionary<string, object> objectValues) where T : class, new()
{
var type = typeof(T);
var instance = new T();
foreach (var entry in objectValues)
{
type.GetProperty(entry.Key).SetValue(instance, entry.Value);
}
return instance;
}
If you don't expect the keys to be an exact match for property names, you can introduce a normalization function:
private static string Normalize(string input)
{
return input.ToLower().Replace(" ", "");
}
private static T SetValue<T>(Dictionary<string, object> objectValues) where T : class, new()
{
var type = typeof(T);
var instance = new T();
foreach (var entry in objectValues)
{
var prop = type.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.First( x => Normalize(x.Name) == Normalize(entry.Key) );
prop.SetValue(instance, entry.Value);
}
return instance;
}

Reflection to get List<object> data

I'm trying to loop through a DetailClass objects inside a List using reflection just like for string fields, but I can't figure out how.
class DetailClass
{
public string FieldDetail1 { get; set; }
public string FieldDetail2 { get; set; }
public string FieldDetail3 { get; set; }
}
class SomeClass
{
public string Field1 { get; set; }
public string Field2 { get; set; }
public string Field3 { get; set; }
public List<DetailClass> Artikli { get; set; }
}
private static PropertyInfo[] GetProperties(object obj)
{
return obj.GetType().GetProperties();
}
var myData = new SomeClass();
var prop = GetProperties(myData);
foreach (var item in prop)
{
if (item.PropertyType == typeof(string))
{
var name = item.Name,
var value = item.GetValue(myData).ToString()));
}
//how to get name and value for data inside List<DetailClass>?
}
You were trying to enumerate properties of the parent class
GetValue needs the a reference to the class you are dealing with
Code
var myData = new SomeClass();
myData.Artikli = new List<DetailClass>() { new DetailClass() { FieldDetail1 = "asd", FieldDetail2 = "sdfd", FieldDetail3 = "sdfsg" } };
foreach (var obj in myData.Artikli)
{
foreach (var item in obj.GetType().GetProperties())
{
if (item.PropertyType == typeof(string))
{
var name = item.Name;
var val = item.GetValue(obj);
Console.WriteLine(name + ", " + val);
}
}
}
Demo Here
Additional Resources
PropertyInfo.GetValue Method (Object)
Returns the property value of a specified object.
Parameters
obj
Type: System.Object
The object whose property value will be returned.
You can use your method recursively to get inside all layer of properties
You can check if
item.PropertyType.GetInterfaces().Contains(typeof(IEnumerable))
and if true cast (IEnumerable)item.GetValue(myData) and iterate on the result
recursively.
Just like TheDude answered, you can use a recursive method like so;
private void Recursion(object obj)
{
var props = GetProperties(obj);
foreach (var item in props)
{
if (item.PropertyType == typeof(string))
{
var name = item.Name;
var value = item.GetValue(obj)?.ToString();
}
else if (item.PropertyType == typeof(List<DetailClass>))
{
var test = (List<DetailClass>) item.GetValue(obj);
foreach (var t in test)
{
Recursion(t);
}
}
}
}
And do whatever you want with the name and values in the list.

How to use attribute to map properties

I have the following code.
public class SyncProperty : Attribute
{
public readonly string PropertyName;
public SyncProperty(string propertyName)
{
this.PropertyName = propertyName;
}
}
public class SyncContact
{
[SyncProperty("first_name")]
public string FirstName { get; set; }
[SyncProperty("last_name")]
public string LastName { get; set; }
[SyncProperty("phone")]
public string Phone { get; set; }
[SyncProperty("email")]
public string Email { get; set; }
}
I need to create an instance of my SyncContact such as
var contact = new SyncContact { FirstName="Test", LastName="Person", Phone="123-123-1234", Email="test#test.com"};
And then with that object I need to create a NameValueCollection where the object's property uses the SyncProperty's PropertyName as the Key in the collection. Then use that to make a post request to an API. So in this case I would end up with a collection like...
collection["first_name"] = "Test"
collection["last_name"] = "Person"
collection["phone"] = "123-123-1234"
collection["email"] = "test#test.com"
How can I do that?
If you want to get some type metadata, you should use Reflection. To read attribute you can use GetCustomAttribute<AttributeType>() extension for MemberInfo.
This extension method builds sync dictionary from type properties decorated with SyncPropertyAttribute (I suggest to use the dictionary instead of NameValueCollection):
public static Dictionary<string, string> ToSyncDictionary<T>(this T value)
{
var syncProperties = from p in typeof(T).GetProperties()
let name = p.GetCustomAttribute<SyncProperty>()?.PropertyName
where name != null
select new {
Name = name,
Value = p.GetValue(value)?.ToString()
};
return syncProperties.ToDictionary(p => p.Name, p => p.Value);
}
Usage:
var collection = contact.ToSyncDictionary();
Output:
{
"first_name": "Test",
"last_name": "Person",
"phone": "123-123-1234",
"email": "test#test.com"
}
Note: if you are going to use contact data in POST request, then you should consider using simple JSON serialization attributes instead of creating your own attributes. E.g. with Json.NET:
public class SyncContact
{
[JsonProperty("first_name")]
public string FirstName { get; set; }
[JsonProperty("last_name")]
public string LastName { get; set; }
[JsonProperty("phone")]
public string Phone { get; set; }
[JsonProperty("email")]
public string Email { get; set; }
}
Then simple serialization will do the job:
string json = JsonConvert.SerializeObject(contact);
And the result will be exactly same as above.
The attributes belong to the properties of the class, so you need to get the type of the class, then find the appropriate properties and then get the custom attributes.
Something like:
var contact = new SyncContact { FirstName="Test", LastName="Person", Phone="123-123-1234", Email="test#test.com"};
var t = typeof(SyncContact);
var props = t.GetProperties().Select(p => new { p, attr = p.GetCustomAttribute<SyncProperty>() }).Where(p => p.attr != null);
var dict = props.ToDictionary(p => p.attr.PropertyName, v => v.p.GetValue(contact) );
This leaves out any properties that aren't tagged, but you can decide to handle those differently if you wanted to.
Fiddle
Assuming SyncProperty is tagged on every property, this should do the job:
var contact = new SyncContact { FirstName="Test", LastName="Person", Phone="123-123-1234", Email="test#test.com"};
var collection = contact.GetType().GetProperties()
.Select(x => new
{
x.GetCustomAttribute<SyncProperty>().PropertyName,
Value = x.GetValue(contact).ToString()
})
.ToDictionary(x => x.PropertyName, x => x.Value);
As a helper method:
public static class SynxHelper
{
public static Dictionary<string, string> Serialize<T>(T obj)
{
return typeof(T).GetProperties()
.Select(x => new
{
SyncProperty = x.GetCustomAttribute<SyncProperty>(),
Value = x.GetValue(obj)
})
.Where(x => x.SyncProperty != null)
.ToDictionary(x => x.SyncProperty.PropertyName, x => x.Value.ToString());
}
}
// usage
var collection = SynxHelper.Serialize(contact);
Here is a one line linq solution for this
(from prop in obj.GetType().GetProperties()
where prop.GetCustomAttribute<SyncProperty>() != null
select new { Key = prop.GetCustomAttribute<SyncProperty>().PropertyName, Value = prop.GetValue(obj) })
.ToDictionary(k => k.Key, v => v.Value);
BUT!!!!!!!! Don't try doing this your self. This is not optimized and slow as all reflection is.
This is just to demonstrate how bad reflection is
static void Main(string[] args)
{
var data = Enumerable.Range(0, 10000).Select(i => new SyncContact { FirstName = "Test", LastName = "Person", Phone = "123-123-1234", Email = "test#test.com" }).ToArray();
Stopwatch sw = new Stopwatch();
long m1Time = 0;
var total1 = 0;
sw.Start();
foreach (var item in data)
{
var a = ToSyncDictionary(item);
total1++;
}
sw.Stop();
m1Time = sw.ElapsedMilliseconds;
sw.Reset();
long m2Time = 0;
var total2 = 0;
sw.Start();
foreach (var item in data)
{
var a = ToSyncDictionary2(item);
total2++;
}
sw.Stop();
m2Time = sw.ElapsedMilliseconds;
Console.WriteLine($"ToSyncDictionary : {m1Time} for {total1}");
Console.WriteLine($"ToSyncDictionary2 : {m2Time} for {total2}");
Console.ReadLine();
}
public static IDictionary<string, string> ToSyncDictionary<T>(T value)
{
var syncProperties = from p in typeof(T).GetProperties()
let name = p.GetCustomAttribute<SyncProperty>()?.PropertyName
where name != null
select new
{
Name = name,
Value = p.GetValue(value)?.ToString()
};
return syncProperties.ToDictionary(p => p.Name, p => p.Value);
}
public static IDictionary<string, string> ToSyncDictionary2<T>(T value)
{
return Mapper<T>.ToSyncDictionary(value);
}
public static class Mapper<T>
{
private static readonly Func<T, IDictionary<string, string>> map;
static Mapper()
{
map = ObjectSerializer();
}
public static IDictionary<string, string> ToSyncDictionary(T value)
{
return map(value);
}
private static Func<T, IDictionary<string, string>> ObjectSerializer()
{
var type = typeof(Dictionary<string, string>);
var param = Expression.Parameter(typeof(T));
var newExp = Expression.New(type);
var addMethod = type.GetMethod(nameof(Dictionary<string, string>.Add), new Type[] { typeof(string), typeof(string) });
var toString = typeof(T).GetMethod(nameof(object.ToString));
var setData = from p in typeof(T).GetProperties()
let name = p.GetCustomAttribute<SyncProperty>()?.PropertyName
where name != null
select Expression.ElementInit(addMethod,
Expression.Constant(name),
Expression.Condition(Expression.Equal(Expression.Property(param, p), Expression.Constant(null)),
Expression.Call(Expression.Property(param, p), toString),
Expression.Constant(null,typeof(string))));
return Expression.Lambda<Func<T, IDictionary<string, string>>>(Expression.ListInit(newExp, setData), param).Compile();
}
}
On my machine I got a 10x boost in pers.
If you can use some serializer like JSON.net since you will need to change a lot of things to make it work well and you already have a tone of stuff that dose it for you.

Categories