CSharp · Console
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization.Formatters;
using System.IO;
using System.Data;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
// https://stackoverflow.com/questions/48561403/convert-json-to-newline-delimit-json
public static class JsonExtensions
{
public static void ToNewlineDelimitedJson(Stream readStream, Stream writeStream)
{
var encoding = new UTF8Encoding(false, true);
// Let caller dispose the underlying streams.
using (var textReader = new StreamReader(readStream, encoding, true, 1024, true))
using (var textWriter = new StreamWriter(writeStream, encoding, 1024, true))
{
ToNewlineDelimitedJson(textReader, textWriter);
}
}
public static void ToNewlineDelimitedJson(TextReader textReader, TextWriter textWriter)
{
using (var jsonReader = new JsonTextReader(textReader) { CloseInput = false, DateParseHandling = DateParseHandling.None })
{
ToNewlineDelimitedJson(jsonReader, textWriter);
}
}
enum State { BeforeArray, InArray, AfterArray };
public static void ToNewlineDelimitedJson(JsonReader jsonReader, TextWriter textWriter)
{
var state = State.BeforeArray;
do
{
if (jsonReader.TokenType == JsonToken.Comment || jsonReader.TokenType == JsonToken.None || jsonReader.TokenType == JsonToken.Undefined || jsonReader.TokenType == JsonToken.PropertyName)
{
// Do nothing
}
else if (state == State.BeforeArray && jsonReader.TokenType == JsonToken.StartArray)
{
state = State.InArray;
}
else if (state == State.InArray && jsonReader.TokenType == JsonToken.EndArray)
{
state = State.AfterArray;
}
else
{
// Formatting.None is the default; I set it here for clarity.
using (var jsonWriter = new JsonTextWriter(textWriter) { Formatting = Formatting.None, CloseOutput = false })
{
jsonWriter.WriteToken(jsonReader);
}
// http://specs.okfnlabs.org/ndjson/
// Each JSON text MUST conform to the [RFC7159] standard and MUST be written to the stream followed by the newline character \n (0x0A).
// The newline charater MAY be preceeded by a carriage return \r (0x0D). The JSON texts MUST NOT contain newlines or carriage returns.
textWriter.Write("\n");
// Root value wasn't an array after all, so end writing with one item.
if (state == State.BeforeArray)
state = State.AfterArray;
}
}
while (jsonReader.Read() && state != State.AfterArray);
}
}
class TestClass
{
public static void Test()
{
var fromFileName = "fromFile.json";
var toFileName = "toFile.json";
try
{
File.WriteAllText(fromFileName, GetJson());
using (var readStream = File.OpenRead(fromFileName))
using (var writeStream = File.Open(toFileName, FileMode.Create))
{
JsonExtensions.ToNewlineDelimitedJson(readStream, writeStream);
}
Console.WriteLine("\nTesting file streaming: ");
Console.WriteLine(File.ReadAllText(toFileName));
var json = GetJson();
Console.WriteLine("\nTesting in-memory streaming: ");
Test(json);
Test(JToken.Parse(json).ToString(Formatting.Indented));
Test("null");
Test("[[null]]");
}
finally
{
try
{
if (File.Exists(fromFileName))
File.Delete(fromFileName);
}
catch (Exception)
{
}
try
{
if (File.Exists(toFileName))
File.Delete(toFileName);
}
catch (Exception)
{
}
}
Console.WriteLine("\nDone");
}
private static void Test(string json)
{
using (var reader = new StringReader(json))
using (var writer = new StringWriter())
{
JsonExtensions.ToNewlineDelimitedJson(reader, writer);
Console.WriteLine(writer);
}
var root1 = new JArray(JToken.Parse(json), JToken.Parse(json));
using (var reader = root1[0].CreateReader())
using (var writer = new StringWriter())
{
JsonExtensions.ToNewlineDelimitedJson(reader, writer);
Console.WriteLine(writer);
}
var root2 = new JObject(new JProperty("Data", JToken.Parse(json)));
using (var reader = root2.CreateReader())
{
while (reader.TokenType != JsonToken.StartObject)
reader.Read();
using (var writer = new StringWriter())
{
JsonExtensions.ToNewlineDelimitedJson(reader, writer);
Console.WriteLine(writer);
}
}
using (var reader = root2.Property("Data").CreateReader())
using (var writer = new StringWriter())
{
JsonExtensions.ToNewlineDelimitedJson(reader, writer);
Console.WriteLine(writer);
}
}
static string GetJson()
{
var json = @"[{""DashboardCategoryId"": 1,""BookingCategoryId"": 3,""BookingChannelId"": 4,""ForDate"": ""2018-01-04"",""BookingCount"": 3,""QuoteCount"": 0,""TotalRevenue"": 2900.5,""AverageDailyRental"": 391.6,""AverageStayLength"": 5,""MarketingCost"": 293,""InlineCost"": 0,""BookingWindows"": [{""DaysRange"": ""31-60 Days"",""BookingNumber"":2},{""DaysRange"": ""Greater Than 1 year"",""BookingNumber"": 1}]},
{""DashboardCategoryId"": 1,""BookingCategoryId"": 2,""BookingChannelId"": 26,""ForDate"": ""2018-01-04"",""BookingCount"": 2,""QuoteCount"": 0,""TotalRevenue"": 2900.5,""AverageDailyRental"": 391.6,""AverageStayLength"": 5,""MarketingCost"": 293,""InlineCost"": 0,""BookingWindows"": [{""DaysRange"": ""61-120 Days"",""BookingNumber"":1},{""DaysRange"": ""8-14"",""BookingNumber"": 1}]}
]";
return json;
}
}
public class Program
{
public static void Main()
{
Console.WriteLine("Environment version: " + Environment.Version);
Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine();
try
{
TestClass.Test();
}
catch (Exception ex)
{
Console.WriteLine("Failed with unhandled exception: ");
Console.WriteLine(ex);
throw;
}
}
}
public class AssertionFailedException : System.Exception
{
public AssertionFailedException() : base() { }
public AssertionFailedException(string s) : base(s) { }
}
public static class Assert
{
public static void IsTrue(bool value)
{
IsTrue(value, "failed");
}
public static void IsTrue(bool value, string message)
{
if (value == false)
throw new AssertionFailedException(message ?? "failed");
}
}