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/74624508/custom-json-net-json-converter-with-nested-objects
public class WrapWithValueContractResolver : DefaultContractResolver
{
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (property.Converter == null && property.ItemConverter == null) // property.Converter check is required to avoid applying the converter to WrapWithValueConverter<TValue>.DTO.value
property.Converter = (JsonConverter)Activator.CreateInstance(typeof(WrapWithValueConverter<>).MakeGenericType(property.PropertyType));
return property;
}
}
public class WrapWithValueConverter<TValue> : JsonConverter
{
// Here we take advantage of the fact that a converter applied to a property has highest precedence to avoid an infinite recursion.
sealed class DTO { [JsonConverter(typeof(NoConverter)), JsonProperty(ReferenceLoopHandling = ReferenceLoopHandling.Serialize)] public TValue value { get; set; } public object GetValue() => value; }
public override bool CanConvert(Type objectType) => typeof(TValue).IsAssignableFrom(objectType);
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
=> serializer.Serialize(writer, new DTO { value = (TValue)value });
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
=> serializer.Deserialize<DTO>(reader)?.GetValue();
}
public class NoConverter : JsonConverter
{
// NoConverter taken from this answer https://stackoverflow.com/a/39739105/3744182
// By https://stackoverflow.com/users/3744182/dbc
// To https://stackoverflow.com/questions/39738714/selectively-use-default-json-converter
public override bool CanConvert(Type objectType) { throw new NotImplementedException(); /* This converter should only be applied via attributes */ }
public override bool CanRead => false;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => throw new NotImplementedException();
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
}
public class SalesOrder
{
[JsonProperty("project", NullValueHandling = NullValueHandling.Ignore)]
public int? Project { get; set; }
[JsonProperty("printDescriptionOnInvoice", NullValueHandling = NullValueHandling.Ignore)]
public bool PrintDescriptionOnInvoice { get; set; }
[JsonProperty("printNoteOnExternalDocuments", NullValueHandling = NullValueHandling.Ignore)]
public bool PrintNoteOnExternalDocuments { get; set; }
[JsonProperty("printNoteOnInternalDocuments", NullValueHandling = NullValueHandling.Ignore)]
public bool PrintNoteOnInternalDocuments { get; set; }
[JsonProperty("soBillingContact", NullValueHandling = NullValueHandling.Ignore)]
public Contact SoBillingContact { get; set; }
}
public class Contact
{
[JsonProperty("overrideContact", NullValueHandling = NullValueHandling.Ignore)]
public bool OverrideContact { get;set; }
[JsonProperty("attention", NullValueHandling = NullValueHandling.Ignore)]
public string Attention { get; set; }
[JsonProperty("email", NullValueHandling = NullValueHandling.Ignore)]
public string Email { get; set; }
[JsonProperty("fax", NullValueHandling = NullValueHandling.Ignore)]
public string Fax { get; set; }
[JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)]
public string Name { get; set; }
[JsonProperty("phone1", NullValueHandling = NullValueHandling.Ignore)]
public string Phone1 { get; set; }
[JsonProperty("phone2", NullValueHandling = NullValueHandling.Ignore)]
public string Phone2 { get; set; }
[JsonProperty("web", NullValueHandling = NullValueHandling.Ignore)]
public string Web { get; set; }
}
class TestClass
{
public static void Test()
{
DefaultContractResolver resolver = new WrapWithValueContractResolver // Cache statically and reuse for best performance
{
//NamingStrategy = new CamelCaseNamingStrategy(), // Uncomment if you need camel case
};
Test(resolver);
Test(null);
}
static void Test(IContractResolver resolver)
{
if (resolver == null)
Console.WriteLine("\nTesting with no resolver:");
else
Console.WriteLine("\nTesting with contract resolver {0}:", resolver);
var vSalesOrder = new SalesOrder()
{
Project = 1,
PrintDescriptionOnInvoice = true,
PrintNoteOnExternalDocuments = true,
SoBillingContact = new Contact
{
Attention = "attention",
Email = "@whatever.se",
Fax = "lolfax"
}
};
var settings = new JsonSerializerSettings
{
ContractResolver = resolver,
};
var json = JsonConvert.SerializeObject(vSalesOrder, Formatting.Indented, settings);
Console.WriteLine(json);
var order2 = JsonConvert.DeserializeObject<SalesOrder>(json, settings);
var json2 = JsonConvert.SerializeObject(order2, Formatting.Indented, settings);
Assert.AreEqual(json, json2);
Assert.AreEqual(vSalesOrder.Project, order2.Project);
Assert.AreEqual(vSalesOrder.PrintDescriptionOnInvoice, order2.PrintDescriptionOnInvoice);
Assert.AreEqual(vSalesOrder.PrintNoteOnExternalDocuments, order2.PrintNoteOnExternalDocuments);
Assert.AreEqual(vSalesOrder.SoBillingContact.Attention, order2.SoBillingContact.Attention);
Assert.AreEqual(vSalesOrder.SoBillingContact.Email, order2.SoBillingContact.Email);
Assert.AreEqual(vSalesOrder.SoBillingContact.Fax, order2.SoBillingContact.Fax);
}
}
public class Program
{
public static void Main()
{
Console.WriteLine("Environment version: {0} ({1}), {2}", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription , Environment.Version, Environment.OSVersion);
Console.WriteLine("{0} version: {1}", typeof(JsonSerializer).Namespace, typeof(JsonSerializer).Assembly.FullName);
Console.WriteLine();
try
{
TestClass.Test();
}
catch (Exception ex)
{
Console.WriteLine("Failed with unhandled exception: ");
Console.WriteLine(ex);
throw;
}
}
}