Getting a dictionary full path to specific key using reflection - c#

I have a situation where I have a nested IDictionary, like this;
class SomeObject {
public IDictionary<string, IDictionary<string, decimal>> Entries { get; set; }
}
So, an object might look like this;
var o = new SomeObject {
Entries = new ... {
["first"] = new ... {
["a"] = 1.0,
["b"] = 2.0
},
["second"] = new ... {
}
}
};
I'm trying to figure out how to get the "path" to one of the nested keys as a collection of strings using a lambda expression. For example ...
class SomeFactory {
public SomeFactory Use<T>(T entity) {
this.Model = entity; return this;
}
public IEnumerable<string> Get<TProperty>(Expression<Func<T, IEnumerable<TProperty>>> selector) {
var properties = ... // get list of properties from [selector] //
}
}
var factory = new SomeFactory();
var list = factory.Use<SomeObject>(o).Get(n => n.Entries["first"]["b"]);
Where the result would be an IEnumerable<string> that had the values ...{"Entries","first","b"}.
Is this possible to do?
Use Case
The reason I am wanting to do this is because I'm working with a library that can issue out commands in a certain way using an object that looks like this (pseudo code);
class Patch {
string Property;
object Value;
Patch[] Nested;
}
Each Patch can take the name of 1 property on an object deserialized from a given point. It will perform a very fast operation to it, considerably faster than loading the entire object from the database, updating it, and saving it again. This is important for a number of reasons at various parts of the program. (This is not an SQL database)
If a nested patch is given, it will not set the value on the top-level property, but instead find the top level property and use it as the launching point to execute the next Patch in the array. This process continues until it gets to the last one, whereupon it makes the change.
So in order to issue a patch to an IDictionary, the whole object graph needs to look something akin to ...
{
"Property": "Entries",
"Nested": [
{
"Property": "first",
"Nested": [
{
"Property": "b",
"Value": 7.0
}
]
}
]
}
This isn't a problem, but it's a bothersome graph to draw up each time. My thought was to make the entire process simpler by being able to construct this graph from just a lambda expression that found the desired destination on the IDictionary object; i.e f(n => n["first"]["b"])

The code below fits your scenario above:
public static IEnumerable<string> Get<T>(Expression<Func<T, object>> selector)
{
var list = new List<string>();
Expression exp = (selector.Body as UnaryExpression).Operand as MethodCallExpression;
while (exp is MethodCallExpression)
{
var call = exp as MethodCallExpression;
var arg = call.Arguments[0].ToString();
if(call.Arguments[0].Type == typeof(string))
{
arg = arg.Substring(1, arg.Length - 2);
}
list.Add(arg);
exp = call.Object as Expression;
}
var member = exp as MemberExpression;
list.Add(member.Member.Name);
list.Reverse();
return list;
}
static void Main(string[] args)
{
var graph = Get<SomeObject>(o => o.Entries["first"]["b"]);
foreach (var node in graph)
{
Console.WriteLine(node);
}
Console.ReadLine();
}
Output is:
Entries
first
b

Related

Reflection: how to get values from object as List<object>

Using reflection, I need a function that accepts a list of generic objects and print its values
List<Gender> genders = new List<Gender> {
new Gender {
Id: 1,
Name: "Male"
},
new Gender {
Id: 2,
Name: "Female"
}
}
PrintListValues(genders, "Name"); // prints Male, Female
PrintListValues has to accept a list as object. And the list can contain any type of object. As long as the generic object have the property passed in, it should print the value of each item in list.
public static void PrintValues(object listObject)
{
// code here
}
Unfortunately that's the way the project requirement is.
Been scratching my head over this one and can't figure it out. Reflection is too difficult for me. If anyone can give me hint that would be great!
Try this:
public static void PrintListValues(IEnumerable<object> seq, string propName)
{
foreach (var obj in seq)
{
var type = obj.GetType();
var prop = type.GetProperty(propName);
var value = prop.GetValue(obj);
Console.WriteLine(value);
}
}
Disclaimer: To play with reflection, the above code is fine. For a real world situation, there might be a few things to check as that code assumes the "happy path".
You likely just need
var values = anyList.Select(x => x.SomeProperty);
Console.WriteLine(string.Join(Environemnt.NewLine,values));
However if you really need to include a string property name
public IEnumerable<object> GetValues<T>(IEnumerable<T> source, string name)
{
var prop = typeof(T).GetProperty(name) ?? throw new ArgumentNullException("typeof(T).GetProperty(propName)");
return source.Select(x => prop.GetValue(x));
}
...
foreach (var value in GetValues(someList,"SomeProperty"))
Console.WriteLine(value);
// or
Console.WriteLine(string.Join(Environemnt.NewLine,GetValues(someList,"SomeProperty"));

How to address an object from a json array

Using the JSON library, I've imported an array of objects:
dynamic serviceList = JsonConvert.DeserializeObject(listString)
I've got an array of json objects, each has the property "name".
[
{
"name":"abcd",
"properties":{
"type":"1234"
}
},
{
"name":"xyz",
"properties":{
"type":"aaaa"
}
}
]
How do I address just the object "abcd"?
You can parse your json Array using Newtonsoft.Json JArray.Parse() function.
Use FirstOrDefault() to get record where name is "abcd"
string listString = #"[{'name': 'abcd','properties': {'type': '1234'}},{'name': 'xyz', 'properties': { 'type': 'aaaa'}}]";
JArray jArray = JArray.Parse(listString);
//FirstOrDefault to get first record which satisfy the condition
var result = jArray.FirstOrDefault(x => (string)x["name"] == "abcd");
Console.WriteLine(result);
Output:
{
"name": "abcd",
"properties": {
"type": "1234"
}
}
.Net Fiddle
The right way to solve the problem is create a static Object
Public Obj1 {
public string name {get;set;}
public Properties properties {get;set;}
}
Public Properties {
public string type {get;set;}
}
Then you can deserialize the JSON into a List<Obj1>, in this way you can iterate your list and find the "name":"abcd" Object. var myobj = mylist.FirstOrDefault(x=> x.name == "abcd")
You can use the dynamic to do pretty much anything you would like it to do, so nothing would stop you from evaluating your result in the following way:
dynamic selectionList = JsonConvert.DeserializeObject( json );
foreach (var item in selectionList) {
if ( string.Equals( (string)item.name, "abcd", StringComparison.OrdinalIgnoreCase ) ) {
Console.WriteLine( item );
}
}
This would work as per your original request, but I think you are making it a lot harder on yourself than need be :)
To see how this works, you could check this dotnetfiddle
I would probably create a class based on the spec, but I am assuming that properties is a dynamic list of properties and their values, meaning you would still end up with a Dictionary<string, object> in the end
You can access it like this:
for (int i = 0; i < serviceList.Count; i++)
{
if (serviceList[i].name == "abc")
{
DoSomethingWith(serviceList[i];
break;
}
}
Edit: didn't see that you wanted the "abc" element, so modified the code accordingly.
This will get the value for you as a JObject, from which you can access the contained values:
var serviceList = JArray.Parse(listString);
var target = serviceList.Single(s => s["name"].ToString() == "abcd");
if (target != null)
{
var type = target["properties"]["type"];
// etc
}

When merging objects using Newtonsoft.Json, how do you ignore empty string values?

I have a data model that is defined as a class in C#. I need to merge two objects using JObject.Merge, but in the case of one particular property I need to ignore empty string values from the 2nd object, similar to how you would ignore null values. Is there an existing attribute property for this, or do I somehow need to write my own?
void Main()
{
string json1 = #"{ Foo: ""foo1"", Bar: ""bar1"" }";
string json2 = #"{ Foo: ""foo2"", Bar: null }";
string json3 = #"{ Foo: ""foo3"", Bar: """" }";
var model1 = JObject.Parse(json1);
var model2 = JObject.Parse(json2);
model1.Merge(model2);
model1.Dump();
model1 = JObject.Parse(json1);
model2 = JObject.Parse(json3);
model1.Merge(model2);
model1.Dump();
}
public class Model
{
[JsonProperty("foo")]
public string Foo { get ; set; }
[JsonProperty("bar", NullValueHandling = NullValueHandling.Ignore)]
public string Bar { get ; set; }
}
Output (1): Model.Bar = "bar1"
Output (2): Model.Bar = "";
Desired output of (2): Model.Bar = "bar1"
EDIT: OK, I realized that attributes could not be applied since I needed to work with the raw json string only as input. This was mainly due to the fact that my classes had attributes with default values. Merging classes with empty values would trigger the defaults and end up overwriting the original, which is what I didn't want. I need to be able to take a partial json representation of a class and update the original. Sorry if that wasn't clear from the get-go. I'll update what I ended up doing an answer.
You could remove the properties with empty string values from the JObject you want to merge in, using the following extension methods:
public static class JsonExtensions
{
public static void RemovePropertiesByValue(this JToken root, Predicate<JValue> filter)
{
var nulls = root.DescendantsAndSelf().OfType<JValue>().Where(v => v.Parent is JProperty && filter(v)).ToList();
foreach (var value in nulls)
{
var parent = (JProperty)value.Parent;
parent.Remove();
}
}
public static IEnumerable<JToken> DescendantsAndSelf(this JToken node)
{
if (node == null)
return Enumerable.Empty<JToken>();
var container = node as JContainer;
if (container != null)
return container.DescendantsAndSelf();
else
return new [] { node };
}
}
Then use it like:
model2.RemovePropertiesByValue(v => v.Type == JTokenType.String && string.IsNullOrEmpty((string)v.Value));
Note this doesn't remove empty strings from arrays because that would mess up array indexing, and therefore merging. Do you need that also?
I manipulated the JObject dictionary keys to massage the specific entry. I feel dirty, but it works. I can't think of a "good" way to do this.
model1 = JObject.Parse(json1);
model2 = JObject.Parse(json3);
IDictionary<string, JToken> dictionary = model2;
dictionary["Bar"] = string.IsNullOrEmpty((string)dictionary["Bar"])
? null
: dictionary["Bar"];
model1.Merge(model2);

C# Expression Serialization / Parameter Replacement

First, a quick overview on what I'm trying to do: I want to take a C# expression, serialize it, send it to another process, de-serialize it, and use it to filter a list. Here's the caveat - when the expression is created it is done so going against a generic parameter type T but when it is de-serialized it needs to go against a dynamic instead. The reason for this is that when it is eventually used on a server in a different process it will be done so against a list of dynamics as I will not have type information in that context.
I feel like I'm close as I've used the Newtonsoft.Json and Serialize.Linq libraries to put together a proof of concept but I can only get it to work without using dynamics (e.g. I have the type T available on both the serializing side (client) and the de-serializing side (server). On to some code to show you what I have...
Given this:
public class Person
{
public string Name { get; set; }
public string Email { get; set; }
}
...as the type we are working with. I have a client that has interface:
public interface IClient
{
T Get<T>(Expression<Func<T, bool>> query);
}
...so that I can do this:
var client = new Client();
var john = client.Get<Person>(p => p.Name == "John");
...all is well and good so far. In the client.Get() method I am taking the passed expression and serializing it down and sending to the server. The server looks as such:
public dynamic Server(string serializedExpression)
{
var people = new List<dynamic>
{
new { Name = "John", Email = "john#stackoverflow.com" },
new { Name = "Jane", Email = "jane#stackoverflow.com" }
};
var serializer = new ExpressionSerializer(new JsonSerializer());
var deserializedExpression = (Expression<Func<dynamic, bool>>)serializer.DeserializeText(serializedExpression);
return people.FirstOrDefault(deserializedExpression.Compile());
}
...and here is where the problems happen because I'm trying to deserialize it into a type of
Expression<Func<dynamic, bool>>
...instead of...
Expression<Func<Person, bool>>.
So my questions are:
1) Is what I'm trying to do even possible? It seems like using an ExpressionVisitor you can change the generic parameter types and I've tried to do so to change from Person to dynamic before I serialize and send but have had no luck.
2) Is there a better way to do what I want to accomplish? I know the first question will be why don't I just get access to the type T specified in the expression Func<> on the server but that won't be possible due to the nature of the server. I basically want the luxury of using Linq on the client to specify query predicates while executing those predicates against dynamic lists.
Thanks in advance for any answers or ideas you can provide.
Regards,
Craig
LINQ doesn't like dynamics in Expressions much. (DLINQ perhaps?)
Alternatively, you could pass along a hint to the server about which object type you're using. I realize this is probably not what you're looking for, but it is working:
(borrowed from this CodeProject article)
public static class Extensions
{
public static object ToType<T>(this object obj, T type)
{
//create instance of T type object:
var tmp = Activator.CreateInstance(Type.GetType(type.ToString()));
//loop through the properties of the object you want to covert:
foreach (PropertyInfo pi in obj.GetType().GetProperties())
{
try
{
//get the value of property and try
//to assign it to the property of T type object:
tmp.GetType().GetProperty(pi.Name).SetValue(tmp,
pi.GetValue(obj, null), null);
}
catch { }
}
//return the T type object:
return tmp;
}
public static object ToNonAnonymousList<T>(this List<T> list, Type t)
{
//define system Type representing List of objects of T type:
var genericType = typeof(List<>).MakeGenericType(t);
//create an object instance of defined type:
var l = Activator.CreateInstance(genericType);
//get method Add from from the list:
MethodInfo addMethod = l.GetType().GetMethod("Add");
//loop through the calling list:
foreach (T item in list)
{
//convert each object of the list into T object
//by calling extension ToType<T>()
//Add this object to newly created list:
addMethod.Invoke(l, new object[] { item.ToType(t) });
}
//return List of T objects:
return l;
}
}
... with some not-so-nice branching on the server:
public interface IClient
{
T Get<T>(Expression<Func<T, bool>> query);
}
public class Person
{
public string Name { get; set; }
public string Email { get; set; }
}
public class Client
{
public T Get<T>(Expression<Func<dynamic, bool>> query)
{
var serializer = new ExpressionSerializer(new JsonSerializer());
var serializedExpression = serializer.SerializeText(query);
return (T)Server.Retrieve(serializedExpression, typeof(T).FullName);
}
}
public static class Server
{
public static dynamic Retrieve(string serializedExpression, string targetType)
{
var people = new List<dynamic>
{
new { Name = "John", Email = "john#stackoverflow.com" },
new { Name = "Jane", Email = "jane#stackoverflow.com" }
};
// Try creating an object of the type hint passed to the server
var typeInstance = Activator.CreateInstance(Type.GetType(targetType));
if (typeInstance.GetType() == typeof(Person))
{
var serializer = new ExpressionSerializer(new JsonSerializer());
var deserializedExpression = (Expression<Func<Person, bool>>)serializer.DeserializeText(serializedExpression);
var peopleCasted = (IEnumerable<Person>)people.ToNonAnonymousList(typeof(Person));
return peopleCasted.Where(deserializedExpression.Compile()).SingleOrDefault();
}
else
{
throw new ArgumentException("Type is unknown");
}
}
}
and a working test:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void MyTestMethod()
{
var client = new Client();
var john = client.Get<Person>(p => p.Name == "John");
Assert.IsNotNull(john);
}
}

Given this lambda, how can I write it manually with expression trees?

class Program
{
static void Main(string[] args)
{
Expression<Func<string[], Poco>> exp = a => new Poco { MyProperty1 = a[0], MyProperty2 = a[1], MyProperty3 = a[2] };
var lambda = exp.Compile();
var output = lambda(new[] {"one", "two", "three"});
Console.WriteLine(output.MyProperty1);
}
}
class Poco
{
public string MyProperty1 { get; set; }
public string MyProperty2 { get; set; }
public string MyProperty3 { get; set; }
}
I'm not interested in the part calling the lambda, thats just for completeness. I get completely lost trying to navigate expression trees, and this might teach me how to fish.
private static Expression<Func<string[], Poco>> CreateExpr()
{
ParameterExpression paramExpr = Expression.Parameter(typeof(string[]), "a");
var newExpr = Expression.New(typeof(Poco));
var memberExprs = Enumerable.Range(0, 3)
.Select(i =>
{
string propertyName = "MyProperty" + (i + 1);
var property = typeof(Poco).GetProperty(propertyName);
Expression.Bind(property, Expression.ArrayIndex(paramExpr, Expression.Constant(i)));
});
var expr = Expression.MemberInit(newExpr, memberExprs);
return Expression.Lambda<Func<string[], Poco>>(expr, paramExpr);
}
I don't have time right now to translate the complete tree, but one thing you can do is compile your code and then use ildasm (or reflector etc) to look at what the compiler's doing. You can't always do exactly the same in your own code, but it gives you an idea of the kind of expressions you'll want. In particular, in this case you'll want:
Expression.Parameter to create the parameter (a)
Expression.New to create the new instance
Expression.Bind to create a property assignment
Expression.MemberInit to assign the properties in the new object
Expression.ArrayIndex for each array access (a[0] etc)
Expression.Constant for the array indexes themselves (0, 1, 2)
Expression.Lambda to create an Expression<TDelegate> for the whole thing
If I get time later on, I'll try to construct a complete working example.

Categories