using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Collections.ObjectModel;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;
public static partial class JsonExtensions
public static string [] SplitJsonFile(string fileName, string [] splitPath, Func<string, string, int, string, string> nameCreator)
List<string> fileNames = new List<string>();
var name = Path.GetFileNameWithoutExtension(fileName);
var ext = Path.GetExtension(fileName);
var directory = Path.GetDirectoryName(fileName);
Func<int, TextWriter> createStream = (i) =>
var newName = nameCreator(directory, name, i, ext);
var writer = new StreamWriter(newName, false, Encoding.UTF8);
using (var reader = new StreamReader(fileName, Encoding.UTF8))
JsonExtensions.SplitJson(reader,splitPath, 1, createStream, Formatting.Indented);
return fileNames.ToArray();
public static void SplitJson(TextReader textReader, IList<string> splitPath, long maxItems, Func<int, TextWriter> createStream, Formatting formatting)
if (splitPath == null || createStream == null || textReader == null)
throw new ArgumentNullException();
if (splitPath.Count < 1 || maxItems < 1)
throw new ArgumentException();
using (var reader = new JsonTextReader(textReader))
List<JsonWriter> writers = new ();
List<ParentToken> parentTokens = new ();
SplitJson(reader, splitPath, 0, maxItems, createStream, formatting, parentTokens, writers);
foreach (IDisposable writer in writers)
public ParentToken(JsonToken tokenType, IList<JToken> prefixTokens = default) => (this.TokenType, this._prefixTokens) = (tokenType, prefixTokens);
readonly IList<JToken> _prefixTokens;
public JsonToken TokenType { get; }
public IList<JToken> PrefixTokens => _prefixTokens ?? Array.Empty<JToken>();
static JsonWriter AddWriter(List<JsonWriter> writers, List<ParentToken> parentTokens, Func<int, TextWriter> createStream, Formatting formatting)
var writer = new JsonTextWriter(createStream(writers.Count)) { Formatting = formatting, AutoCompleteOnClose = false };
foreach (var parent in parentTokens)
switch (parent.TokenType)
case JsonToken.StartObject:
writer.WriteStartObject();
case JsonToken.StartArray:
writer.WriteStartArray();
throw new JsonException();
for (int i = 0; i < parent.PrefixTokens.Count; i++)
if (i == parent.PrefixTokens.Count - 1 && parent.PrefixTokens[i] is JProperty property && property.Value.Type == JTokenType.Undefined)
writer.WritePropertyName(property.Name);
parent.PrefixTokens[i].WriteTo(writer);
static (JsonWriter, int) GetCurrentWriter(List<JsonWriter> writers, List<ParentToken> parentTokens, Func<int, TextWriter> createStream, Formatting formatting)
=> writers.Count == 0 ? (AddWriter(writers, parentTokens, createStream, formatting), 0) : (writers[writers.Count-1], writers.Count-1);
static void SplitJson(JsonTextReader reader, IList<string> splitPath, int index, long maxItems, Func<int, TextWriter> createStream, Formatting formatting, List<ParentToken> parentTokens , List<JsonWriter> writers)
var startTokenType = reader.MoveToContentAndAssert().TokenType;
var startReaderDepth = reader.Depth;
var bottom = index >= splitPath.Count;
case JsonToken.StartObject:
(var firstWriter, var firstWriterIndex) = GetCurrentWriter(writers, parentTokens, createStream, formatting);
firstWriter.WriteStartObject();
var parentToken = new ParentToken(JsonToken.StartObject, new List<JToken>());
while ((doRead ? reader.ReadToContentAndAssert() : reader.MoveToContentAndAssert()).TokenType != JsonToken.EndObject)
var propertyName = (string)reader.AssertTokenType(JsonToken.PropertyName).Value;
if (propertyName == splitPath[index])
throw new JsonException(string.Format("Duplicated property name {0}", propertyName));
reader.ReadToContentAndAssert();
firstWriter.WritePropertyName(propertyName);
parentToken.PrefixTokens.Add(new JProperty(propertyName, JValue.CreateUndefined()));
parentTokens.Add(parentToken);
SplitJson(reader, splitPath, index + 1, maxItems, createStream, formatting, parentTokens, writers);
parentTokens.RemoveAt(parentTokens.Count-1);
var property = JProperty.Load(reader);
property.WriteTo(firstWriter);
parentToken.PrefixTokens.Add(property);
var property = JProperty.Load(reader);
for (int i = firstWriterIndex; i < writers.Count; i++)
property.WriteTo(writers[i]);
for (int i = firstWriterIndex; i < writers.Count; i++)
foreach (var property in parentToken.PrefixTokens)
property.WriteTo(writers[i]);
writers[i].WriteEndObject();
case JsonToken.StartArray:
var maxItemsAtDepth = bottom ? maxItems : 1L;
(var writer, var firstWriterIndex) = GetCurrentWriter(writers, parentTokens, createStream, formatting);
writer.WriteStartArray();
while (reader.ReadToContentAndAssert().TokenType != JsonToken.EndArray)
if (reader.TokenType == JsonToken.Comment || reader.TokenType == JsonToken.None)
if (count >= maxItemsAtDepth)
writer = AddWriter(writers, parentTokens, createStream, formatting);
writer.WriteStartArray();
writer.WriteToken(reader);
parentTokens.Add(new ParentToken(JsonToken.StartArray));
SplitJson(reader, splitPath, index, maxItems, createStream, formatting, parentTokens, writers);
parentTokens.RemoveAt(parentTokens.Count-1);
for (int i = firstWriterIndex; i < writers.Count; i++)
writers[i].WriteEndArray();
(var writer, var _) = GetCurrentWriter(writers, parentTokens, createStream, formatting);
writer.WriteToken(reader);
public static partial class JsonExtensions
public static JsonReader AssertTokenType(this JsonReader reader, JsonToken tokenType) =>
reader.TokenType == tokenType ? reader : throw new JsonSerializationException(string.Format("Unexpected token {0}, expected {1}", reader.TokenType, tokenType));
public static JsonReader ReadToContentAndAssert(this JsonReader reader) =>
reader.ReadAndAssert().MoveToContentAndAssert();
public static JsonReader MoveToContentAndAssert(this JsonReader reader)
throw new ArgumentNullException();
if (reader.TokenType == JsonToken.None)
while (reader.TokenType == JsonToken.Comment)
public static JsonReader ReadAndAssert(this JsonReader reader)
throw new ArgumentNullException();
throw new JsonReaderException("Unexpected end of JSON stream.");
public static void Test()
""FileType"": ""Measurements"",
""InstrumentDescriptions"": [
""InstrumentID"": ""1723007"",
""InstrumentType"": ""Actual1"",
""DataType"": ""Double"",
""InstrumentID"": ""2424009"",
""InstrumentType"": ""Actual2"",
""DataType"": ""Double"",
""StepResult"": ""NormalEnd""
""InstrumentID"": ""1723007""
""DateTime"": ""2021-11-16 21:18:37.000"",
""DateTime"": ""2021-11-16 21:18:37.100"",
""DateTime"": ""2021-11-16 21:18:37.200"",
""DateTime"": ""2021-11-16 21:18:37.300"",
""DateTime"": ""2021-11-16 21:18:37.400"",
""DateTime"": ""2021-11-16 21:18:37.500"",
""DateTime"": ""2021-11-16 21:18:37.600"",
""DateTime"": ""2021-11-16 21:18:37.700"",
""DateTime"": ""2021-11-16 21:18:37.800"",
""InstrumentID"": ""2424009""
""DateTime"": ""2021-11-16 21:18:37.000"",
""DateTime"": ""2021-11-16 21:18:37.100"",
""DateTime"": ""2021-11-16 21:18:37.200"",
""DateTime"": ""2021-11-16 21:18:37.300"",
""DateTime"": ""2021-11-16 21:18:37.400"",
""DateTime"": ""2021-11-16 21:18:37.500"",
""DateTime"": ""2021-11-16 21:18:37.600"",
""DateTime"": ""2021-11-16 21:18:37.700"",
""DateTime"": ""2021-11-16 21:18:37.800"",
TestSplitJson(json, new [] { "Job", "Steps", "InstrumentData" });
TestSplitJson(json, new [] { "Job", "Steps" });
TestSplitJson(json, new [] { "Job", "Steps", "InstrumentData", "Measurements" });
private static void TestSplitJson(string json, string [] splitPath)
var filename = "Question70584260.json";
File.WriteAllText(filename, json);
Console.WriteLine("Splitting on {0}:", string.Join(",", splitPath));
var fileNames = JsonExtensions.SplitJsonFile(filename, splitPath,
(directory, name, i, ext) => Path.Combine(directory, Path.ChangeExtension(name + $"_fragment_{i}", ext)));
Console.WriteLine("Created {0} fragments:", fileNames.Length);
foreach (var name in fileNames)
Console.WriteLine("Contents of {0}:", name);
Console.WriteLine(File.ReadAllText(name));
foreach (var name in fileNames)
private static void AssertCorrect(string json, string [] splitPath, List<StringBuilder> builders)
var mainToken = JObject.Parse(json);
foreach (var s in builders.Select(b => b.ToString()))
var token = JObject.Parse(s);
if (!(token.Properties().Select(p => p.Name).SequenceEqual(mainToken.Properties().Select(p => p.Name))))
throw new InvalidOperationException();
public static void Main()
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("{0} version: {1}", typeof(JsonSerializer).Assembly.GetName().Name, typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine("Failed with unhandled exception: ");
public static string GetNetCoreVersion()
var assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
var assemblyPath = assembly.Location.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];