使用C#将csv文件转换为json

34
我想知道是否有人已经编写了一个使用C#将CSV文件转换为Json的实用程序。根据之前在stackoverflow上的一个问题,我知道有这个很好的实用程序 - https://github.com/cparker15/csv-to-json,目前我打算参考它,但如果有现有的C#实现将会非常有帮助。

你可以很容易地将那段JS代码转换成C#,你也可以保留var关键字。 - tlehman
是的,这就是我计划要做的,但我能找到的唯一一篇文章是在 MSDN 上的 http://social.msdn.microsoft.com/Forums/en-US/vsto/thread/b6e8a28c-6760-4e86-a1aa-e2ce9ec36380/,它使用了 Office.Interop,而我是 C# 的新手,对此并不熟悉。我需要使用它吗?还是直接尝试翻译 js 实用程序就可以了?谢谢! - user1427026
6
我建议你不要在CSV文件上使用Office.Interop,因为CSV只是文本,这样做过于复杂。 - tlehman
18个回答

24

如果您可以使用 System.Web.Extensions,那么类似这样的代码可以起作用:

var csv = new List<string[]>(); // or, List<YourClass>
var lines = System.IO.File.ReadAllLines(@"C:\file.txt");
foreach (string line in lines)
    csv.Add(line.Split(',')); // or, populate YourClass          
string json = new 
    System.Web.Script.Serialization.JavaScriptSerializer().Serialize(csv);

你可能对csv文件有更复杂的解析要求,也可能有一个类来封装一行数据,但关键是,一旦你拥有了一系列行数据的集合,你只需要一行代码就可以将其序列化为JSON。


如果文件过大,通常会导致错误。 例如:使用JSON JavaScriptSerializer 进行序列化或反序列化时发生的错误。字符串的长度超出了 maxJsonLength 属性设置的值。 - Kurkula

23

Cinchoo ETL是一个开源库,可以通过几行代码轻松地将CSV转换为JSON。

以下是一个CSV示例:

Id, Name, City
1, Tom, NY
2, Mark, NJ
3, Lou, FL
4, Smith, PA
5, Raj, DC

示例代码,

string csv = @"Id, Name, City
1, Tom, NY
2, Mark, NJ
3, Lou, FL
4, Smith, PA
5, Raj, DC
";

StringBuilder sb = new StringBuilder();
using (var p = ChoCSVReader.LoadText(csv)
    .WithFirstLineHeader()
    )
{
    using (var w = new ChoJSONWriter(sb))
        w.Write(p);
}

Console.WriteLine(sb.ToString());

输出 JSON:

[
 {
  "Id": "1",
  "Name": "Tom",
  "City": "NY"
 },
 {
  "Id": "2",
  "Name": "Mark",
  "City": "NJ"
 },
 {
  "Id": "3",
  "Name": "Lou",
  "City": "FL"
 },
 {
  "Id": "4",
  "Name": "Smith",
  "City": "PA"
 },
 {
  "Id": "5",
  "Name": "Raj",
  "City": "DC"
 }
]

示例代码:https://dotnetfiddle.net/pclnsT

查看CodeProject文章以获取更多帮助。

更新: 如果您的CSV文件具有重复的列名或没有列名,请使用以下步骤生成JSON文件。

string csv = @"Id, Name, 
1, Tom, NY
2, Mark, NJ
3, Lou, FL
4, Smith, PA
5, Raj, DC
";

StringBuilder sb = new StringBuilder();
using (var p = ChoCSVReader.LoadText(csv)
    .WithField("Id", position: 1)
    .WithField("Name", position: 2)
    .WithField("City", position: 3)
    .WithFirstLineHeader(true)
    )
{
    using (var w = new ChoJSONWriter(sb))
        w.Write(p);
}

Console.WriteLine(sb.ToString());

示例代码: https://dotnetfiddle.net/pP5Du6

免责声明:我是这个库的作者。


1
使用 ChoCSVReader / ChoJSONReader 构造函数。 - Cinchoo
5
这种方法使用了动态模型。foreach (dynamic e in new ChoCSVReader(csv).WithFirstLineHeader()) { System.Diagnostics.Debug.Write(e.CSVColumnName); }。欲了解更多信息,请访问CodeProject文章/GitHub wiki。 - Cinchoo
1
这个库很好,它能够完成它所设计的功能。但是当CSV文件中的键值包含一些特殊字符时,比如我有一个叫做loerem.lipsm的键值,它会将我的键值转换为loerem_lipsm。是否有任何解决方法? - Yagnesh Khamar
1
这个库让我节省了很多时间,将一堆csv文件转换成json格式。非常简单易用。感谢你的库。 - blahblah
1
这是一个更好的答案,因为几乎所有其他解决方案都假定逗号是唯一的分隔符。在csv中,引用包含分隔符的值是很常见的。下面有一个使用此库进行操作的示例,但我使用了上面的示例,并将“.WithFirstLineHeader()”更改为“.WithFirstLineHeader()。QuoteAllFields()”,它完美地处理了我的测试数据。话虽如此,如果该值包含行分隔符,则无法正常工作,但也可能有一些控制它的方法,我只是没有寻找它。干得好Cinchoo... - Jay13
显示剩余12条评论

17

我使用了Dictionary并使用Newtonsoft返回了JSON。

public string ConvertCsvFileToJsonObject(string path) 
{
    var csv = new List<string[]>();
    var lines = File.ReadAllLines(path);

    foreach (string line in lines)
        csv.Add(line.Split(','));

    var properties = lines[0].Split(',');

    var listObjResult = new List<Dictionary<string, string>>();

    for (int i = 1; i < lines.Length; i++)
    {
        var objResult = new Dictionary<string, string>();
        for (int j = 0; j < properties.Length; j++)
            objResult.Add(properties[j], csv[i][j]);

        listObjResult.Add(objResult);
    }

    return JsonConvert.SerializeObject(listObjResult); 
}

4
我喜欢这个答案。它使用了字典并保持了通用性,使得它可以适用于几乎任何CSV文件。做得好! - justdan23
感谢 @justdan23 =) - Tasso Mello
2
这个程序如何处理CSV中的转义逗号? - Nick
这是一个具体的场景,也是可能的。很可能会用逗号来分割句子。我会考虑一下。 - Tasso Mello
1
@TassoMello 如果标题相同,这将失败。 - Vineet Agarwal

5
Install Nuget package NewtonSoft.Json
Add reference dll Microsoft.VisualBasic

using System.Linq;
using Newtonsoft.Json;
using Microsoft.VisualBasic.FileIO;
using System.IO;
using System;
using System.Collections.Generic;
using System.Globalization;

namespace Project
{
    public static class Program
    {
        public static void Main(string[] args)
        {
            string CSVpath = @"D:\New Folder\information.csv";
            string analyticsData = ReadFile(CSVpath);
        }

        private static string ReadFile(string filePath)
        {
            string payload = "";
            try
            {
                if (!string.IsNullOrWhiteSpace(filePath) && File.Exists(filePath) && Path.GetExtension(filePath).Equals(".csv", StringComparison.InvariantCultureIgnoreCase))
                {
                    string[] lines = File.ReadAllLines(filePath);

                    if (lines != null && lines.Length > 1)
                    {
                        var headers = GetHeaders(lines.First());
                        payload = GetPayload(headers, lines.Skip(1));
                    }
                }
            }
            catch (Exception exp)
            {
            }
            return payload;
        }

        private static IEnumerable<string> GetHeaders(string data)
        {
            IEnumerable<string> headers = null;

            if (!string.IsNullOrWhiteSpace(data) && data.Contains(','))
            {
                headers = GetFields(data).Select(x => x.Replace(" ", ""));
            }
            return headers;
        }

        private static string GetPayload(IEnumerable<string> headers, IEnumerable<string> fields)
        {
            string jsonObject = "";
            try
            {
                var dictionaryList = fields.Select(x => GetField(headers, x));
                jsonObject = JsonConvert.SerializeObject(dictionaryList);
            }
            catch (Exception ex)
            {
            }
            return jsonObject;
        }

        private static Dictionary<string, string> GetField(IEnumerable<string> headers, string fields)
        {
            Dictionary<string, string> dictionary = null;

            if (!string.IsNullOrWhiteSpace(fields))
            {
                var columns = GetFields(fields);

                if (columns != null && headers != null && columns.Count() == headers.Count())
                {
                    dictionary = headers.Zip(columns, (x, y) => new { x, y }).ToDictionary(item => item.x, item => item.y);
                }
            }
            return dictionary;
        }

        public static IEnumerable<string> GetFields(string line)
        {
            IEnumerable<string> fields = null;
            using (TextReader reader = new StringReader(line))
            {
                using (TextFieldParser parser = new TextFieldParser(reader))
                {
                    parser.TextFieldType = FieldType.Delimited; parser.SetDelimiters(","); fields = parser.ReadFields();
                }
            }
            return fields;
        }
    }
}

2
谢谢。你能分享一下“GetFields”函数吗?好像它没有了。 - Alex Javarotti
1
@AlexJavarotti,代码中已添加了缺失的方法。 - Anand Kishore
TextFieldParser 方法缺失?您能否在您的回答中添加这个方法? - Abdul Haseeb

3

我使用ChoETL

using ChoETL;
using System.IO;

public class FromCSVtoJSON
{
    public FromCSVtoJSON()
    {

    }

    public void convertFile(string inputFile, string outputFile)
    {
        using (var writer = new ChoJSONWriter(outputFile))
        {
            using (var reader = new ChoCSVReader(inputFile).WithFirstLineHeader())
            {
                writer.Write(reader);
            }
        }
    }
}

当两个标题具有相同的名称或没有名称时,此操作将失败。 - A X
你使用的ChoETL版本是哪个?因为对我来说,ChoJSONWriter无法识别,会导致错误。 - BUMA
安装 ChoETL.JSON 包 - Cinchoo

3

仅依赖于 Newtonsoft.Json,这里是一个帮助方法,给出一个 CSV 行数组,第一行是标题。

    public static IEnumerable<JObject> CsvToJson(IEnumerable<string> csvLines)
    {
        var csvLinesList = csvLines.ToList();

        var header = csvLinesList[0].Split(',');
        for (int i = 1; i < csvLinesList.Count; i++)
        {
            var thisLineSplit = csvLinesList[i].Split(',');
            var pairedWithHeader = header.Zip(thisLineSplit, (h, v) => new KeyValuePair<string, string>(h, v));

            yield return new JObject(pairedWithHeader.Select(j => new JProperty(j.Key, j.Value)));
        }
    }

有关如何将从文件读取的 csv 字符串变量转换为 string 类型的 IEnumerable<string> 集合的任何指导? - benhorgen

2

对bc3tech所提供的解决方案进行了小的变化,即避免使用外部依赖(如Newtonsoft.Json),而改为使用System.Text.Json(dotnet core 3+)

public static IEnumerable<string> CsvToJson(string fileName, char delim = '|')
{
    var lines = File.ReadLines(fileName); 
    var hdr = new List<string>(lines.First().Trim().Split(delim));
    foreach (var l in lines.Skip(1).Where(l => (l.Trim() != String.Empty))) 
    {
        var val = l.Trim().Split(delim);
        var ds = hdr.Zip(val, (k, v) => new { k, v }).ToDictionary(x => x.k, x => x.v);
        yield return JsonSerializer.Serialize(ds);
    }
}

2

同一篇SO答案中,有一个链接指向这篇文章

CsvToJson扩展方法

/// <summary>
/// Converts a CSV string to a Json array format.
/// </summary>
/// <remarks>First line in CSV must be a header with field name columns.</remarks>
/// <param name="value"></param>
/// <returns></returns>
public static string CsvToJson(this string value)
{
    // Get lines.
    if (value == null) return null;
    string[] lines = value.Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
    if (lines.Length < 2) throw new InvalidDataException("Must have header line.");

    // Get headers.
    string[] headers = lines.First().SplitQuotedLine(new char[] { ',' }, false);

    // Build JSON array.
    StringBuilder sb = new StringBuilder();
    sb.AppendLine("[");
    for (int i = 1; i < lines.Length; i++)
    {
        string[] fields = lines[i].SplitQuotedLine(new char[] { ',', ' ' }, true, '"', false);
        if (fields.Length != headers.Length) throw new InvalidDataException("Field count must match header count.");
        var jsonElements = headers.Zip(fields, (header, field) => string.Format("{0}: {1}", header, field)).ToArray();
        string jsonObject = "{" + string.Format("{0}", string.Join(",", jsonElements)) + "}";
        if (i < lines.Length - 1)
            jsonObject += ",";
        sb.AppendLine(jsonObject);
    }
    sb.AppendLine("]");
    return sb.ToString();
}

似乎存在一个问题,就是在上述扩展中调用的某些方法所在的位置有问题(请参见原博客文章的评论),但它应该能够帮助您完成大部分工作。

编辑 这里有另一个SO答案,关于如何拆分CSV行。您可以使用其中建议的正则表达式解决方案之一来创建自己的SplitQuotedLine方法:

public static string SplitQuotedLine(this string value, char separator, bool quotes) {
    // Use the "quotes" bool if you need to keep/strip the quotes or something...
    var s = new StringBuilder();
    var regex = new Regex("(?<=^|,)(\"(?:[^\"]|\"\")*\"|[^,]*)");
    foreach (Match m in regex.Matches(value)) {
        s.Append(m.Value);
    }
    return s.ToString();
}

如果我有任何错误,请谅解,我没有测试上述内容。

此外,看起来Zip是一个LINQ扩展方法,因此这个问题得到了解决。


是的,但是SplitQuotedLine - 这是一种用户自定义的方法,在博客文章中缺失。 - user1427026
即使不知道该方法内部是如何运行的,仍然很容易看出它只是在分割字符串。lines.First().Split(',')本质上会执行相同的操作 - 它可能只会测试是否存在带引号的逗号,并且可能还会去除引号。 Zip扩展方法可能需要更多的了解。就像我说的,它可以让你完成大部分工作。不过你是因为刚开始学习C#,所以想要得到100%完整的东西,对吧? - Tim Hobbs
当然,我希望它能奏效。就像我说的那样,我只是举了一个例子,我没有尝试过。它应该可以工作,但可能需要一些小调整,但不能保证成功! :) - Tim Hobbs
SplitQuotedLine 是返回 string [] 吗? - Rajib Chy

2

这是我的代码...它可以在几个世纪内解析9k个CSV记录。哈哈

class CSVTOJSON
{
    public string ConvertToJSON()
    {
        string json = string.Empty;
        string csv = string.Empty;

        using (StreamReader reader = new StreamReader("data.csv"))
        {
            csv = reader.ReadToEnd();
        }

        string[] lines = csv.Split(new string[] { "\n" }, System.StringSplitOptions.None);

        if (lines.Length > 1)
        {
            // parse headers
            string[] headers = lines[0].Split(',');

            StringBuilder sbjson = new StringBuilder();
            sbjson.Clear();
            sbjson.Append("[");

            // parse data
            for (int i = 1; i < lines.Length; i++)
            {
                if (string.IsNullOrWhiteSpace(lines[i])) continue;
                if (string.IsNullOrEmpty(lines[i])) continue;

                sbjson.Append("{");

                string[] data = lines[i].Split(',');

                for (int h = 0; h < headers.Length; h++)
                {
                    sbjson.Append(
                        $"\"{headers[h]}\": \"{data[h]}\"" + (h < headers.Length - 1 ? "," : null)
                    );
                }

                sbjson.Append("}" + (i < lines.Length - 1 ? "," : null));
            }

            sbjson.Append("]");

            json = sbjson.ToString();
        }

        return json;
    }
}

但它有效。
控制台日志:
Converting CSV to JSON
CSV has 9486 data
Total duration converting CSV to JSON: 00:00:00.0775373

1

我看大多数人只是假设解析CSV文件只需要简单地在每列之间拆分逗号分隔符,但以下格式仍然是有效的CSV。

"aaa","bbb","ccc"
"z, z",yyy,xxx

微软.VisualBasic.FileIO中有一个很好的类可以正确处理CSV文件格式。我将其与JSON.NET结合使用得出了解决方案。

   public static string? CsvToJson(string input, string delimiter)
   {
        using (TextFieldParser parser = new TextFieldParser(
                 new MemoryStream(Encoding.UTF8.GetBytes(input))))
        {
            parser.Delimiters = new string[] { delimiter };
            string[]? headers = parser.ReadFields();
            if (headers == null) return null;
            string[]? row;
            string comma = "";
            var sb = new StringBuilder((int)(input.Length * 1.1));
            sb.Append("[");
            while ((row = parser.ReadFields()) != null)
            {
                var dict = new Dictionary<string, object>();
                for (int i = 0; row != null && i < row.Length; i++)
                    dict[headers[i]] = row[i];

                var obj = JsonConvert.SerializeObject(dict);
                sb.Append(comma + obj);
                comma = ",";
            }
            return sb.Append("]").ToString();
        }
    }

使用方法

var str = @"Header1,""Header,,2 "",Data3
1,444.00, ""Liang, Jerry""
0,""5,550"",Jerry
";

var json = CsvToJson(str, ",");

结果

[
  {
    "Header1": "1",
    "Header,,2": "444.00",
    "Data3": "Liang, Jerry"
  },
  {
    "Header1": "0441",
    "Header,,2": "5,550",
    "Data3": "Jerry"
  }
]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接