Home > Back-end >  C# Create Dictionary with All Non-Object Property Names Mapped to the Object's Structure
C# Create Dictionary with All Non-Object Property Names Mapped to the Object's Structure

Time:04-28

I want to create a dictionary that has all of the public properties of an object mapped to the plain string property name. For example, the following "Person" class should return the following dictionary. I think I want to walk the object tree with the .GetProperties() method, but I am having trouble with where to go from there.

Code:

public class Person
{
    public string FirstName;
    public string LastName;
    public Address Addr;
}

public class Address
{
    public string Country;
    public City City;
}

public class City
{
    public string ZipCode;
    public string Street;
}

Dictionary Result:

Property Name Full Path
FirstName FirstName
LastName LastName
Country Addr.Country
ZipCode Addr.City.ZipCode
Street Addr.City.Street

CodePudding user response:

Although I couldn't figure out why you need to do it, you can use reflection to get all data you need:

void PropertyToDictionary()
{
    Person person = new()
    {
        FirstName = "Johnny",
        LastName = "Depp",
        Addr = new()
        {
            City = new()
            {
                Street = "Street",
                ZipCode = "12345"
            }
        }
    };

    var dic =new Dictionary<string, string>();

    person.GetType()
        .GetProperties()
        .ToList()
        .ForEach(p =>
        {
            if (p.Name == "Addr")
            {
                p.GetValue(person)
                .GetType()
                .GetProperties()
                .ToList()
                .ForEach(pp =>
                {
                    if (pp.Name == "City")
                    {
                        pp.GetValue(person.Addr)
                        .GetType()
                        .GetProperties()
                        .ToList()
                        .ForEach(ppc =>
                        {
                            dic.Add(ppc.Name, $"{p.Name}.{pp.Name}.{ppc.Name}");
                        });
                    }
                    else
                    {
                        dic.Add(pp.Name, $"{p.Name}.{pp.Name}");
                    }
                });
            }
            else
            {
                dic.Add(p.Name,p.Name);
            }
        });

    foreach (var item in dic)
    {
        Console.WriteLine(item.Key   "-----"   item.Value);
    }
}

Output:

Output console

CodePudding user response:

Apologies that I did not do a good job explaining my problem. I wanted to keep it high level from the actual core of the problem, but I think that ended up being more confusing.

Essentially, I have a web project that makes requests to search, sort, etc. on Database Entities. The requests only contain a single string as the property that should be used and then the type of request. That property code is at the top-level or nested. So, I needed to build out a way to get the possible top-level and nested properties that I can use and map that to just the simple name. In the end, just a dynamic dictionary with all public top-level properties and nested properties.

Here is how I ended up solving it:

/// <summary>
/// This method will populate the <paramref name="propertiesDictionary"/> with keys of all of the public property and nested
/// public properties. This is useful for mapping search, sort, etc. requests with the request property to the fully pathed
/// with the object property name. The values of the dictionary can then be used in linq queries to pull the data from the entities.
/// Important notes:
///     1. This will only use the top most level property name if there are multiple properties with the same name.
///     2. This will not handle nested objects of the same type.
///     3. It requires all objects to reside in the same namespace. This is needed to ignore properties on strings, Lists, etc. However, this means only objects in that namespace will be used.
/// </summary>
/// <param name="objectType">The type of the object that will be pulled from</param>
/// <param name="propertiesDictionary">The dictionary that will be populated with the plain string names as the keys and the full pathed property has the value</param>
/// <param name="objectNameSpace">The namespace of the properties</param>
/// <param name="prefix">Prefix used in the recursive call to fill out the full path. THIS SHOULD BE EMPTY ON THE FIRST CALL</param>
/// <param name="topLevelObject">The top level object that we are using. THIS SHOULD BE NULL ON THE FIRST CALL</param>
/// <param name="recurssionIteration">The current iteration of recursive which is used to bail to avoid a stack-overflow. THIS SHOULD BE ZERO ON THE FIRST CALL</param>
private static void GetPublicPropertiesAndNestedPropertiesRecursively( Type objectType, IDictionary<string, string> propertiesDictionary, string objectNameSpace, string prefix = "", Type topLevelObject = null, int recurssionIteration = 0 )
{
    // First, check to make sure that we didn't hit the recursion max
    if ( recurssionIteration > 8 )
    {
        return;
    }

    // If this is not the first call, bail out if this is the same object. This would cause an infinite loop if not. 
    // If this is the first call, set the top level object.
    if ( topLevelObject == null )
    {
        topLevelObject = objectType;
    }
    else if ( objectType == topLevelObject )
    {
        return;
    }

    // Get all the public properties and return if there is no or th
    var currentProperties = objectType.GetProperties();
    if ( objectType.GetProperties().Length < 1 || objectType.Namespace == null || !objectType.Namespace.StartsWith( objectNameSpace ) )
    {
        return;
    }

    // Add all of the properties that currently don't exist
    foreach ( var currentProperty in currentProperties.Where( currentProperty => !propertiesDictionary.ContainsKey( currentProperty.Name.ToLower() ) ) )
    {
        propertiesDictionary.Add( currentProperty.Name.ToLower(), $"{prefix}{currentProperty.Name}".ToLower() );
    }

    // Recursively get all of the nested properties
    foreach ( var propertyInfo in currentProperties )
    {
        GetPublicPropertiesAndNestedPropertiesRecursively( propertyInfo.PropertyType, propertiesDictionary, objectNameSpace, $"{propertyInfo.Name}.",  topLevelObject, recurssionIteration   );
    }
  • Related