https://stackoverflow.com/questions/53278497/create-invalid-json-with-newtonsoft-allow-invalid-objects

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 Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Serialization;	

using NUnit.Framework; // Throws NUnit.Framework.AssertionException

    //https://stackoverflow.com/questions/53278497/create-invalid-json-with-newtonsoft-allow-invalid-objects
    public class EsiObjConverter<T> : JsonConverter
    {
        const string EsiObjName = "EsiObj";

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var contract = serializer.ContractResolver.ResolveContract(value.GetType()) as JsonObjectContract;
            if (contract == null)
                throw new JsonSerializationException(string.Format("Non-object type {0}", value));
            writer.WriteStartObject();
            int propertyCount = 0;
            bool lastWasEsiProperty = false;
            foreach (var property in contract.Properties.Where(p => p.Readable && !p.Ignored))
            {
                if (property.UnderlyingName == EsiObjName && property.PropertyType == typeof(string))
                {
                    var esiValue = (string)property.ValueProvider.GetValue(value);
                    if (!string.IsNullOrEmpty(esiValue))
                    {
                        if (propertyCount > 0)
                        {
                            WriteValueDelimiter(writer);
                        }
                        writer.WriteWhitespace("\n");
                        writer.WriteRaw(esiValue);
                        // If it makes replacement easier, you could force the ESI string to be on its own line by calling
                        // writer.WriteWhitespace("\n");

                        propertyCount++;
                        lastWasEsiProperty = true;
                    }
                }
                else
                {
                    var propertyValue = property.ValueProvider.GetValue(value);

                    // Here you might check NullValueHandling, ShouldSerialize(), ...

                    if (propertyCount == 1 && lastWasEsiProperty)
                    {
                        WriteValueDelimiter(writer);
                    }
                    writer.WritePropertyName(property.PropertyName);
                    serializer.Serialize(writer, propertyValue);

                    propertyCount++;
                    lastWasEsiProperty = false;
                }
            }
            writer.WriteEndObject();
        }

        static void WriteValueDelimiter(JsonWriter writer)
        {
            var args = new object[0];
            // protected virtual void WriteValueDelimiter() 
            // https://www.newtonsoft.com/json/help/html/M_Newtonsoft_Json_JsonWriter_WriteValueDelimiter.htm
            // Since this is overridable by client code it is unlikely to be removed.
            writer.GetType().GetMethod("WriteValueDelimiter", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Invoke(writer, args);
        }

        public override bool CanConvert(Type objectType)
        {
            return typeof(T).IsAssignableFrom(objectType);
        }

        public override bool CanRead { get { return false; } }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }

    public class mApiResponseClass
    {
        public int value { get; set; }
        public string string1 { get; set; }
        public string EsiObj { get; set; }
        public List<string> Song { get; set; }
    }

    public class mApiResponseClassEsiFirst
    {
        public string EsiObj { get; set; }
        public int value { get; set; }
        public string string1 { get; set; }
        public List<string> Song { get; set; }
    }

    public class mApiResponseClassEsiLast
    {
        public int value { get; set; }
        public string string1 { get; set; }
        public List<string> Song { get; set; }
        public string EsiObj { get; set; }
    }

    public class mApiResponseClassEsiOnly
    {
        public string EsiObj { get; set; }
    }

    class TestClass
    {
        public static void Test()
        {
            var esiObj = new mApiResponseClass
            {
                value = 7,
                string1 = "woohoo",
                EsiObj = @"<esi:include src=""/something"" />",
                Song = new List<string> { "I am a small API","all i do is run","but from who?","nobody knows" },
            };
            var esiFirst = new mApiResponseClassEsiFirst
            {
                value = 7,
                string1 = "woohoo",
                EsiObj = @"<esi:include src=""/something"" />",
                Song = new List<string> { "I am a small API", "all i do is run", "but from who?", "nobody knows" },
            };
            var esiOnly = new mApiResponseClassEsiOnly
            {
                EsiObj = @"<esi:include src=""/something"" />",
            };
            var esiLast = new mApiResponseClassEsiLast
            {
                value = 7,
                string1 = "woohoo",
                EsiObj = @"<esi:include src=""/something"" />",
                Song = new List<string> { "I am a small API", "all i do is run", "but from who?", "nobody knows" },
            };

            TestSimpleAndUnquoted(esiObj);

            TestSimpleAndUnquoted(esiFirst);

            TestSimpleAndUnquoted(esiLast);

            TestSimpleAndUnquoted(esiOnly);

            Console.WriteLine();

            TestNested(esiObj);

            Console.WriteLine("Done tests");
        }

        private static void TestSimpleAndUnquoted<mApiResponseClass>(mApiResponseClass esiObj)
        {
            TestSimple(esiObj);

            TestWithoutNameQuoting(esiObj);
        }

        private static void TestSimple<mApiResponseClass>(mApiResponseClass obj)
        {
            Console.WriteLine("Serializing an instance of {0}:", obj);

            var settings = new JsonSerializerSettings
            {
                Converters = { new EsiObjConverter<mApiResponseClass>() },
            };

            var json = JsonConvert.SerializeObject(obj, Formatting.Indented, settings);

            Console.WriteLine(json);
        }

        private static void TestWithoutNameQuoting<mApiResponseClass>(mApiResponseClass obj)
        {
            Console.WriteLine("Serializing an instance of {0} without name quoting:", obj);

            var settings = new JsonSerializerSettings
            {
                Converters = { new EsiObjConverter<mApiResponseClass>() },
            };

            // Adapted from this answer https://stackoverflow.com/a/7555096
			// to https://stackoverflow.com/questions/7553516/json-net-serialize-property-name-without-quotes
            // by https://stackoverflow.com/users/893099/christophe-geers

            var stringWriter = new StringWriter();
            using (var writer = new JsonTextWriter(stringWriter))
            {
                writer.QuoteName = false;
                writer.Formatting = Formatting.Indented;
                writer.Indentation = 0;
                JsonSerializer.CreateDefault(settings).Serialize(writer, obj);
            }

            var json = stringWriter.ToString();
            Console.WriteLine(json);
        }

        private static void TestNested<mApiResponseClass>(mApiResponseClass obj)
        {
            Console.WriteLine("Testing nested serialization of {0}:", obj);

            var settings = new JsonSerializerSettings
            {
                Converters = { new EsiObjConverter<mApiResponseClass>() },
            };

            var json = JsonConvert.SerializeObject(new { data = new [] { obj  }}, Formatting.Indented, settings);

            Console.WriteLine(json);
        }
    }

	public class Program
	{
		public static void Main()
		{
			Console.WriteLine("Environment version: " + Environment.Version);
			Console.WriteLine("Json.NET version: " + typeof(JsonSerializer).Assembly.FullName);
			Console.WriteLine();

			try
			{
				TestClass.Test();
			}
			catch (Exception ex)
			{
				Console.WriteLine("Failed with unhandled exception: ");
				Console.WriteLine(ex);
				throw;
			}
		}
	}
;