[英]Line delimited json serializing and de-serializing
我正在使用 JSON.NET 和 C# 5。我需要將對象列表序列化/反序列化為行分隔的 json。 http://en.wikipedia.org/wiki/Line_Delimited_JSON 。 例子,
{"some":"thing1"}
{"some":"thing2"}
{"some":"thing3"}
和
{"kind": "person", "fullName": "John Doe", "age": 22, "gender": "Male", "citiesLived": [{ "place": "Seattle", "numberOfYears": 5}, {"place": "Stockholm", "numberOfYears": 6}]}
{"kind": "person", "fullName": "Jane Austen", "age": 24, "gender": "Female", "citiesLived": [{"place": "Los Angeles", "numberOfYears": 2}, {"place": "Tokyo", "numberOfYears": 2}]}
為什么我需要,因為它的 Google BigQuery 要求https://cloud.google.com/bigquery/preparing-data-for-bigquery
更新:我發現的一種方法是單獨序列化每個對象並在最后加入換行符。
您可以通過使用JsonTextReader
手動解析您的 JSON 並將SupportMultipleContent
標志設置為true
來true
。
如果我們查看您的第一個示例,並創建一個名為Foo
的 POCO:
public class Foo
{
[JsonProperty("some")]
public string Some { get; set; }
}
這是我們解析它的方式:
var json = "{\"some\":\"thing1\"}\r\n{\"some\":\"thing2\"}\r\n{\"some\":\"thing3\"}";
var jsonReader = new JsonTextReader(new StringReader(json))
{
SupportMultipleContent = true // This is important!
};
var jsonSerializer = new JsonSerializer();
while (jsonReader.Read())
{
Foo foo = jsonSerializer.Deserialize<Foo>(jsonReader);
}
如果您想要項目列表作為結果,只需將每個項目添加到while
循環內的列表中即可。
listOfFoo.Add(jsonSerializer.Deserialize<Foo>(jsonReader));
注意:對於 Json.Net 10.0.4 及更高版本,相同的代碼也支持逗號分隔的 JSON 條目,請參閱如何反序列化狡猾的 JSON(帶有不正確引用的字符串和缺少括號)? )
為了使用 .NET 5 (C# 9) 和System.Text.Json.JsonSerializer
類以及“大”數據實現,我編寫了用於流處理的代碼。
使用System.IO.Pipelines
擴展包,這是非常有效的。
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipelines;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
class Program
{
static readonly byte[] NewLineChars = {(byte)'\r', (byte)'\n'};
static readonly byte[] WhiteSpaceChars = {(byte)'\r', (byte)'\n', (byte)' ', (byte)'\t'};
private static async Task Main()
{
JsonSerializerOptions jsonOptions = new(JsonSerializerDefaults.Web);
var json = "{\"some\":\"thing1\"}\r\n{\"some\":\"thing2\"}\r\n{\"some\":\"thing3\"}";
var contentStream = new MemoryStream(Encoding.UTF8.GetBytes(json));
var pipeReader = PipeReader.Create(contentStream);
await foreach (var foo in ReadItemsAsync<Foo>(pipeReader, jsonOptions))
{
Console.WriteLine($"foo: {foo.Some}");
}
}
static async IAsyncEnumerable<TValue> ReadItemsAsync<TValue>(PipeReader pipeReader, JsonSerializerOptions jsonOptions = null)
{
while (true)
{
var result = await pipeReader.ReadAsync();
var buffer = result.Buffer;
bool isCompleted = result.IsCompleted;
SequencePosition bufferPosition = buffer.Start;
while (true)
{
var(value, advanceSequence) = TryReadNextItem<TValue>(buffer, ref bufferPosition, isCompleted, jsonOptions);
if (value != null)
{
yield return value;
}
if (advanceSequence)
{
pipeReader.AdvanceTo(bufferPosition, buffer.End); //advance our position in the pipe
break;
}
}
if (isCompleted)
yield break;
}
}
static (TValue, bool) TryReadNextItem<TValue>(ReadOnlySequence<byte> sequence, ref SequencePosition sequencePosition, bool isCompleted, JsonSerializerOptions jsonOptions)
{
var reader = new SequenceReader<byte>(sequence.Slice(sequencePosition));
while (!reader.End) // loop until we've come to the end or read an item
{
if (reader.TryReadToAny(out ReadOnlySpan<byte> itemBytes, NewLineChars, advancePastDelimiter: true))
{
sequencePosition = reader.Position;
if (itemBytes.TrimStart(WhiteSpaceChars).IsEmpty)
{
continue;
}
return (JsonSerializer.Deserialize<TValue>(itemBytes, jsonOptions), false);
}
else if (isCompleted)
{
// read last item
var remainingReader = sequence.Slice(reader.Position);
using var memoryOwner = MemoryPool<byte>.Shared.Rent((int)reader.Remaining);
remainingReader.CopyTo(memoryOwner.Memory.Span);
reader.Advance(remainingReader.Length); // advance reader to the end
sequencePosition = reader.Position;
if (!itemBytes.TrimStart(WhiteSpaceChars).IsEmpty)
{
return (JsonSerializer.Deserialize<TValue>(memoryOwner.Memory.Span, jsonOptions), true);
}
else
{
return (default, true);
}
}
else
{
// no more items in sequence
break;
}
}
// PipeReader needs to read more
return (default, true);
}
}
public class Foo
{
public string Some
{
get;
set;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.