Edit: 假设我有一个这样的对象:
class MyObject
{
String name = "Dan";
int age = 88;
List<Children> myChildren = ...(lots of children with lots of grandchildren);
}
我想删除任何孩子(甚至复杂类型)以返回这样的对象:
class MyObject
{
String name = "Dan";
int age = 88;
List<Children> myChildren = null;
}
和序列化器之间的一些协调。一个自定义
ContractResolver
当对象启动时会增加计数器,然后在结束时再次将其降低。
JsonWriter
a自定义public class CustomJsonTextWriter : JsonTextWriter
{
public CustomJsonTextWriter(TextWriter textWriter) : base(textWriter) {}
public int CurrentDepth { get; private set; }
public override void WriteStartObject()
{
CurrentDepth++;
base.WriteStartObject();
}
public override void WriteEndObject()
{
CurrentDepth--;
base.WriteEndObject();
}
}
对所有将用于验证当前深度的属性应用特殊的谓词。
ContractResolver
以下方法显示了这两个自定义类是如何一起工作的。
ShouldSerialize
以下测试代码分别将最大深度限制为1和2级别。
public class CustomContractResolver : DefaultContractResolver
{
private readonly Func<bool> _includeProperty;
public CustomContractResolver(Func<bool> includeProperty)
{
_includeProperty = includeProperty;
}
protected override JsonProperty CreateProperty(
MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
var shouldSerialize = property.ShouldSerialize;
property.ShouldSerialize = obj => _includeProperty() &&
(shouldSerialize == null ||
shouldSerialize(obj));
return property;
}
}
首先,我想说所有的荣誉都应该去内森·鲍尔奇(Nathan Baulch)。 这是他的答案的改编,并在设置中使用MaxDepth。 感谢您的帮助Nathan!
public static string SerializeObject(object obj, int maxDepth)
{
using (var strWriter = new StringWriter())
{
using (var jsonWriter = new CustomJsonTextWriter(strWriter))
{
Func<bool> include = () => jsonWriter.CurrentDepth <= maxDepth;
var resolver = new CustomContractResolver(include);
var serializer = new JsonSerializer {ContractResolver = resolver};
serializer.Serialize(jsonWriter, obj);
}
return strWriter.ToString();
}
}
使用:
var obj = new Node {
Name = "one",
Child = new Node {
Name = "two",
Child = new Node {
Name = "three"
}
}
};
var txt1 = SerializeObject(obj, 1);
var txt2 = SerializeObject(obj, 2);
public class Node
{
public string Name { get; set; }
public Node Child { get; set; }
}
您可以使用反射检查对象并制作根据需要更改每个属性值的副本。巧合的是,我刚刚公开了一个新图书馆,这使得这种事情变得非常容易。您可以在这里获取:
Https://github.com/jamietre/iqobjectmapper there是您要使用的代码的一个示例“映射”方法通过对象的每个属性迭代,并为每个属性调用
// instantiating JsonNetResult to handle circular reference issue.
var result = new JsonNetResult
{
Data = <<The results to be returned>>,
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Settings =
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
MaxDepth = 1
}
};
return result;
(具有反射信息,例如属性名称,类型等)。该功能返回每个属性的新值。因此,在此示例中,我只是测试每个属性的值,以查看是否是类,如果是的,则返回null;如果没有,请返回原始值。
其他更能表达的方法:
var newInstance = ObjectMapper.Map(obj,(value,del) => {
return value !=null && value.GetType().IsClass ?
null :
value;
});
在任何一种情况下,希望这有帮助。顺便说一句 - 从您的评论中,听起来JSON并不是您的目标,而是您认为可以帮助您实现目标的事情。如果您想结束JSON,只需序列化此输出,例如
var obj = new MyObject();
// map the object to a new dictionary
var dict = ObjectMapper.ToDictionary(obj);
// iterate through each item in the dictionary, a key/value pair
// representing each property
foreach (KeyValuePair<string,object> kvp in dict) {
if (kvp.Value!=null && kvp.Value.GetType().IsClass) {
dict[kvp.Key]=null;
}
}
// map back to an instance
var newObject = ObjectMapper.ToNew<MyObject>(dict);
如果您想在ASP.NET核心项目中使用此此此事,也许您无法实现自己的JSontextWriter。但是您可以自定义DefaultContractresolver和Ivalueprovider
newInstance.myChildren
在您的控制器中使用它
string json = JavaScriptSerializer.Serialize(newObject);
也许是System.Text.json的解决方案对某人很有用
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
namespace customserialization
{
/// <summary>
/// IValueProvider personalizado para manejar max depth level
/// </summary>
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;
}
}
/// <summary>
/// Maneja los niveles de serialización
/// </summary>
public class MaxDepthHandler
{
int _maxDepth;
int _currentDepthLevel;
/// <summary>
/// Nivel actual
/// </summary>
public int CurrentDepthLevel { get { return _currentDepthLevel; } }
public MaxDepthHandler(int maxDepth)
{
this._currentDepthLevel = 1;
this._maxDepth = maxDepth;
}
/// <summary>
/// Incrementa el nivel actual
/// </summary>
public void IncrementLevel()
{
this._currentDepthLevel++;
}
/// <summary>
/// Decrementa el nivel actual
/// </summary>
public void DecrementLevel()
{
this._currentDepthLevel--;
}
/// <summary>
/// Determina si se alcanzó el nivel actual
/// </summary>
/// <returns></returns>
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;
}
}
}