mganss / ExcelMapper

An Excel to object mapper. Maps POCOs to and from Excel. Configuration via convention, attributes, or fluent methods.
MIT License
793 stars 122 forks source link

Q: Nested property expressions support #312

Open vladimir-ilnytskyi opened 1 week ago

vladimir-ilnytskyi commented 1 week ago

Hi!

I'm facing an issue with nested object mapping from excel:

class Address { 
public string City {get ;set;}
 ...
 }

class Product {
public Address ShippingAddress { get; set; }
public Address DeliveryAddress {get; set; }
}

It would be nice to be able to use mappings for shipping address city and Delivery address city like this:

mapper.AddMapping("shipping address city", x => x.ShippingAddress.City); // handle expression with nested property accessors. For sure it is possible to use refrection on nested objects So now i get "something like cannot convert cell string to type string".

Something like this would help

public static (PropertyInfo propertyInfo, string fullPath) GetPropertyInfo<T>(Expression<Func<T, object>> propertySelector)
    {
        if (propertySelector is not LambdaExpression lambdaExpression)
        {
            throw new ArgumentException($"Unsupported property selector: {propertySelector}", nameof(propertySelector));
        }

        var pathBuilder = new StringBuilder();
        var body = lambdaExpression.Body;

        while (body is MemberExpression or UnaryExpression{ Operand: MemberExpression })
        {
            var memberAccess = body as MemberExpression ?? (MemberExpression)((UnaryExpression)body).Operand;

            if (pathBuilder.Length > 0)
            {
                pathBuilder.Insert(0, ".");
            }

            pathBuilder.Insert(0, memberAccess.Member.Name);
            body = memberAccess.Expression;
        }

        if (pathBuilder.Length > 0)
        {
            pathBuilder.Insert(0, typeof(T).Name + ".");
        }

        var lambdaBody = lambdaExpression.Body.NodeType == ExpressionType.MemberAccess ?
            (MemberExpression)lambdaExpression.Body :
            (MemberExpression)((UnaryExpression)lambdaExpression.Body).Operand; // for nullable value such as int?

        // body.Member will return the MemberInfo of the base class, so we have to get it from T...
        //return (PropertyInfo)body.Member;

        var propertyInfo = lambdaBody.Expression.Type.GetProperty(lambdaBody.Member.Name);
        return (propertyInfo, pathBuilder.ToString());
    }

Having separate mapping for type is making impossible to map different properties of same type to excel, maybe there is a way but i seems like i have stuck with using custom mapping funcs.

Please advise,

Best regards

mganss commented 1 day ago

Thanks a lot for mapping out a solution already! Could you make a PR?