Recursively Get Properties & Child Properties of a Class

Recursively Get Properties & Child Properties Of A Class

You have two problems with your code:

  1. because of condition if (property.PropertyType.Assembly == objType.Assembly) you will omit System.Collections like List<>
  2. you do not treat differently propValue that are collections. Hence it will print List properties, not its elements properties.

You can change that for example into:

public void PrintProperties(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)
{
object propValue = property.GetValue(obj, null);
var elems = propValue as IList;
if (elems != null)
{
foreach (var item in elems)
{
PrintProperties(item, indent + 3);
}
}
else
{
// This will not cut-off System.Collections because of the first check
if (property.PropertyType.Assembly == objType.Assembly)
{
Console.WriteLine("{0}{1}:", indentString, property.Name);

PrintProperties(propValue, indent + 2);
}
else
{
Console.WriteLine("{0}{1}: {2}", indentString, property.Name, propValue);
}
}
}
}

Recursively Get Properties & Child Properties Of An Object

I suggest you to mark all the classes, you need to grab, with custom attribute after that you could do something like this

 class Program
{
static void Main(string[] args)
{
var lines = ExtractHelper.IterateProps(typeof(Container)).ToArray();

foreach (var line in lines)
Console.WriteLine(line);

Console.ReadLine();
}
}

static class ExtractHelper
{

public static IEnumerable<string> IterateProps(Type baseType)
{
return IteratePropsInner(baseType, baseType.Name);
}

private static IEnumerable<string> IteratePropsInner(Type baseType, string baseName)
{
var props = baseType.GetProperties();

foreach (var property in props)
{
var name = property.Name;
var type = ListArgumentOrSelf(property.PropertyType);
if (IsMarked(type))
foreach (var info in IteratePropsInner(type, name))
yield return string.Format("{0}.{1}", baseName, info);
else
yield return string.Format("{0}.{1}", baseName, property.Name);
}
}

static bool IsMarked(Type type)
{
return type.GetCustomAttributes(typeof(ExtractNameAttribute), true).Any();
}

public static Type ListArgumentOrSelf(Type type)
{
if (!type.IsGenericType)
return type;
if (type.GetGenericTypeDefinition() != typeof(List<>))
throw new Exception("Only List<T> are allowed");
return type.GetGenericArguments()[0];
}
}

[ExtractName]
public class Container
{
public string Name { get; set; }
public List<Address> Addresses { get; set; }
}

[ExtractName]
public class Address
{
public string AddressLine1 { get; set; }
public string AddressLine2 { get; set; }
public List<Telephone> Telephones { get; set; }
}

[ExtractName]
public class Telephone
{
public string CellPhone { get; set; }
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = true, AllowMultiple = true)]
public sealed class ExtractNameAttribute : Attribute
{ }

Recursively Get Properties and values & Child Properties and values Of An Object

Another option would be to define a custom serialization mechanism.

For example, consider the following implementation (that has some rough edges and does not necessary cover corner cases). The following interface defines serialization of some object as query part:

public interface IQueryPartDescriptor
{
/// <summary>
/// Converts the specified object into a string usable as query part.
/// </summary>
string ObjectToQueryPart(string prefix, object obj);

/// <summary>
/// Describes the properties containing lists of children objects.
/// </summary>
IEnumerable<ChildrenCollectionDescriptor> ChildrenListsDescriptors { get; }
}

With children collection descriptor like:

public struct ChildrenCollectionDescriptor
{
private readonly Func<object, IEnumerable<object>> _getChildren;
private readonly Func<int, string, string> _buildChildPrefix;

public ChildrenCollectionDescriptor(
Func<object, IEnumerable<object>> getChildren,
Func<int, string, string> buildChildPrefix)
: this()
{
_getChildren = getChildren;
_buildChildPrefix = buildChildPrefix;
}

public IEnumerable<object> GetChildren(object parent)
{
return _getChildren(parent);
}

public string BuildChildPrefix(int childPosition, string accumulatedPrefix)
{
return _buildChildPrefix(childPosition, accumulatedPrefix);
}
}

Implementations of this interface for each of the classes you've described will look something like:

public class PaxDescriptor : IQueryPartDescriptor
{
public string ObjectToQueryPart(string prefix, object obj)
{
var pax = (Pax)obj;
var queryPart = prefix + "[paxType]=" + pax.PaxType;
if (pax.PaxType == PaxType.Child)
{
queryPart += prefix + "[age]=" + pax.Age;
}

return queryPart;
}

public IEnumerable<ChildrenCollectionDescriptor> ChildrenListsDescriptors
{
get { return Enumerable.Empty<ChildrenCollectionDescriptor>(); }
}
}

public class RoomDescriptor : IQueryPartDescriptor
{
public string ObjectToQueryPart(string prefix, object obj)
{
return String.Empty;
}

public IEnumerable<ChildrenCollectionDescriptor> ChildrenListsDescriptors
{
get
{
return new[]
{
new ChildrenCollectionDescriptor(
room => ((Room)room).Paxes,
(index, roomsPrefix) => roomsPrefix + "[" + index + "]")
};
}
}
}

public class AvaliableHotelRequestDescriptor : IQueryPartDescriptor
{
public string ObjectToQueryPart(string prefix, object obj)
{
var request = (AvaliableHotelRequest)obj;
return
"method=" + request.Method + "&" +
"apiKey=" + request.ApiKey + "&" +
"destinationID=" + request.DestinationId + "&" +
"checkIn=" + request.CheckIn.ToString("yyyy-MM-dd") + "&" +
"checkOut=" + request.CheckOut.ToString("yyyy-MM-dd") + "&" +
"currency=" + request.Currency + "&" +
"clientNationality=" + request.ClientNationality + "&" +
"onRequest=" + request.OnRequest.ToString().ToLower();
}

public IEnumerable<ChildrenCollectionDescriptor> ChildrenListsDescriptors
{
get
{
return new[]
{
new ChildrenCollectionDescriptor(
request => ((AvaliableHotelRequest)request).Rooms,
(index, _) => "&rooms[" + index + "]")
};
}
}
}

Of course, implementations of the interface could be provided by the classes in question themselves.

Now, the whole serialization part will be:

public static string ToQuery(object root, IDictionary<Type, IQueryPartDescriptor> partDescriptors)
{
var queryBuilder = new StringBuilder();
AddQueryPart(root, String.Empty, partDescriptors, queryBuilder);

return queryBuilder.Insert(0, '?').ToString();
}

private static void AddQueryPart(
object obj,
string prefixInQuery,
IDictionary<Type, IQueryPartDescriptor> partDescriptors,
StringBuilder queryBuilder)
{
var queryPartDescriptor = partDescriptors[obj.GetType()];

queryBuilder
.Append(queryPartDescriptor.ObjectToQueryPart(prefixInQuery, obj));

foreach (var childrenListDescriptor in queryPartDescriptor.ChildrenListsDescriptors)
{
var children = childrenListDescriptor.GetChildren(obj).ToList();
for (var childIndex = 0; childIndex < children.Count; childIndex++)
{
var childPrefix = childrenListDescriptor.BuildChildPrefix(childIndex, prefixInQuery);
AddQueryPart(children[childIndex], childPrefix, partDescriptors, queryBuilder);
}
}
}

This approach does not involve reflection and is more modular. You can also easily extend the number of the covered types by adding more descriptors.

Updating property of child properties using recursion

Well, if there's really can't change any of the existing stuff.....

I believe this is what you're looking for:

How to set Vaues to the Nested Property using C# Reflection.?

Your program should look something like this:

class Program
{
static void Main(string[] args)
{
string mappingAddress = "EmployeeAddress.ChildAddressType.TheAddressType";

string theAddressTypeValue = "A Home";

Employee employee = new Employee();

//Magic code - Thanks Jon Skeet
SetProperty(mappingAddress, employee, theAddressTypeValue);
}

public static void SetProperty(string compoundProperty, object target, object value)
{
string[] bits = compoundProperty.Split('.');
for (int i = 0; i < bits.Length - 1; i++)
{
PropertyInfo propertyToGet = target.GetType().GetProperty(bits[i]);
target = propertyToGet.GetValue(target, null);
}
PropertyInfo propertyToSet = target.GetType().GetProperty(bits.Last());
propertyToSet.SetValue(target, value, null);
}
}

public class Employee
{
public Employee()
{
EmployeeAddress = new Address();
}

public Address EmployeeAddress { get; set; }
}

public class Address
{
public Address()
{
ChildAddressType = new AddressType();
}

public AddressType ChildAddressType { get; set; }
}

public class AddressType
{
public string TheAddressType { get; set; }
}

How to iterate all child properties recursively while setting values

Well, I think it's easier to return TreeNode itself instead of its Id:

private IEnumerable<TreeNode> ThisAndAllParents() {
for (TreeNode current = this; current != null; current = current.ParentNode)
yield return current;
}

then ExpandNodesRecursively can be something like this:

private void ExpandNodesRecursively() {
foreach (TreeNode node in ThisAndAllParents())
node.IsExpanded = true;
}

C# how to recursively search object for objects of type x

If I understand you correctly, you might need to use Reflection to retrieve the Address objects:

public static class MyExtensions
{
public static List<Address> GetAddresses(this Dog dog)
{
var addresses = new List<Address>();
GetAddress(dog, ref addresses);
return addresses;
}

private static void GetAddress(object property, ref List<Address> addresses)
{
if (property == null)
{
return;
}

var props = property.GetType()
.GetProperties()
.Where(p => p.PropertyType.IsClass &&
!p.PropertyType.IsPrimitive)
.ToList();

foreach (var prop in props)
{
if (prop.PropertyType == typeof(Address))
{
var address = prop.GetValue(property) as Address;
addresses?.Add(address);
}

var next = prop.GetValue(obj: property);

// When property is a collection
if (next is IEnumerable collection)
{
foreach (var item in collection)
{
GetAddress(property: item,
addresses: ref addresses);
}
}
else
{
GetAddress(property: next,
addresses: ref addresses);
}
}
}
}

And usage example:

var dogs = new List<Dog>();
for (int i = 0; i < 5; i++)
{
var dog = new Dog
{
Name = $"Dog - {i}",
Age = 10,
Kennel = new Kennel
{
Address = new Address()
},
Kennels = new List<Kennel>
{
new Kennel
{
Address = new Address()
},
new Kennel
{
Address = new Address()
},
new Kennel()
{
Address = new Address()
}
},
Owner = new Owner
{
Address = new Address(),
Kennel = new Kennel
{
Address = new Address()
}
}
};

dogs.Add(dog);
}

var address = dogs.Select(dog => dog.GetAddresses());
Console.WriteLine(address.Sum(a => a.Count)); // will print 30

Please note that using Reflection could affect the performance of your application.



Related Topics



Leave a reply



Submit