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 int[] Faces { get; set; }
public Vec3[] Vertices { get; set; }
public double X { get; set; }
public double Y { get; set; }
public double Z { get; set; }
public static partial class MeshExtensions
const string Vertices = "Vertices";
const string Faces = "Faces";
public static Mesh FromJson(JsonTextReader reader)
var nameTable = new DefaultJsonNameTable();
reader.PropertyNameTable = nameTable;
reader.DateParseHandling = DateParseHandling.None;
bool verticesFound = false;
List<Vec3> vertices = null;
while (reader.ReadToContent())
if (reader.TokenType == JsonToken.PropertyName && reader.Value == (object)Vertices)
throw new JsonSerializationException("Multiple vertices");
reader.ReadToContentAndAssert();
vertices = ReadVertices(reader);
else if (reader.TokenType == JsonToken.PropertyName && reader.Value == (object)Faces)
throw new JsonSerializationException("Multiple faces");
reader.ReadToContentAndAssert();
faces = reader.ReadIntArray();
Vertices = vertices == null ? null : vertices.ToArray(),
Faces = faces == null ? null : faces.ToArray(),
static List<Vec3> ReadVertices(JsonTextReader reader)
if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
else if (reader.TokenType != JsonToken.StartArray)
throw new JsonSerializationException(string.Format("Unexpected token type {0}", reader.TokenType));
var vertices = new List<Vec3>();
while (reader.ReadToContent())
switch (reader.TokenType)
case JsonToken.StartObject:
var vertex = ReadVertex(reader);
throw new JsonSerializationException(string.Format("Unexpected token type {0}", reader.TokenType));
throw new JsonReaderException();
static Vec3 ReadVertex(JsonTextReader reader)
if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
else if (reader.TokenType != JsonToken.StartObject)
throw new JsonException();
while (reader.ReadToContent())
switch (reader.TokenType)
case JsonToken.EndObject:
case JsonToken.PropertyName:
if (reader.Value == (object)X)
vec.X = reader.ReadAsDouble().Value;
else if (reader.Value == (object)Y)
vec.Y = reader.ReadAsDouble().Value;
else if (reader.Value == (object)Z)
vec.Z = reader.ReadAsDouble().Value;
reader.ReadToContentAndAssert().Skip();
throw new JsonSerializationException(string.Format("Unexpected token type {0}", reader.TokenType));
throw new JsonReaderException();
public static class JsonExtensions
public static List<int> ReadIntArray(this JsonReader reader)
if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
else if (reader.TokenType != JsonToken.StartArray)
throw new JsonReaderException(string.Format("Unexpected token type {0}", reader.TokenType));
var list = new List<int>();
for (var value = reader.ReadAsInt32(); true; value = reader.ReadAsInt32())
switch (reader.TokenType)
throw new JsonReaderException(string.Format("Unexpected token type {0}", reader.TokenType));
public static bool ReadToContent(this JsonReader reader)
throw new ArgumentNullException();
while (reader.TokenType == JsonToken.Comment)
public static JsonReader ReadToContentAndAssert(this JsonReader reader)
return 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()
Faces = new[] { 0, 1, 2, 3 },
Vertices = new[] { new Vec3 { X = 1, Y = 2, Z = 3 }, new Vec3 { X = double.PositiveInfinity, Y = double.MaxValue, Z = double.Epsilon }, new Vec3 { X = 1.0/3.0, Y = 2.0/3.0, Z = Math.PI } },
var json = JsonConvert.SerializeObject(inputMesh, Formatting.Indented);
using (var textReader = new StringReader(json))
using (var jsonReader = new JsonTextReader(textReader))
var newMesh = MeshExtensions.FromJson(jsonReader);
var json2 = JsonConvert.SerializeObject(newMesh, Formatting.Indented);
Console.WriteLine(json2);
Assert.IsTrue(JToken.DeepEquals(JToken.Parse(json), JToken.Parse(json2)));
using (var textReader = new StringReader(GetJsonWithCommentsAndAddedProperties()))
using (var jsonReader = new JsonTextReader(textReader))
var newMesh = MeshExtensions.FromJson(jsonReader);
var deserializedMesh = JsonConvert.DeserializeObject<Mesh>(GetJsonWithCommentsAndAddedProperties());
var json2 = JsonConvert.SerializeObject(newMesh, Formatting.Indented);
Console.WriteLine(json2);
JToken.DeepEquals(JToken.Parse(json2), JToken.Parse(JsonConvert.SerializeObject(deserializedMesh)));
static void SimpleReaderTests()
var json = @"{""value"" : /* comment */ 2 }";
var value1 = JsonConvert.DeserializeAnonymousType(json, new { value = default(double) });
using (var reader = new JsonTextReader(new StringReader(json)))
if (reader.TokenType == JsonToken.PropertyName)
var value2 = reader.ReadAsDouble();
Assert.IsTrue(value2 == value1.value);
using (var reader = new JsonTextReader(new StringReader("[1, 2")))
Assert.Throws(typeof(JsonReaderException), () => reader.ReadIntArray());
using (var reader = new JsonTextReader(new StringReader("/**/ [/**/ 1/**/, /**/2 /**/ ]")))
var list = reader.ReadIntArray();
Assert.IsTrue(new[] { 1, 2 }.SequenceEqual(list));
using (var reader = new JsonTextReader(new StringReader("[1, {}]")))
Assert.Throws(typeof(JsonReaderException), () => reader.ReadIntArray());
using (var reader = new JsonTextReader(new StringReader("[1}")))
Assert.Throws(typeof(JsonReaderException), () => reader.ReadIntArray());
using (var reader = new JsonTextReader(new StringReader("[1, null]")))
Assert.Throws(typeof(JsonReaderException), () => reader.ReadIntArray());
using (var reader = new JsonTextReader(new StringReader("[1, 2.2]")))
Assert.Throws(typeof(JsonReaderException), () => reader.ReadIntArray());
static string GetJsonWithCommentsAndAddedProperties()
return @"{ ""SomeOtherArray"":[[[{},{""X"":""XXX""}]]],
/* COmment*/ ""Faces"": [ // COmment
""Vertices"": [ /* COmment*/
""SomeOtherProperty"": {/**/ ""X"" :/**/""XXX""/**/}/**/,
/* COmment*/ ""Y"": /* COmment*/ 2.0,
/* COmment*/ ""Z"": /* COmment*/3.0
/* COmment*/ ""X"": ""Infinity"",
""Y"": 1.7976931348623157E+308,
""Z"": 4.94065645841247E-324,
""SomeOtherProperty"":{""X"":""XXX""}
/* COmment*/ ""X"": 0.33333333333333331,
/* COmment*/ ""Y"": 0.66666666666666663,
""Z"": 3.1415926535897931 /* COmment*/
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];