CSharp · Console
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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/73817932/newtonsoft-json-has-a-deserialization-bug-when-class-has-quick-access-property
public class Parent
{
public List<Child> Children { get; set; } = new List<Child>();
[GetOnlyJsonProperty]
public Child FirstChild => Children.First();
public bool ShouldSerializeFirstChild() => Children != null && Children.Count > 0;
}
public class Child
{
public List<GrandChild> Children { get; set; } = new List<GrandChild>();
}
public class GrandChild
{
public string Value { get; set; }
}
[System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple = false)]
public class GetOnlyJsonPropertyAttribute : Attribute
{
}
public class GetOnlyContractResolver : DefaultContractResolver
{
// From this answer https://stackoverflow.com/a/56885301/3744182
// By https://stackoverflow.com/users/7027460/pavlo-lissov
// To https://stackoverflow.com/questions/31731320/serialize-property-but-do-not-deserialize-property-in-json-net
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
if (property != null)
{
var attributes = property.AttributeProvider.GetAttributes(typeof(GetOnlyJsonPropertyAttribute), true);
if (attributes != null && attributes.Count > 0)
property.ShouldDeserialize = (a) => false;
}
return property;
}
}
class TestClass
{
public static void Test()
{
var parent = new Parent
{
Children = new List<Child>
{
new Child
{
Children = new List<GrandChild>
{
new GrandChild { Value = "I am a grand child" },
}
},
new Child(),
}
};
IContractResolver resolver = new GetOnlyContractResolver(); // In production code this should be cached statically to improve performance
var settings = new JsonSerializerSettings { ContractResolver = resolver };
var serializedParent = JsonConvert.SerializeObject(parent, settings);
var deserializedParent = JsonConvert.DeserializeObject<Parent>(serializedParent, settings);
var reserializedParent = JsonConvert.SerializeObject(deserializedParent, settings);
Console.WriteLine(serializedParent);
Console.WriteLine(reserializedParent);
// Expected: 1, Actual: 2
Assert.AreEqual(parent.Children[0].Children.Count, deserializedParent.Children[0].Children.Count);
Assert.AreEqual(serializedParent, reserializedParent);
Assert.DoesNotThrow(() => JsonConvert.SerializeObject(new Parent (), settings));
}
}
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;
}
}
}