如何将列名各异的CSV反序列化为对象并完成属性映射?
When dealing with CSV files that have varying column headers (like mapping "Id"/"Id_Person" to PersonId), using a dedicated CSV library is the most reliable approach—rolling your own parser would mean handling edge cases like quoted fields, delimiter variations, and missing columns manually. Let’s use CsvHelper (the most popular C# CSV library) to solve this cleanly.
Step 1: Install CsvHelper
First, add the package to your project via NuGet:
Install-Package CsvHelper
Or using the .NET CLI:
dotnet add package CsvHelper
Step 2: Define a Custom Mapping Class
Create a ClassMap to explicitly link all possible CSV column names to your People class properties. This gives you full control over how headers map to properties, even when they vary across files.
using CsvHelper.Configuration; public class PeopleMap : ClassMap<People> { public PeopleMap() { // Map PersonId to either "Id" or "Id_Person" Map(m => m.PersonId) .Name("Id") .AlternativeNames("Id_Person") .Optional(); // Allow this column to be missing without errors // Map Name to either "Name" or "First_Name" Map(m => m.Name) .Name("Name") .AlternativeNames("First_Name") .Optional(); // Map Doc to "Document" (add more alternatives here if needed) Map(m => m.Doc) .Name("Document") .Optional(); } }
The .Optional() method ensures that if a column is missing from a CSV, the property will just use its default value (like 0 for int, null for string) instead of throwing an error.
Step 3: Deserialize Your CSV Files
Use the custom mapping to read both of your example CSV formats into People objects. Don’t forget to configure the correct delimiter (your first file uses ; instead of commas).
Example for Input 1 (Semicolon Delimiter)
using CsvHelper; using System.Globalization; using System.IO; using System.Collections.Generic; var config1 = new CsvConfiguration(CultureInfo.InvariantCulture) { Delimiter = ";", // Match the delimiter in your first CSV HasHeaderRecord = true }; using (var reader = new StreamReader("input1.csv")) using (var csv = new CsvReader(reader, config1)) { csv.Context.RegisterClassMap<PeopleMap>(); List<People> peopleFromInput1 = csv.GetRecords<People>().ToList(); }
Example for Input 2 (Quoted Fields, Comma Delimiter)
var config2 = new CsvConfiguration(CultureInfo.InvariantCulture) { HasHeaderRecord = true, // CsvHelper handles quoted fields automatically by default }; using (var reader = new StreamReader("input2.csv")) using (var csv = new CsvReader(reader, config2)) { csv.Context.RegisterClassMap<PeopleMap>(); List<People> peopleFromInput2 = csv.GetRecords<People>().ToList(); }
In Input 2, the Doc property will be null since there’s no "Document" column—this is safe because we marked it as optional.
Bonus: Dynamic Mappings (If You Need Runtime Flexibility)
If you don’t know all possible column names upfront, you can build the mapping dynamically:
var dynamicMap = new ClassMap<People>(); // Add dynamic alternatives for PersonId dynamicMap.Map(m => m.PersonId) .Name("Id") .AlternativeNames("Id_Person", "PersonID", "User_Id") // Add any other variations here .Optional(); // Repeat for other properties... // Register the dynamic map with your CSV reader csv.Context.RegisterClassMap(dynamicMap);
Key Tips
- Delimiter Awareness: Always set the correct delimiter in
CsvConfiguration—CsvHelper defaults to commas, but many CSVs use semicolons or tabs. - Case Insensitivity: By default, CsvHelper ignores case, so "ID" or "id" will still match "Id". You can disable this in the configuration if needed.
- Default Values: Optional properties will use C# default values if their column is missing (e.g.,
0for integers,nullfor strings).
内容的提问来源于stack exchange,提问作者Matheus Lacerda

