ExcelMapper

repository·master·Indexed 21 days ago

https://github.com/mganss/excelmapper

A C# library for mapping POCO objects to and from Excel files (.xlsx and .xls) using NPOI. It supports attribute-based and fluent API mapping, dynamic objects via ExpandoObject, nested objects, C# records, and JSON serialization within cells. Features include formula handling, custom data formats, and the ability to preserve workbook styles when saving templates.

Tokens
2.6K
Snippets
11
Records
12
Agent score
25%

What's inside excelmapper

  1. Map nested objects and records

    master

    ExcelMapper supports mapping hierarchical data structures (parent/child objects) automatically. If your Excel columns follow a naming convention that matches your nested class properties, no extra configuration is required.

    This support extends to C# records. For positional records without a default constructor, the mapper uses the constructor with the highest number of arguments, provided the parameter names match the property names (case-insensitive).

    public class Person
    {
        public string Name { get; set; }
        public Address Address { get; set; }
    }
    
    public class Address
    {
        public string Street { get; set; }
        public string City { get; set; }
    }
    
    // Works with records too
    public record Person(string Name, Address Address);
    public record Address(string Street, string City);
    
    var customers = new ExcelMapper("customers.xlsx").Fetch<Person>();
  2. Handle multiple mappings for a single column

    master

    If multiple properties map to the same column, you must specify which property should be used when saving the object back to Excel.

    1. Using Attributes: Add MappingDirections.ExcelToObject to the [Column] attribute of all properties that should be ignored during the Excel-to-Object write process, leaving only one property without it.
    2. Using Fluent API: Use the .FromExcelOnly() method on the mapping definition.

    Additionally, [Column] attributes are inherited by default. To prevent multiple mappings when a derived class overrides a property, set Inherit = false on the base class attribute.

    // Using Attributes
    public class Product
    {
        public decimal Price { get; set; }
        [Column("Price", MappingDirections.ExcelToObject)]
        public string PriceString { get; set; }
    }
    
    // Using Fluent API
    excel.AddMapping<Product>("Price", p => p.PriceString).FromExcelOnly();
  3. Read objects from an Excel file

    master

    To read data from an Excel file into a collection of POCO (Plain Old CLR Object) instances, instantiate ExcelMapper with the file path and use the Fetch<T>() method.

    By default:

    • It expects a header row where column names match property names (case-insensitive).
    • It reads from the first worksheet.
    • It autodetects the file format (.xlsx or .xls).

    If your file does not have a header row, set the HeaderRow property to false.

    var products = new ExcelMapper("products.xlsx").Fetch<Product>();
  4. Map columns using names or indexes

    master

    You can define how Excel columns map to your object properties using the [Column] attribute or the AddMapping() method.

    Using Column Names

    Use [Column("Name")] to map a specific header name to a property.

    Using Column Indexes

    Column indexes start at 1. When using indexes, every property must be explicitly mapped. You can use [Column(1)] or [Column(Letter="A")]. To use indexes without a header row, set HeaderRow = false on the ExcelMapper instance.

    Using Method Calls (Fluent API)

    You can define mappings programmatically using AddMapping<T>() or AddMapping(Type, ...).

    // Attribute mapping
    public class Product
    {
      public string Name { get; set; }
      [Column("Number")]
      public int NumberInStock { get; set; }
      [Column(1)]
      public string NameByIndex { get; set; }
      [Column(Letter="C")]
      public int NumberInLetter { get; set; }
    }
    
    // Programmatic mapping
    var excel = new ExcelMapper("products.xls");
    excel.AddMapping<Product>("Number", p => p.NumberInStock);
    excel.AddMapping<Product>(1, p => p.NumberInStock);
    excel.AddMapping(typeof(Product), "Number", "NumberInStock");
    excel.AddMapping(typeof(Product), ExcelMapper.LetterToIndex("A"), "NumberInStock");
  5. Configure data formats and formulas

    master

    Data Formats

    Use the [DataFormat] attribute to specify Excel numeric or DateTime formats. You can use builtin formats (via hex code) or custom format strings.

    Formulas

    • Reading Formulas: To map the result of a formula to a property, use the [FormulaResult] attribute. For string properties, the formula itself is mapped by default.
    • Writing Formulas: To save a formula to a cell, use the [Formula] attribute or the .AsFormula() method.

    Note: When saving formulas via strings, do not include the leading = sign (e.g., use A1+B1 instead of =A1+B1).

    public class Product
    {
        [DataFormat(0xf)]
        public DateTime Date { get; set; }
    
        [FormulaResult]
        public string Result { get; set; }
    
        [Formula]
        public string Formula { get; set; }
    }
    
    // Or via Fluent API
    excel.AddMapping<Product>("Result", p => p.Result).AsFormulaResult();
    excel.AddMapping<Product>("Formula", p => p.Formula).AsFormula();
  6. Track objects for updates

    master

    To modify existing Excel data and save only the changes, use Fetch<T>().ToList() to load the objects into memory. After modifying the objects in the list, call Save on the same ExcelMapper instance used to fetch them.

    var excel = new ExcelMapper("products.xlsx");
    var products = excel.Fetch<Product>().ToList();
    products[1].Price += 1.0m;
    excel.Save("products.out.xlsx");
  7. Ignore properties during mapping

    master

    You can prevent specific properties from being mapped using either the [Ignore] attribute or the Ignore<T>() method.

    // Using Attribute
    public class Product
    {
        [Ignore]
        public int Number { get; set; }
    }
    
    // Using Fluent API
    var excel = new ExcelMapper("products.xlsx");
    excel.Ignore<Product>(p => p.Price);
  8. Use custom object factories

    master

    If you need to instantiate types that cannot be created via a default constructor (such as interfaces), you can provide a custom factory using CreateInstance<T>(Func<T>).

    public class Person
    {
        public string Name { get; set; }
        public IAddress Address { get; set; }
    }
    
    // Register a factory for the interface
    excel.CreateInstance<IAddress>(() => new Address());
  9. Save objects to Excel

    master

    Use the Save method to write a collection of objects to a worksheet.

    If you use the same ExcelMapper instance to save objects that were previously read from a file, the workbook's existing styles and formatting are preserved. This is useful for filling out Excel templates.

    var products = new List<Product> { ... };
    new ExcelMapper().Save("products.xlsx", products, "Products");
  10. Map JSON formatted cells

    master

    ExcelMapper can automatically serialize and deserialize JSON strings within cells using the [Json] attribute or the .AsJson() method. This works for both single objects and lists.

    public class ProductJson
    {
        [Json]
        public Product Product { get; set; }
    }
    
    // Or via Fluent API
    var excel = new ExcelMapper("products.xls");
    excel.AddMapping<ProductJson>("Product", p => p.Product).AsJson();
  11. Fetch and save dynamic objects

    master

    If you do not provide a type to Fetch(), it returns an IEnumerable<dynamic>.

    • The returned objects are instances of ExpandoObject.
    • They include a special __indexes__ property (a dictionary) that maps property names to their corresponding column indexes.
    • If HeaderRow is false, property names will correspond to Excel column letters (e.g., "A", "B").
    var products = new ExcelMapper("products.xlsx").Fetch(); // -> IEnumerable<dynamic>
    products.First().Price += 1.0;
  12. Configure row ranges and header positions

    master

    You can fine-tune which rows are processed using the following properties on the ExcelMapper instance:

    • HeaderRowNumber: The zero-based index of the header row (default is 0).
    • MinRowNumber: The first row index containing data (default is 0).
    • MaxRowNumber: The last row index containing data (default is int.MaxValue).

    The header row does not need to fall within the MinRowNumber and MaxRowNumber range.