I'm recursively iterating through an object's properties using the following method:
void GetProps(object obj)
{
if (obj == null)
return;
var objType = obj.GetType();
var properties = objType.GetProperties();
foreach (var property in properties)
{
object value = property.GetValue(obj, null);
if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
{
var enumerable = (IEnumerable)value;
foreach (object child in enumerable)
GetProps(child);
}
else
{
GetProps(value);
}
}
}
The object is very complex (over 30 classes). I'm getting a StackOverflowException when going deeper into the object at GetProps(value.
Is there a way to catch the exception and check why it's failing, and, solve the problem?
EDIT
I added a fail-safe at the top of the method:
if (visited.Contains(obj))
return;
visited.Add(obj);
The problem is not with circular references (which I don't have) but with property types such as DateTime, int and decimal. which was assuming they're primitive but the IsPrimitive property is false.
Can I differentiate between such types and my own classes?
A StackOverflowException can't be caught unless you threw it, because it indicates a fatal problem with your application.
Most likely this happens because you have a circular reference. I.e. an object that contains another object which contains a reference to the original object. There may be an arbitrary number of hierarchy levels between the two classes.
You need to implement some kind of mechanism to stop traversing an object that you already traversed, e.g. with the help of a hash set.
Here is an example not using recursion, which leverages Eric Lippert's explicit stack approach. I don't know if the behavior with strings is as you intended, but this may prevent your stack from blowing:
public static IEnumerable<object> GetPropertiesDepthFirst(object obj)
{
if (obj == null)
yield break;
var stack = new Stack<object>();
stack.Push(obj);
while (stack.Count > 0)
{
var current = stack.Pop();
yield return current;
var objType = current.GetType();
var properties = objType.GetProperties();
foreach (var property in properties)
{
object value = property.GetValue(current, null);
if (value == null)
continue;
if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
{
var enumerable = (IEnumerable)value;
foreach (object child in enumerable)
stack.Push(child);
}
else
{
yield return value;
}
}
}
}
Also for IEnumerables with defined indexes, you may want to exclude Indexed Properties:
objType.GetProperties().Where(p => p.GetIndexParameters().Length == 0)
Related
Using reflection I can obtain the object properties and values for all Data Types and the objects inside this object. But if the object contains a List of other objects I am having trouble in getting the objects in the list. The list can contain any type of object.
private static void SaveObj(object obj) {
foreach (var prop in obj.GetType().GetProperties()) {
if (prop.PropertyType.Namespace == "Entities") { //It is an object
object obL = prop.GetValue(obj, null);
SaveObj(obj);
}
else if (prop.PropertyType.Name == "List`1") { //This is a list of objects
object obP = prop.GetValue(obj);
//obP has the list of objects, I can see the list in debug mode.
List<object> obL = (List<object>)prop.GetValue(obj, null);
//This line returns an exception!!
}
else {
columns += prop.Name.ToLower() + ", ";
values[i] = prop.GetValue(obj, null).ToString();
}
... // the code continues ...
}
}
The exception message returned is:
"It is not possible to convert an object of type 'System.Collections.Generic.List1[Entities.OrderItem]' to type 'System.Collections.Generic.List1[System.Object]'."
Interesting is that I can see all the objects and its contents in degug mode. In Immediate Window I can print the content of the variable obP with all the objects in the list, but how to read them?
Any ideas on how to solve this?
You can try casting it to IEnumerable and then use .Cast<object>().ToList() Like this:
IEnumerable obL = prop.GetValue(obj, null) as IEnumerable;
List<object> list = obL.Cast<object>().ToList();
Sins a List implements the ICollection interface, you could, instead of attempting to cast the returned value to a List<object>, do something like:
ICollection collection = (prop.GetValue(obj, null) as ICollection);
if (collection != null)
{
object[] array = new object[collection.Count];
collection.CopyTo(array, 0);
//if you need a list just create a new one and pass in the array: new List<object>(array);
}
As a side note, wouldn't your:
if (prop.PropertyType.Namespace == "Entities")
{
object obL = prop.GetValue(obj, null);
SaveObj(obj);
}
just cause a infinite loop, which will lead to a StackOverflow/OutOfMemory exception, might want to change it to SaveObj(obL); if that was the intended behavior.
I am trying to serialize an object to JSON using newtonsoft.json. The only thing is that I cannot append the json type to the field name. Consider this example:
var item = new {
value = "value",
data = new []{"str", "str"},
b = true
};
I want to convert that to
{
"value.string" : "value",
"data.array" : ["str", "str"],
"b.bool" : true
}
or something similar. The idea is to append the json type (not the c# type) to the json field. The reason I don't want to append the C# type is because it could be complex (sometimes the type is anonymous, sometimes it is IEnumerable, etc.)
I have seen many solutions that can convert to C# type such as implementing a IContractResolver. Unfortunately that doesn't work for this case.
I also do not know the type that I will convert before hand.
The closest I could get to is
public JObject Convert(JObject data)
{
var queue = new Queue<JToken>();
foreach (var child in data.Children())
{
queue.Enqueue(child);
}
while (queue.Count > 0)
{
var token = queue.Dequeue();
if (token is JProperty p)
{
if (p.Value.Type != JTokenType.Object)
{
token.Replace(new JProperty(
$"{p.Name}.{p.Value.Type}",
p.Value
));
}
}
foreach (var child in token.Children())
{
queue.Enqueue(child);
}
}
return data;
}
But it does not work for nested objects like
var result = convertor.Convert(JObject.FromObject(new { nested = new { item = "str"}}));
For some reason, Replace does not work for the nested objects. Not sure if it is a bug or not.
Your main problem is that, when you add a child JToken to a parent, and the child already has a parent, the child is cloned and the clone is added to the parent -- in this case your new JProperty. Then when you replace the original property with the new property, the cloned value hierarchy replaces the original value hierarchy in the overall JToken tree. And finally, when you do
foreach (var child in token.Children())
{
queue.Enqueue(child);
}
You end up looping through the original children that have already been cloned and replaced. While this doesn't matter when the property value is a primitive, it causes the problem you are seeing if the value is an array or other container.
(A secondary, potential issue is that you don't handle the possibility of the root container being an array.)
The fix is to prevent the wholesale cloning of property values by removing the property value from the old property before adding it to the new property, then later looping through the new property's children:
public static class JsonExtensions
{
public static TJToken Convert<TJToken>(this TJToken data) where TJToken : JToken
{
var queue = new Queue<JToken>();
foreach (var child in data.Children())
{
queue.Enqueue(child);
}
while (queue.Count > 0)
{
var token = queue.Dequeue();
if (token is JProperty)
{
var p = (JProperty)token;
if (p.Value.Type != JTokenType.Object)
{
var value = p.Value;
// Remove the value from its parent before adding it to a new parent,
// to prevent cloning.
p.Value = null;
var replacement = new JProperty(
string.Format("{0}.{1}", p.Name, value.Type),
value
);
token.Replace(replacement);
token = replacement;
}
}
foreach (var child in token.Children())
{
queue.Enqueue(child);
}
}
return data;
}
}
Working .Net fiddle.
Why does Json.NET clone the value when adding it to the new JProperty? This happens because there is a bi-directional reference between parents and children in the JToken hierarchy:
JToken.Children() iterates through all child tokens of a given token;
JToken.Parent gets the parent of a given token.
Thus a JToken cannot have two parents -- i.e., it cannot exist in two locations in a JToken hierarchy simultaneously. So when you add the property value to a new JProperty, what should happen to the previous parent? Possibilities include:
The previous parent is unmodified and a clone of the child is added to the new parent.
The previous parent is modified by replacing the child with a clone of its child.
The previous parent is modified by replacing the child with a null JValue.
As it turns out, Json.NET takes option #1, resulting in your bug.
I'm using Value Injecters to map from 1 type to another using the LoopValueInjection and overriding the SetValue(object v) method with some custom logic. I am trying to detect when a HashSet is being passed in and go through the HashSet and apply a method to each item in it to do some clean up. The issues I'm having are because the parameter is just an object and I don't know what the type of item will be in the HashSet. For example, it could be HashSet or HashSet.
Here is the code I currently have but I'm getting an InvalidCastException.
protected override object SetValue(object v)
{
if (type.Name == "HashSet`1")
{
var genericType = type.GetGenericArguments()[0];
// this line throws the InvalidCastException
var cleanHashSet = (HashSet<object>)Activator.CreateInstance(type);
foreach (var item in (HashSet<object>)v) // I'm sure this cast will throw as well
{
cleanHashSet.Add(Clean(item));
}
return cleanHashSet;
}
return base.SetValue(v);
}
I guess the main question is how can I loop through the HashSet that is passed in as an object once I determine it is in fact a HashSet of some kind? I'm also thinking I will need to create a new empty HashSet of the specific type as well so I can put each item that gets cleaned up into it.
In .NET only interfaces and delegate types can be co- and contra-variant. So it is not possible to cast HashSet<SomeType> to HashSet<object>.
You want to cast your v to non-generic version of IEnumerable interface
dynamic cleanHashSet = Activator.CreateInstance(type);
foreach (object item in (IEnumerable)v)
{
cleanHashSet.Add(Clean(item));
}
If you don't want to use dynamic keyword then you need to call Add method with reflection
object cleanHashSet = Activator.CreateInstance(type);
var method = type.GetMethod("Add");
foreach (object item in (IEnumerable)v)
{
method.Invoke(cleanHashSet, new object[] { Clean(item) });
}
Use:
bool isHashSet = typeof(HashSet<object>).IsAssignableFrom(type);
or
object x = ...
HashSet<object> hs = x as HashSet<object>;
if (hs != null)
{
// use hs
}
If you really have <T> specified then use it instead of object.
HashSet<T> implements ICollection<T>, IEnumerabel<T>, IEnumerable. What means if you know T you can both enumerate and add. Otherwise enumerate only enumerate.
I have a class that compares 2 instances of the same objects, and generates a list of their differences. This is done by looping through the key collections and filling a set of other collections with a list of what has changed (this may make more sense after viewing the code below). This works, and generates an object that lets me know what exactly has been added and removed between the "old" object and the "new" one.
My question/concern is this...it is really ugly, with tons of loops and conditions. Is there a better way to store/approach this, without having to rely so heavily on endless groups of hard-coded conditions?
public void DiffSteps()
{
try
{
//Confirm that there are 2 populated objects to compare
if (NewStep.Id != Guid.Empty && SavedStep.Id != Guid.Empty)
{
//<TODO> Find a good way to compare quickly if the objects are exactly the same...hash?
//Compare the StepDoc collections:
OldDocs = SavedStep.StepDocs;
NewDocs = NewStep.StepDocs;
Collection<StepDoc> docstoDelete = new Collection<StepDoc>();
foreach (StepDoc oldDoc in OldDocs)
{
bool delete = false;
foreach (StepDoc newDoc in NewDocs)
{
if (newDoc.DocId == oldDoc.DocId)
{
delete = true;
}
}
if (delete)
docstoDelete.Add(oldDoc);
}
foreach (StepDoc doc in docstoDelete)
{
OldDocs.Remove(doc);
NewDocs.Remove(doc);
}
//Same loop(s) for StepUsers...omitted for brevity
//This is a collection of users to delete; it is the collection
//of users that has not changed. So, this collection also needs to be checked
//to see if the permisssions (or any other future properties) have changed.
foreach (StepUser user in userstoDelete)
{
//Compare the two
StepUser oldUser = null;
StepUser newUser = null;
foreach(StepUser oldie in OldUsers)
{
if (user.UserId == oldie.UserId)
oldUser = oldie;
}
foreach (StepUser newie in NewUsers)
{
if (user.UserId == newie.UserId)
newUser = newie;
}
if(oldUser != null && newUser != null)
{
if (oldUser.Role != newUser.Role)
UpdatedRoles.Add(newUser.Name, newUser.Role);
}
OldUsers.Remove(user);
NewUsers.Remove(user);
}
}
}
catch(Exception ex)
{
string errorMessage =
String.Format("Error generating diff between Step objects {0} and {1}", NewStep.Id, SavedStep.Id);
log.Error(errorMessage,ex);
throw;
}
}
The targeted framework is 3.5.
Are you using .NET 3.5? I'm sure LINQ to Objects would make a lot of this much simpler.
Another thing to think about is that if you've got a lot of code with a common pattern, where just a few things change (e.g. "which property am I comparing?" then that's a good candidate for a generic method taking a delegate to represent that difference.
EDIT: Okay, now we know we can use LINQ:
Step 1: Reduce nesting
Firstly I'd take out one level of nesting. Instead of:
if (NewStep.Id != Guid.Empty && SavedStep.Id != Guid.Empty)
{
// Body
}
I'd do:
if (NewStep.Id != Guid.Empty && SavedStep.Id != Guid.Empty)
{
return;
}
// Body
Early returns like that can make code much more readable.
Step 2: Finding docs to delete
This would be much nicer if you could simply specify a key function to Enumerable.Intersect. You can specify an equality comparer, but building one of those is a pain, even with a utility library. Ah well.
var oldDocIds = OldDocs.Select(doc => doc.DocId);
var newDocIds = NewDocs.Select(doc => doc.DocId);
var deletedIds = oldDocIds.Intersect(newDocIds).ToDictionary(x => x);
var deletedDocs = oldDocIds.Where(doc => deletedIds.Contains(doc.DocId));
Step 3: Removing the docs
Either use the existing foreach loop, or change the properties. If your properties are actually of type List<T> then you could use RemoveAll.
Step 4: Updating and removing users
foreach (StepUser deleted in usersToDelete)
{
// Should use SingleOfDefault here if there should only be one
// matching entry in each of NewUsers/OldUsers. The
// code below matches your existing loop.
StepUser oldUser = OldUsers.LastOrDefault(u => u.UserId == deleted.UserId);
StepUser newUser = NewUsers.LastOrDefault(u => u.UserId == deleted.UserId);
// Existing code here using oldUser and newUser
}
One option to simplify things even further would be to implement an IEqualityComparer using UserId (and one for docs with DocId).
As you are using at least .NET 2.0 I recommend implement Equals and GetHashCode ( http://msdn.microsoft.com/en-us/library/7h9bszxx.aspx ) on StepDoc. As a hint to how it can clean up your code you could have something like this:
Collection<StepDoc> docstoDelete = new Collection<StepDoc>();
foreach (StepDoc oldDoc in OldDocs)
{
bool delete = false;
foreach (StepDoc newDoc in NewDocs)
{
if (newDoc.DocId == oldDoc.DocId)
{
delete = true;
}
}
if (delete) docstoDelete.Add(oldDoc);
}
foreach (StepDoc doc in docstoDelete)
{
OldDocs.Remove(doc);
NewDocs.Remove(doc);
}
with this:
oldDocs.FindAll(newDocs.Contains).ForEach(delegate(StepDoc doc) {
oldDocs.Remove(doc);
newDocs.Remove(doc);
});
This assumes oldDocs is a List of StepDoc.
If both StepDocs and StepUsers implement IComparable<T>, and they are stored in collections that implement IList<T>, then you can use the following helper method to simplify this function. Just call it twice, once with StepDocs, and once with StepUsers. Use the beforeRemoveCallback to implement the special logic used to do your role updates. I'm assuming the collections don't contain duplicates. I've left out argument checks.
public delegate void BeforeRemoveMatchCallback<T>(T item1, T item2);
public static void RemoveMatches<T>(
IList<T> list1, IList<T> list2,
BeforeRemoveMatchCallback<T> beforeRemoveCallback)
where T : IComparable<T>
{
// looping backwards lets us safely modify the collection "in flight"
// without requiring a temporary collection (as required by a foreach
// solution)
for(int i = list1.Count - 1; i >= 0; i--)
{
for(int j = list2.Count - 1; j >= 0; j--)
{
if(list1[i].CompareTo(list2[j]) == 0)
{
// do any cleanup stuff in this function, like your role assignments
if(beforeRemoveCallback != null)
beforeRemoveCallback(list[i], list[j]);
list1.RemoveAt(i);
list2.RemoveAt(j);
break;
}
}
}
}
Here is a sample beforeRemoveCallback for your updates code:
BeforeRemoveMatchCallback<StepUsers> callback =
delegate(StepUsers oldUser, StepUsers newUser)
{
if(oldUser.Role != newUser.Role)
UpdatedRoles.Add(newUser.Name, newUser.Role);
};
What framework are you targeting? (This will make a difference in the answer.)
Why is this a void function?
Shouldn't the signature look like:
DiffResults results = object.CompareTo(object2);
If you want to hide the traversal of the tree-like structure you could create an IEnumerator subclass that hides the "ugly" looping constructs and then use CompareTo interface:
MyTraverser t =new Traverser(oldDocs, newDocs);
foreach (object oldOne in t)
{
if (oldOne.CompareTo(t.CurrentNewOne) != 0)
{
// use RTTI to figure out what to do with the object
}
}
However, I'm not at all sure that this particularly simplifies anything. I don't mind seeing the nested traversal structures. The code is nested, but not complex or particularly difficult to understand.
Using multiple lists in foreach is easy. Do this:
foreach (TextBox t in col)
{
foreach (TextBox d in des) // here des and col are list having textboxes
{
// here remove first element then and break it
RemoveAt(0);
break;
}
}
It works similar as it is foreach (TextBox t in col && TextBox d in des)
Is there a way to iterate (through foreach preferably) over a collection using reflection? I'm iterating over the properties in an object using reflection, and when the program gets to a type that is a collection, I'd like it to iterate over the contents of the collection and be able to access the objects in the collection.
At the moment I have an attribute set on all of my properties, with an IsCollection flag set to true on the properties that are collections. My code checks for this flag and if it's true, it gets the Type using reflection. Is there a way to invoke GetEnumerator or Items somehow on a collection to be able to iterate over the items?
I had this issue, but instead of using reflection, i ended up just checking if it was IEnumerable. All collections implement that.
if (item is IEnumerable)
{
foreach (object o in (item as IEnumerable))
{
}
} else {
// reflect over item
}
I've tried to use a similar technique as Darren suggested, however just beware that not just collections implement IEnumerable. string for instance is also IEnumerable and will iterate over the characters.
Here's a small function I'm using to determine if an object is a collection (which will be enumerable as well since ICollection is also IEnumerable).
public bool isCollection(object o)
{
return typeof(ICollection).IsAssignableFrom(o.GetType())
|| typeof(ICollection<>).IsAssignableFrom(o.GetType());
}
Just get the value of the property and then cast it into an IEnumerable. Here is some (untested) code to give you an idea:
ClassWithListProperty obj = new ClassWithListProperty();
obj.List.Add(1);
obj.List.Add(2);
obj.List.Add(3);
Type type = obj.GetType();
PropertyInfo listProperty = type.GetProperty("List", BindingFlags.Public);
IEnumerable listObject = (IEnumerable) listProperty.GetValue(obj, null);
foreach (int i in listObject)
Console.Write(i); // should print out 123
Just for information may be it will be of someone's help...
I had a class with nested classes and collection of some other classes. I wanted to save the property values of the class as well nested classes and collection of classes. My code is as follows:
public void LogObject(object obj, int indent)
{
if (obj == null) return;
string indentString = new string(' ', indent);
Type objType = obj.GetType();
PropertyInfo[] properties = objType.GetProperties();
foreach (PropertyInfo property in properties)
{
Type tColl = typeof(ICollection<>);
Type t = property.PropertyType;
string name = property.Name;
object propValue = property.GetValue(obj, null);
//check for nested classes as properties
if (property.PropertyType.Assembly == objType.Assembly)
{
string _result = string.Format("{0}{1}:", indentString, property.Name);
log.Info(_result);
LogObject(propValue, indent + 2);
}
else
{
string _result = string.Format("{0}{1}: {2}", indentString, property.Name, propValue);
log.Info(_result);
}
//check for collection
if (t.IsGenericType && tColl.IsAssignableFrom(t.GetGenericTypeDefinition()) ||
t.GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == tColl))
{
//var get = property.GetGetMethod();
IEnumerable listObject = (IEnumerable)property.GetValue(obj, null);
if (listObject != null)
{
foreach (object o in listObject)
{
LogObject(o, indent + 2);
}
}
}
}
}
An called this function
LogObject(obj, 0);
However, I have some structs inside my classes and I need to figure out how to get their values. Moreoevr, I have some LIst. I need to get their value as well.... I will post if I update my code.
The best you could probably do would be to check if the object implements certain collection interfaces - probably IEnumerable would be all that you need. Then it's just a matter of calling GetEnumerator() off of the object, and using IEnumerator.MoveNext() and IEnumerator.Current to work your way through the collection.
This won't help you if the collection doesn't implement those interfaces, but if that's the case it's not really much of a collection, I suppose.
When your using reflection you aren't necessarily using an instance of that object. You would have to create an instance of that type of be able to iterate through the object's properties. So if you are using reflection use the ConstructorInfo.Invoke() (?) method to create a new instance or point to an instance of the type.
I would look at the Type.FindInterfaces method. This can filter out the interfaces implemented by a given type. As in PropertyInfo.PropertyType.FindInterfaces(filterMethod, filterObjects). You can filter by IEnumerable and see if any results are returned. MSDN has a great example in the method documentation.
If you're not using an instance of the object but rather a Type, you can use the following:
// type is IEnumerable
if (type.GetInterface("IEnumerable") != null)
{
}
A rather straightforward approach would be to type cast the object as the collection and directly use that.