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 System.Reflection;
using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;
using NUnit.Framework; // Throws NUnit.Framework.AssertionException
using Debug = System.Console;
//https://stackoverflow.com/questions/63376873/in-system-text-json-is-it-possible-to-specify-custom-indentation-rules
public partial class Root
{
[JsonPropertyName("TILESET")]
public string Tileset { get; set; }
[JsonPropertyName("TILES")]
public Tiles Tiles { get; set; }
}
public partial class Tiles
{
[JsonPropertyName("TILE_1")]
public Tile1 Tile1 { get; set; }
}
public partial class Tile1
{
[JsonPropertyName("NAME")]
public string Name { get; set; }
[JsonPropertyName("TEXTURE_BOUNDS")]
[JsonConverter(typeof(CollectionFormattingConverter<List<long>, long>))]
public List<long> TextureBounds { get; set; }
[JsonPropertyName("SCREEN_BOUNDS")]
[JsonConverter(typeof(CollectionFormattingConverter<List<long>, long>))]
public List<long> ScreenBounds { get; set; }
}
class CollectionFormattingConverter<TCollection, TItem> : JsonConverter<TCollection> where TCollection : class, ICollection<TItem>, new()
{
public override TCollection Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
=> JsonSerializer.Deserialize<CollectionSurrogate<TCollection, TItem>>(ref reader, options)?.BaseCollection;
public override void Write(Utf8JsonWriter writer, TCollection value, JsonSerializerOptions options)
{
var copy = options.Clone();
copy.WriteIndented = false;
using var doc = JsonExtensions.JsonDocumentFromObject(new CollectionSurrogate<TCollection, TItem>(value), copy);
Debug.WriteLine("Preformatted JsonDocument: {0}", doc.RootElement);
doc.WriteTo(writer);
}
}
public static partial class JsonExtensions
{
public static JsonSerializerOptions Clone(this JsonSerializerOptions options)
{
if (options == null)
return new JsonSerializerOptions();
//In .Net 5 a copy constructor will be introduced for JsonSerializerOptions. Use the following in that version.
//return new JsonSerializerOptions(options);
//In the meantime copy manually.
var clone = new JsonSerializerOptions
{
AllowTrailingCommas = options.AllowTrailingCommas,
DefaultBufferSize = options.DefaultBufferSize,
DictionaryKeyPolicy = options.DictionaryKeyPolicy,
Encoder = options.Encoder,
IgnoreNullValues = options.IgnoreNullValues,
IgnoreReadOnlyProperties = options.IgnoreReadOnlyProperties,
MaxDepth = options.MaxDepth,
PropertyNameCaseInsensitive = options.PropertyNameCaseInsensitive,
PropertyNamingPolicy = options.PropertyNamingPolicy,
ReadCommentHandling= options.ReadCommentHandling,
WriteIndented = options.WriteIndented,
};
foreach (var converter in options.Converters)
clone.Converters.Add(converter);
return clone;
}
// Copied from this answer https://stackoverflow.com/a/62998253/3744182
// To https://stackoverflow.com/questions/62996999/convert-object-to-system-text-json-jsonelement
// By https://stackoverflow.com/users/3744182/dbc
public static JsonDocument JsonDocumentFromObject<TValue>(TValue value, JsonSerializerOptions options = default)
=> JsonDocumentFromObject(value, typeof(TValue), options);
public static JsonDocument JsonDocumentFromObject(object value, Type type, JsonSerializerOptions options = default)
{
var bytes = JsonSerializer.SerializeToUtf8Bytes(value, options);
return JsonDocument.Parse(bytes);
}
}
public class CollectionSurrogate<TCollection, TItem> : ICollection<TItem> where TCollection : ICollection<TItem>, new()
{
public TCollection BaseCollection { get; }
public CollectionSurrogate() { this.BaseCollection = new TCollection(); }
public CollectionSurrogate(TCollection baseCollection) { this.BaseCollection = baseCollection ?? throw new ArgumentNullException(); }
public void Add(TItem item) => BaseCollection.Add(item);
public void Clear() => BaseCollection.Clear();
public bool Contains(TItem item) => BaseCollection.Contains(item);
public void CopyTo(TItem[] array, int arrayIndex) => BaseCollection.CopyTo(array, arrayIndex);
public int Count => BaseCollection.Count;
public bool IsReadOnly => BaseCollection.IsReadOnly;
public bool Remove(TItem item) => BaseCollection.Remove(item);
public IEnumerator<TItem> GetEnumerator() => BaseCollection.GetEnumerator();
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => ((IEnumerable)BaseCollection).GetEnumerator();
}
class TestClass
{
public static void Test()
{
var originalJson = GetJson();
var options = new JsonSerializerOptions
{
WriteIndented = true,
};
var root = JsonSerializer.Deserialize<Root>(originalJson, options);
var json2 = JsonSerializer.Serialize(root, options);
Console.WriteLine("Serialized {0}: ", root);
Console.WriteLine(json2);
}
static string GetJson()
{
return @"{
""TILESET"": ""tilesets/HOW_TO_GET_TILESET_NAME_?"",
""TILES"": {
""TILE_1"": {
""NAME"": ""auto_tile_18"",
""TEXTURE_BOUNDS"": [
304,
16,
16,
16
],
""SCREEN_BOUNDS"": [
485,
159,
64,
64
]
}
}
}";
}
}
public class Program
{
public static void Main()
{
Console.WriteLine("Environment version: {0} ({1})", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , GetNetCoreVersion());
Console.WriteLine("System.Text.Json version: " + typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine();
try
{
TestClass.Test();
}
catch (Exception ex)
{
Console.WriteLine("Failed with unhandled exception: ");
Console.WriteLine(ex);
throw;
}
}
public static string GetNetCoreVersion()
{
//https://techblog.dorogin.com/how-to-detect-net-core-version-in-runtime-ecd65ad695be
//
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];
return null;
}
}