我如何将 JSON 从输入流写入 S3 文件而不将其全部加载到内存中?
How would I write JSON to a S3 file from an input stream without loading it all into memory?
假设我想做一些简单的事情,例如将 CSV 文件转换为 JSON 文件。一种简单的方法是将整个 CSV 文件读入内存,然后使用 JSON.NET.
序列化结果
First,Last,Age
Jane,Doe,45
John,Smith,60
会变成:
[
{
"First": "Jane",
"Last": "Doe",
"Age: 45
},
{
"First": "John",
"Last": "Smith",
"Age: 60
}
]
但是,如果我的资源有限且数据集非常大,最好从 CSV 文件中一次读取 1000 行并将其写入 JSON 输出文件。然后继续附加到文件,而不必将整个数据集读入内存。
我可以想到一些低级的方法,例如手动 adding/removing 括号和逗号。但我希望有人能提出更优雅的方法。
假设您有两个流,一个 Stream csvStream
用于要读取的 CSV 文件,一个 Stream jsonStream
用于要写入的 JSON 文件,您可以从 CSV 流式传输到 JSON 通过组合微软的 TextFieldParser
with Json.NET's JsonTextWriter
像这样:
public static partial class JsonExtensions
{
const int buffersize = 4096;
static readonly UTF8Encoding defaultEncoding = new UTF8Encoding(false, true);
public static void CopyCSVToJson(Stream csvStream, Stream jsonStream, Formatting formatting = Formatting.Indented, Encoding encoding = default)
{
encoding = encoding ?? defaultEncoding;
using (var textReader = new StreamReader(csvStream, encoding, true, buffersize, true))
using (var textWriter = new StreamWriter(jsonStream, encoding, buffersize, true))
CopyCSVToJson(textReader, textWriter, formatting);
}
public static void CopyCSVToJson(TextReader csvTextReader, TextWriter jsonTextWriter, Formatting formatting = Formatting.Indented)
{
using (var parser = new TextFieldParser(csvTextReader) { Delimiters = new[] { "," } })
{
if (parser.EndOfData)
return;
var headers = parser.ReadFields();
using (var jsonWriter = new JsonTextWriter(jsonTextWriter) { Formatting = formatting })
{
jsonWriter.WriteStartArray();
while (!parser.EndOfData)
{
var fields = parser.ReadFields();
jsonWriter.WriteStartObject();
foreach (var (name, value) in headers.Zip(fields))
{
jsonWriter.WritePropertyName(name);
// Check if the value is an integer, a decimal, or a Boolean.
// Could add BigInteger if needed
if (long.TryParse(value, out var l))
jsonWriter.WriteValue(l);
else if (decimal.TryParse(value, out var d))
jsonWriter.WriteValue(d);
else if (bool.TryParse(value, out var b))
jsonWriter.WriteValue(b);
else
jsonWriter.WriteValue(value);
}
jsonWriter.WriteEndObject();
}
jsonWriter.WriteEndArray();
}
}
}
}
然后你可以调用上面的例程,例如如下:
using (var csvStream = File.OpenRead(csvFileName))
using (var jsonStream = File.OpenWrite(jsonFileName))
{
JsonExtensions.CopyCSVToJson(csvStream, jsonStream);
}
TextFieldParser
位于 Microsoft.VisualBasic.Core.dll
和 Microsoft.VisualBasic.dll
的 Microsoft.VisualBasic.FileIO
命名空间中。它完全可以从 c# 使用。
复制方法假定第一行对应于 属性 名称(如您的问题所示),并尝试在写入之前将每个单元格解析为整数、小数或布尔值单元格的字符串值。
复制方法假定两个流具有相同的编码。如果需要,您显然可以概括这一点。
演示 fiddle here.
假设我想做一些简单的事情,例如将 CSV 文件转换为 JSON 文件。一种简单的方法是将整个 CSV 文件读入内存,然后使用 JSON.NET.
序列化结果First,Last,Age
Jane,Doe,45
John,Smith,60
会变成:
[
{
"First": "Jane",
"Last": "Doe",
"Age: 45
},
{
"First": "John",
"Last": "Smith",
"Age: 60
}
]
但是,如果我的资源有限且数据集非常大,最好从 CSV 文件中一次读取 1000 行并将其写入 JSON 输出文件。然后继续附加到文件,而不必将整个数据集读入内存。
我可以想到一些低级的方法,例如手动 adding/removing 括号和逗号。但我希望有人能提出更优雅的方法。
假设您有两个流,一个 Stream csvStream
用于要读取的 CSV 文件,一个 Stream jsonStream
用于要写入的 JSON 文件,您可以从 CSV 流式传输到 JSON 通过组合微软的 TextFieldParser
with Json.NET's JsonTextWriter
像这样:
public static partial class JsonExtensions
{
const int buffersize = 4096;
static readonly UTF8Encoding defaultEncoding = new UTF8Encoding(false, true);
public static void CopyCSVToJson(Stream csvStream, Stream jsonStream, Formatting formatting = Formatting.Indented, Encoding encoding = default)
{
encoding = encoding ?? defaultEncoding;
using (var textReader = new StreamReader(csvStream, encoding, true, buffersize, true))
using (var textWriter = new StreamWriter(jsonStream, encoding, buffersize, true))
CopyCSVToJson(textReader, textWriter, formatting);
}
public static void CopyCSVToJson(TextReader csvTextReader, TextWriter jsonTextWriter, Formatting formatting = Formatting.Indented)
{
using (var parser = new TextFieldParser(csvTextReader) { Delimiters = new[] { "," } })
{
if (parser.EndOfData)
return;
var headers = parser.ReadFields();
using (var jsonWriter = new JsonTextWriter(jsonTextWriter) { Formatting = formatting })
{
jsonWriter.WriteStartArray();
while (!parser.EndOfData)
{
var fields = parser.ReadFields();
jsonWriter.WriteStartObject();
foreach (var (name, value) in headers.Zip(fields))
{
jsonWriter.WritePropertyName(name);
// Check if the value is an integer, a decimal, or a Boolean.
// Could add BigInteger if needed
if (long.TryParse(value, out var l))
jsonWriter.WriteValue(l);
else if (decimal.TryParse(value, out var d))
jsonWriter.WriteValue(d);
else if (bool.TryParse(value, out var b))
jsonWriter.WriteValue(b);
else
jsonWriter.WriteValue(value);
}
jsonWriter.WriteEndObject();
}
jsonWriter.WriteEndArray();
}
}
}
}
然后你可以调用上面的例程,例如如下:
using (var csvStream = File.OpenRead(csvFileName))
using (var jsonStream = File.OpenWrite(jsonFileName))
{
JsonExtensions.CopyCSVToJson(csvStream, jsonStream);
}
TextFieldParser
位于Microsoft.VisualBasic.Core.dll
和Microsoft.VisualBasic.dll
的Microsoft.VisualBasic.FileIO
命名空间中。它完全可以从 c# 使用。复制方法假定第一行对应于 属性 名称(如您的问题所示),并尝试在写入之前将每个单元格解析为整数、小数或布尔值单元格的字符串值。
复制方法假定两个流具有相同的编码。如果需要,您显然可以概括这一点。
演示 fiddle here.