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