https://stackoverflow.com/questions/63376873/in-system-text-json-is-it-possible-to-specify-custom-indentation-rules

CSharp · Console

/Program.cs

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

//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")]
        public List<long> TextureBounds { get; set; }

        [JsonPropertyName("SCREEN_BOUNDS")]
        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 = new JsonSerializerOptions(options);  A copy constructor will be introduced for JsonSerializerOptions in .Net 5.  Use that there
			var copy = options.Clone();
			copy.WriteIndented = false;
			JsonSerializer.Serialize(writer, new CollectionSurrogate<TCollection, TItem>(value), copy);
		}
	}

	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;
		}
	}

	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,
				Converters = { new CollectionFormattingConverter<List<long>, long>() },
			};
			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;
		}
	}
;