从csv文件结构生成c#模型类

本文关键字:模型 csv 文件结构 | 更新日期: 2023-09-27 18:23:47

这里的目标是在输入csv文件后,一个神奇的工具将输出带有csv字段的c#class。让我们来看看例子。

输入myFile.csv:

Year,Make,Model
1997,Ford,E350
2000,Mercury,Cougar

输出myFile.cs

public class myFile
{
   public string Year;
   public string Make;
   public string Model;
}

所以,我唯一需要修复的就是属性的类型。之后,我会使用这个类和FileHelpers来读取csv文件。稍后,它将被映射到EntityFramework类(使用AutoMapper)并保存到数据库中。

实际上,https://csv2entity.codeplex.com/看起来像是在做我需要的事情,但它就是不起作用——我安装了它,在我的Visual studio中没有任何变化,也没有出现新的模板。这个项目彻底失败了。已打开源代码和。。。决定也许我会在stackoverflow中问这个问题:)

FileHelpers只有一个简单的向导,可以手动添加字段。但我有50个字段,这不是我最后一次需要这样做,所以这里首选自动化解决方案。

我相信这个问题以前已经解决过很多次了,有什么帮助吗?

从csv文件结构生成c#模型类

谢谢Bedford,我拿走了你的代码并添加了三件事:

  • 它删除了对属性名称无效的符号。例如,"订单号"将变为"OrderNo"属性
  • 能够添加属性和类属性。在我的情况下,我需要[DimitedRecord(",")]和[FieldOption()],因为我使用的是FileHelpers
  • 有些列没有名称,所以它自己生成名称。命名约定为第10列、第11列,依此类推

最终代码:

public class CsvToClass
{
    public static string CSharpClassCodeFromCsvFile(string filePath, string delimiter = ",", 
        string classAttribute = "", string propertyAttribute = "")
    {
        if (string.IsNullOrWhiteSpace(propertyAttribute) == false)
            propertyAttribute += "'n't";
        if (string.IsNullOrWhiteSpace(propertyAttribute) == false)
            classAttribute += "'n";
        string[] lines = File.ReadAllLines(filePath);
        string[] columnNames = lines.First().Split(',').Select(str => str.Trim()).ToArray();
        string[] data = lines.Skip(1).ToArray();
        string className = Path.GetFileNameWithoutExtension(filePath);
        // use StringBuilder for better performance
        string code = String.Format("{0}public class {1} {{ 'n", classAttribute, className);
        for (int columnIndex = 0; columnIndex < columnNames.Length; columnIndex++)
        {
            var columnName = Regex.Replace(columnNames[columnIndex], @"['s'.]", string.Empty, RegexOptions.IgnoreCase);
            if (string.IsNullOrEmpty(columnName))
                columnName = "Column" + (columnIndex + 1);
            code += "'t" + GetVariableDeclaration(data, columnIndex, columnName, propertyAttribute) + "'n'n";
        }
        code += "}'n";
        return code;
    }
    public static string GetVariableDeclaration(string[] data, int columnIndex, string columnName, string attribute = null)
    {
        string[] columnValues = data.Select(line => line.Split(',')[columnIndex].Trim()).ToArray();
        string typeAsString;
        if (AllDateTimeValues(columnValues))
        {
            typeAsString = "DateTime";
        }
        else if (AllIntValues(columnValues))
        {
            typeAsString = "int";
        }
        else if (AllDoubleValues(columnValues))
        {
            typeAsString = "double";
        }
        else
        {
            typeAsString = "string";
        }
        string declaration = String.Format("{0}public {1} {2} {{ get; set; }}", attribute, typeAsString, columnName);
        return declaration;
    }
    public static bool AllDoubleValues(string[] values)
    {
        double d;
        return values.All(val => double.TryParse(val, out d));
    }
    public static bool AllIntValues(string[] values)
    {
        int d;
        return values.All(val => int.TryParse(val, out d));
    }
    public static bool AllDateTimeValues(string[] values)
    {
        DateTime d;
        return values.All(val => DateTime.TryParse(val, out d));
    }
    // add other types if you need...
}

用法示例:

class Program
{
    static void Main(string[] args)
    {
        var cSharpClass = CsvToClass.CSharpClassCodeFromCsvFile(@"YourFilePath.csv", ",", "[DelimitedRecord('",'")]", "[FieldOptional()]");
        File.WriteAllText(@"OutPutPath.cs", cSharpClass);
    }
}

有一个完整代码和工作示例的链接https://github.com/povilaspanavas/CsvToCSharpClass

您可以使用一个小C#应用程序生成类代码,该应用程序检查每列的所有值。你可以确定哪种类型最窄:

public static string CSharpClassCodeFromCsvFile(string filePath)
{
    string[] lines = File.ReadAllLines(filePath);
    string[] columnNames = lines.First().Split(',').Select(str => str.Trim()).ToArray();
    string[] data = lines.Skip(1).ToArray();
    string className = Path.GetFileNameWithoutExtension(filePath);
    // use StringBuilder for better performance
    string code = String.Format("public class {0} {{ 'n", className);
    for (int columnIndex = 0; columnIndex < columnNames.Length; columnIndex++)
    {
        code += "'t" + GetVariableDeclaration(data, columnIndex, columnNames[columnIndex]) + "'n";
    }
    code += "}'n";
    return code;
}
public static string GetVariableDeclaration(string[] data, int columnIndex, string columnName)
{
    string[] columnValues = data.Select(line => line.Split(',')[columnIndex].Trim()).ToArray();
    string typeAsString;
    if (AllDateTimeValues(columnValues))
    {
        typeAsString = "DateTime";
    }
    else if (AllIntValues(columnValues))
    {
        typeAsString = "int";
    }
    else if (AllDoubleValues(columnValues))
    {
        typeAsString = "double";
    } 
    else
    {
        typeAsString = "string";
    }
    string declaration = String.Format("public {0} {1} {{ get; set; }}", typeAsString, columnName);
    return declaration;
}
public static bool AllDoubleValues(string[] values)
{
    double d;
    return values.All(val => double.TryParse(val, out d));
}
public static bool AllIntValues(string[] values)
{
    int d;
    return values.All(val => int.TryParse(val, out d));
}
public static bool AllDateTimeValues(string[] values)
{
    DateTime d;
    return values.All(val => DateTime.TryParse(val, out d));
}
// add other types if you need...

您可以在此基础上创建一个命令行应用程序,该应用程序可以在自动化解决方案中使用。

您可以在C#中使用dynamic从CSV创建动态模型类。重写自定义DynamicObject类的TryGetMember并使用Indexers。

一个有用的链接:C#Linq到CSV动态对象运行时列名

csv2entity已移动到:

https://github.com/juwikuang/csv2entity

安装指南是readme.md文件。