using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Collections.ObjectModel;
using System.Runtime.Serialization.Json;
public static class XmlWriterExtensions
public static void WriteTransformedNode(this XmlWriter writer, XmlReader reader, Predicate<XmlReader> shouldTransform, Action<XmlReader, XmlWriter> transform)
if (reader == null || writer == null || shouldTransform == null || transform == null)
throw new ArgumentNullException();
int d = reader.NodeType == XmlNodeType.None ? -1 : reader.Depth;
if (reader.NodeType == XmlNodeType.Element && shouldTransform(reader))
using (var subReader = reader.ReadSubtree())
transform(subReader, writer);
writer.WriteShallowNode(reader);
while (!reader.EOF && (d < reader.Depth || (d == reader.Depth && reader.NodeType == XmlNodeType.EndElement)));
public static void WriteShallowNode(this XmlWriter writer, XmlReader reader)
throw new ArgumentNullException("reader");
throw new ArgumentNullException("writer");
case XmlNodeType.Element:
writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI);
writer.WriteAttributes(reader, true);
if (reader.IsEmptyElement)
writer.WriteEndElement();
case XmlNodeType.Whitespace:
case XmlNodeType.SignificantWhitespace:
case XmlNodeType.XmlDeclaration:
case XmlNodeType.ProcessingInstruction:
case XmlNodeType.EntityReference:
case XmlNodeType.DocumentType:
case XmlNodeType.Comment:
writer.WriteNode(reader, true);
case XmlNodeType.EndElement:
writer.WriteFullEndElement();
throw new XmlException(string.Format("Unknown NodeType {0}", reader.NodeType));
public static partial class XmlReaderExtensions
public static bool CopyBase64ElementContentsToFile(this XmlReader reader, string path)
using (var stream = File.Create(path))
byte[] buffer = new byte[8192];
while ((readBytes = reader.ReadElementContentAsBase64(buffer, 0, buffer.Length)) > 0)
stream.Write(buffer, 0, readBytes);
public static class JsonPatchExtensions
public static string[] PatchFileContentToFileRoute(string oldJsonFileName, string newJsonFileName, FilenameGenerator generator)
var newNames = new List<string>();
using (var inStream = File.OpenRead(oldJsonFileName))
using (var outStream = File.Open(newJsonFileName, FileMode.Create))
using (var xmlReader = JsonReaderWriterFactory.CreateJsonReader(inStream, XmlDictionaryReaderQuotas.Max))
using (var xmlWriter = JsonReaderWriterFactory.CreateJsonWriter(outStream))
xmlWriter.WriteTransformedNode(xmlReader,
r => r.LocalName == "fileContent" && r.NamespaceURI == "",
var name = generator.GenerateNewName();
r.CopyBase64ElementContentsToFile(name);
w.WriteStartElement("fileRoute", "");
w.WriteAttributeString("type", "string");
return newNames.ToArray();
public abstract class FilenameGenerator
public abstract string GenerateNewName();
public class IncrementalFilenameGenerator : FilenameGenerator
readonly string extension;
public IncrementalFilenameGenerator(string prefix, string extension)
this.extension = extension;
public override string GenerateNewName()
var newName = Path.ChangeExtension(prefix + (++count).ToString(), extension);
public class FileNameAndContent
public string name { get; set; }
public byte [] fileContent { get; set; }
public RootObject() { this.files = new List<FileNameAndContent>(); }
public List<FileNameAndContent> files { get; set; }
public class OutFileNameAndContent
public string name { get; set; }
public string fileRoute { get; set; }
public class OutRootObject
public OutRootObject() { this.files = new List<OutFileNameAndContent>(); }
public List<OutFileNameAndContent> files { get; set; }
static string GetOldJsonFileName(int n) { return string.Format("{0}_{1}.json", "Question59839437", n); }
static string GetNewJsonFileName(int n) { return string.Format("{0}_{1}_Patched.json", "Question59839437", n); }
public static void Test()
Console.WriteLine("All tests passed.");
public static void Test(int numberOfBytes, bool printFileContents)
var oldFile = GetOldJsonFileName(numberOfBytes);
var newFile = GetNewJsonFileName(numberOfBytes);
CreateJson(oldFile, numberOfBytes);
using (var inStream = File.OpenRead(oldFile))
using (var xmlReader = JsonReaderWriterFactory.CreateJsonReader(inStream, XmlDictionaryReaderQuotas.Max))
Assert.IsTrue(xmlReader.CanReadValueChunk, "xmlReader.CanReadValueChunk");
TestNullPatch(oldFile, newFile);
var binaryFiles = TestPatch(oldFile, newFile, printFileContents);
foreach (var f in binaryFiles.Concat(new [] { oldFile, newFile }))
static string[] TestPatch(string oldJsonFileName, string newJsonFileName, bool printFileContents)
var binaryFileNames = JsonPatchExtensions.PatchFileContentToFileRoute(
new IncrementalFilenameGenerator("Question59839437_fileContent_", ".bin"));
using (var textReader = new StreamReader(oldJsonFileName))
using (var reader = new Newtonsoft.Json.JsonTextReader(textReader))
oldRoot = Newtonsoft.Json.JsonSerializer.CreateDefault().Deserialize<RootObject>(reader);
Assert.IsTrue(oldRoot.files.Count == binaryFileNames.Length);
for (int i = 0; i < oldRoot.files.Count; i++)
var bytes = File.ReadAllBytes(binaryFileNames[i]);
Assert.IsTrue(bytes.SequenceEqual(oldRoot.files[0].fileContent));
Console.WriteLine(string.Format("Patching of fileContent in {0} to fileRoute in {1} with binary files [{2}] resulted in identical binary data.", oldJsonFileName, newJsonFileName, string.Join(",", binaryFileNames)));
Console.WriteLine("Input file {0}:", oldJsonFileName);
Console.WriteLine(File.ReadAllText(oldJsonFileName));
Console.WriteLine("Patched file {0}:", newJsonFileName);
Console.WriteLine(File.ReadAllText(newJsonFileName));
static void TestNullPatch(string oldJsonFileName, string newJsonFileName)
NullPatch(oldJsonFileName, newJsonFileName);
Assert.IsTrue(EqualJsonFiles(oldJsonFileName, newJsonFileName));
Console.WriteLine(string.Format("Null patch of {0} to {1} produced equivalent JSON files.", oldJsonFileName, newJsonFileName));
private static void NullPatch(string oldJsonFileName, string newJsonFileName)
using (var inStream = File.OpenRead(oldJsonFileName))
using (var outStream = File.Open(newJsonFileName, FileMode.Create))
using (var xmlReader = JsonReaderWriterFactory.CreateJsonReader(inStream, XmlDictionaryReaderQuotas.Max))
using (var xmlWriter = JsonReaderWriterFactory.CreateJsonWriter(outStream))
xmlWriter.WriteTransformedNode(xmlReader, r => false, (r, w) => { });
static void CreateJson(string fileName, int numberOfBytes)
var root = new RootObject
new FileNameAndContent { name = "One Name", fileContent = Enumerable.Range(0, numberOfBytes).Select(i => unchecked((byte)i)).ToArray() },
using (var textWriter = new StreamWriter(fileName, false))
using (var jsonWriter = new Newtonsoft.Json.JsonTextWriter(textWriter))
Newtonsoft.Json.JsonSerializer.CreateDefault().Serialize(jsonWriter, root);
static bool EqualJsonFiles(string inFile, string outFile)
Newtonsoft.Json.Linq.JToken inToken, outToken;
using (var textReader = new StreamReader(inFile))
using (var reader = new Newtonsoft.Json.JsonTextReader(textReader))
inToken = Newtonsoft.Json.Linq.JToken.Load(reader);
using (var textReader = new StreamReader(inFile))
using (var reader = new Newtonsoft.Json.JsonTextReader(textReader))
outToken = Newtonsoft.Json.Linq.JToken.Load(reader);
return Newtonsoft.Json.Linq.JToken.DeepEquals(inToken, outToken);
static XElement CreateOutputSample()
var outRoot = new OutRootObject
new OutFileNameAndContent { name = "One Name", fileRoute = "fooName1.json" }
var json = Newtonsoft.Json.JsonConvert.SerializeObject(outRoot);
using (var reader = JsonReaderWriterFactory.CreateJsonReader(new UTF8Encoding(false).GetBytes(json), XmlDictionaryReaderQuotas.Max))
return XElement.Load(reader);
public static void Main()
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("Failed with unhandled exception: ");
public static string GetNetCoreVersion()
var assembly = typeof(System.Runtime.GCSettings).GetTypeInfo().Assembly;
var assemblyPath = assembly.CodeBase.Split(new[] { '/', '\\' }, StringSplitOptions.RemoveEmptyEntries);
int netCoreAppIndex = Array.IndexOf(assemblyPath, "Microsoft.NETCore.App");
if (netCoreAppIndex > 0 && netCoreAppIndex < assemblyPath.Length - 2)
return assemblyPath[netCoreAppIndex + 1];