【问题标题】:Generate c# model class from csv file structure从csv文件结构生成c#模型类
【发布时间】:2014-09-19 12:18:52
【问题描述】:

这里的目标是在输入 csv 文件后,一个神奇的工具会输出 c# 类,其中包含 csv 中的字段。我们来看例子。

输入 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 个字段,这不是我最后一次需要这样做,所以这里首选自动化解决方案。

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

【问题讨论】:

  • 我认为从中创建 XML 文件会非常容易; XML 到类是标准的。
  • 这是一个 MS 建议,可以更笼统地说:msdn.microsoft.com/en-us/library/bb387090.aspx
  • 这个类是在运行时生成的,还是只是在寻找生成代码文件的方法,以便后续编译?
  • 查看“csv2entity”代码,我认为您几乎可以重用他的代码并使其工作。如果您打开 Ezfx.Csv.Templates 项目,您将看到一个名为“Wizard”的文件夹,在该文件夹内有一个名为“ClassWizard”的类。该类正在内存中生成 .cs 文件并将其写入磁盘。该过程非常简单.. 从文件 (CsvConfig) 生成一个 CSV 配置类,然后使用 GetPropertyCode() 方法为每一列 (CsvColumn) 生成一个 C# 属性字符串,然后将其附加到 StringBuilder。我认为你至少可以重用他 80% 的代码。
  • PeterSchneider Schneider:一般来说是可行的,但是如果没有这样的工具来做 csv -> xml 也没有意义,不是吗?您提供的链接只是告诉如何手动操作。这个问题的目标是用自动化的方法得到答案。 stovroz:它只生成一次,而不是在运行时。 DoubleScorpio:我再次查看了源代码并设法编译了它。而且,是的,我可以使用大部分代码,或者只是自己实现所有内容。但也许外面的某个地方有一个已经有效的解决方案?目前看来,您的建议是唯一可行的选择。

标签: c# csv


【解决方案1】:

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

  • 它删除了对属性名称无效的符号。例如“订单号”。将成为“OrderNo”属性。
  • 能够添加属性和类属性。就我而言,我需要 [DelimitedRecord(",")] 和 [FieldOptional()],因为我使用的是 FileHelpers。
  • 有些列没有名称,所以它自己生成名称。命名约定为 Column10、Column11 等。

最终代码:

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

【讨论】:

    【解决方案2】:

    您可以使用一个小 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...
    

    您可以从中创建一个命令行应用程序,该应用程序可用于自动化解决方案。

    【讨论】:

    • 感谢您的代码。拿了它,添加了一些修改,它的工作原理。我的修改如下所述,在我的回答中。
    • 当然。您应该自定义它以适合您自己的应用程序,这只是一个基本解决方案。您可以使用相同的技术来生成实体框架类。
    【解决方案3】:

    您可以在 C# 中使用 dynamic 从 CSV 创建动态模型类。覆盖自定义 DynamicObject 类的 TryGetMember 并使用索引器。

    一个有用的链接: C# Linq to CSV Dynamic Object runtime column name

    【讨论】:

      【解决方案4】:

      csv2entity 已移至:

      https://github.com/juwikuang/csv2entity

      安装指南是readme.md文件。

      【讨论】:

        猜你喜欢
        • 2011-12-01
        • 1970-01-01
        • 2016-12-08
        • 2015-04-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-11-23
        • 2015-05-31
        相关资源
        最近更新 更多