I only want the first depth level of an object (I do not want any children). I am willing to use any library available. Most libraries will merely throw an exception when
If you want to use this in a ASP.NET Core project, maybe you cant implement your own JsonTextWriter. But you can custom the DefaultContractResolver and IValueProvider
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
namespace customserialization
{
///
/// IValueProvider personalizado para manejar max depth level
///
public class CustomDynamicValueProvider : DynamicValueProvider, IValueProvider
{
MemberInfo _memberInfo;
MaxDepthHandler _levelHandler;
public CustomDynamicValueProvider(MemberInfo memberInfo, MaxDepthHandler levelHandler) : base(memberInfo)
{
_memberInfo = memberInfo;
_levelHandler = levelHandler;
}
public new object GetValue(object target)
{
//Si el valor a serializar es un objeto se incrementa el nivel de profundidad. En el caso de las listas el nivel se incrementa en el evento OnSerializing
if (((PropertyInfo)_memberInfo).PropertyType.IsClass) this._levelHandler.IncrementLevel();
var rv = base.GetValue(target);
//Al finalizar la obtención del valor se decrementa. En el caso de las listas el nivel se decrementa en el evento OnSerialized
if (((PropertyInfo)_memberInfo).PropertyType.IsClass) this._levelHandler.DecrementLevel();
return rv;
}
}
///
/// Maneja los niveles de serialización
///
public class MaxDepthHandler
{
int _maxDepth;
int _currentDepthLevel;
///
/// Nivel actual
///
public int CurrentDepthLevel { get { return _currentDepthLevel; } }
public MaxDepthHandler(int maxDepth)
{
this._currentDepthLevel = 1;
this._maxDepth = maxDepth;
}
///
/// Incrementa el nivel actual
///
public void IncrementLevel()
{
this._currentDepthLevel++;
}
///
/// Decrementa el nivel actual
///
public void DecrementLevel()
{
this._currentDepthLevel--;
}
///
/// Determina si se alcanzó el nivel actual
///
///
public bool IsMaxDepthLevel()
{
return !(this._currentDepthLevel < this._maxDepth);
}
}
public class ShouldSerializeContractResolver : DefaultContractResolver
{
MaxDepthHandler _levelHandler;
public ShouldSerializeContractResolver(int maxDepth)
{
this._levelHandler = new MaxDepthHandler(maxDepth);
}
void OnSerializing(object o, System.Runtime.Serialization.StreamingContext context)
{
//Antes de serializar una lista se incrementa el nivel. En el caso de los objetos el nivel se incrementa en el método GetValue del IValueProvider
if (o.GetType().IsGenericList())
_levelHandler.IncrementLevel();
}
void OnSerialized(object o, System.Runtime.Serialization.StreamingContext context)
{
//Despues de serializar una lista se decrementa el nivel. En el caso de los objetos el nivel se decrementa en el método GetValue del IValueProvider
if (o.GetType().IsGenericList())
_levelHandler.DecrementLevel();
}
protected override JsonContract CreateContract(Type objectType)
{
var contract = base.CreateContract(objectType);
contract.OnSerializingCallbacks.Add(new SerializationCallback(OnSerializing));
contract.OnSerializedCallbacks.Add(new SerializationCallback(OnSerialized));
return contract;
}
protected override IValueProvider CreateMemberValueProvider(MemberInfo member)
{
var rv = base.CreateMemberValueProvider(member);
if (rv is DynamicValueProvider) //DynamicValueProvider es el valueProvider usado en general
{
//Utilizo mi propio ValueProvider, que utilizar el levelHandler
rv = new CustomDynamicValueProvider(member, this._levelHandler);
}
return rv;
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
var isObjectOrList = ((PropertyInfo)member).PropertyType.IsGenericList() || ((PropertyInfo)member).PropertyType.IsClass;
property.ShouldSerialize =
instance =>
{
var shouldSerialize = true;
//Si se alcanzo el nivel maximo y la propiedad (member) actual a serializar es un objeto o lista no se serializa (shouldSerialize = false)
if (_levelHandler.IsMaxDepthLevel() && isObjectOrList)
shouldSerialize = false;
return shouldSerialize;
};
return property;
}
}
public static class Util
{
public static bool IsGenericList(this Type type)
{
foreach (Type @interface in type.GetInterfaces())
{
if (@interface.IsGenericType)
{
if (@interface.GetGenericTypeDefinition() == typeof(ICollection<>))
{
// if needed, you can also return the type used as generic argument
return true;
}
}
}
return false;
}
}
}
and use this in your controller
[HttpGet]
public IActionResult TestJSON()
{
var obj = new Thing
{
id = 1,
reference = new Thing
{
id = 2,
reference = new Thing
{
id = 3,
reference = new Thing
{
id = 4
}
}
}
};
var settings = new JsonSerializerSettings()
{
ContractResolver = new ShouldSerializeContractResolver(2),
};
return new JsonResult(obj, settings);
}