如何在C#中将JSON对象序列化或反序列化到一定深度?
我只想要对象的第一个深度级别(我不想要任何子级)。我愿意使用任何可用的图书馆。大多数库仅在达到递归深度时抛出异常,而不是忽略异常。如果这是不可能的,有没有一种方法可以忽略给定特定数据类型的特定成员的序列化 编辑: 假设我有这样一个对象:如何在C#中将JSON对象序列化或反序列化到一定深度?,c#,.net,json,serialization,C#,.net,Json,Serialization,我只想要对象的第一个深度级别(我不想要任何子级)。我愿意使用任何可用的图书馆。大多数库仅在达到递归深度时抛出异常,而不是忽略异常。如果这是不可能的,有没有一种方法可以忽略给定特定数据类型的特定成员的序列化 编辑: 假设我有这样一个对象: class MyObject { String name = "Dan"; int age = 88; List<Children> myChildren = ...(lots of children with lots of
class MyObject
{
String name = "Dan";
int age = 88;
List<Children> myChildren = ...(lots of children with lots of grandchildren);
}
类MyObject
{
字符串name=“Dan”;
年龄=88岁;
列出我的孩子=…(很多孩子有很多孙子);
}
我想删除所有子对象(甚至是复杂类型)以返回如下对象:
class MyObject
{
String name = "Dan";
int age = 88;
List<Children> myChildren = null;
}
类MyObject
{
字符串name=“Dan”;
年龄=88岁;
List myChildren=null;
}
在和序列化程序之间使用某种协调是可能的
自定义JsonWriter
在对象启动时递增计数器,然后在对象结束时再次递减计数器
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
对所有用于验证当前深度的属性应用特殊谓词
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;
}
}
您可以使用反射来检查对象,并制作一个副本,根据需要更改每个属性值。巧合的是,我刚刚公开了一个新的图书馆,使这类事情变得非常容易。你可以在这里找到它: 下面是一个您将使用的代码示例
var newInstance = ObjectMapper.Map(obj,(value,del) => {
return value !=null && value.GetType().IsClass ?
null :
value;
});
“Map”方法迭代对象的每个属性,并为每个属性调用Func
(IDelegateInfo具有反射信息,如属性名称、类型等)。函数返回每个属性的新值。所以在这个例子中,我只是测试每个属性的值,看看它是否是一个类,如果是,则返回null;如果不是,则返回原始值
另一种更具表现力的方式:
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);
但如果json仅仅是达到目的的一种手段,我就不会涉及它;如果您想留在CLR对象中,那么就不需要使用JSON作为中介。首先,我想说的是,所有的功劳都应该归于Nathan Baulch。这是对他的答案的改编,并结合使用MaxDepth in设置。谢谢你的帮助,内森
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
namespace Helpers
{
public class JsonNetResult : JsonResult
{
public JsonNetResult()
{
Settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Error
};
}
public JsonSerializerSettings Settings { get; private set; }
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException("JSON GET is not allowed");
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = string.IsNullOrEmpty(this.ContentType) ? "application/json" : this.ContentType;
if (this.ContentEncoding != null)
response.ContentEncoding = this.ContentEncoding;
if (this.Data == null)
return;
var scriptSerializer = JsonSerializer.Create(this.Settings);
using (var sw = new StringWriter())
{
if (Settings.MaxDepth != null)
{
using (var jsonWriter = new JsonNetTextWriter(sw))
{
Func<bool> include = () => jsonWriter.CurrentDepth <= Settings.MaxDepth;
var resolver = new JsonNetContractResolver(include);
this.Settings.ContractResolver = resolver;
var serializer = JsonSerializer.Create(this.Settings);
serializer.Serialize(jsonWriter, Data);
}
response.Write(sw.ToString());
}
else
{
scriptSerializer.Serialize(sw, this.Data);
response.Write(sw.ToString());
}
}
}
}
public class JsonNetTextWriter : JsonTextWriter
{
public JsonNetTextWriter(TextWriter textWriter) : base(textWriter) { }
public int CurrentDepth { get; private set; }
public override void WriteStartObject()
{
CurrentDepth++;
base.WriteStartObject();
}
public override void WriteEndObject()
{
CurrentDepth--;
base.WriteEndObject();
}
}
public class JsonNetContractResolver : DefaultContractResolver
{
private readonly Func<bool> _includeProperty;
public JsonNetContractResolver(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;
}
}
}
使用Newtonsoft.Json;
使用Newtonsoft.Json.Serialization;
使用制度;
使用System.Collections.Generic;
使用System.IO;
使用System.Linq;
运用系统反思;
使用System.Web;
使用System.Web.Mvc;
命名空间帮助程序
{
公共类JsonNetResult:JsonResult
{
公共JsonNetResult()
{
设置=新JsonSerializerSettings
{
ReferenceLoopHandling=ReferenceLoopHandling.Error
};
}
公共JsonSerializerSettings设置{get;private set;}
公共覆盖无效ExecuteSult(ControllerContext上下文)
{
if(上下文==null)
抛出新的ArgumentNullException(“上下文”);
if(this.JsonRequestBehavior==JsonRequestBehavior.DenyGet&&string.Equals(context.HttpContext.Request.HttpMethod,“GET”,StringComparison.OrdinalIgnoreCase))
抛出新的InvalidOperationException(“不允许JSON获取”);
HttpResponseBase response=context.HttpContext.response;
response.ContentType=string.IsNullOrEmpty(this.ContentType)?“application/json”:this.ContentType;
if(this.ContentEncoding!=null)
response.ContentEncoding=this.ContentEncoding;
if(this.Data==null)
返回;
var scriptSerializer=JsonSerializer.Create(this.Settings);
使用(var sw=new StringWriter())
{
如果(Settings.MaxDepth!=null)
{
使用(var jsonWriter=new jsonnetextwriter(sw))
{
Func include=()=>jsonWriter.CurrentDepth\u includeProperty()&&
(shouldSerialize==null||
应该序列化(obj));
归还财产;
}
}
}
使用:
//实例化JsonNetResult以处理循环引用问题。
var result=new JsonNetResult
{
数据=,
JsonRequestBehavior=JsonRequestBehavior.AllowGet,
背景=
{
ReferenceLoopHandling=ReferenceLoopHandling.Ignore,
MaxDepth=1
}
};
返回结果;
如果您想在ASP.NET核心项目中使用此功能,可能无法实现自己的JsonTextWriter。但您可以自定义DefaultContractResolver和IValueProvider
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;
}
}
}
你能举一些JSON问题的例子吗?你能创建一个没有任何子对象的新对象并序列化它吗?从逻辑上讲这是有意义的,但我想去掉任何object类型的对象的子对象。我认为JSON序列化将是最好的方法,但我绝对愿意接受建议。我不能让它在ac中工作库Json.Net的实际版本。似乎从未调用CustomContractResolver方法。抱歉,我错过了其中明确说明的部分:CreatePROPERTY…我的坏。普通成员需要更多的了解吗?有解决方案吗?很遗憾,没有处理这个问题。我添加了这个类,它工作得非常好。尽管我的child节点是一个列表,出于某种原因,我的结束节点包含空对象而不是无对象。是否有办法防止这种情况发生?它可能无法工作,因为这里有对已释放闭包的访问:
Func include=()=>jsonWriter.CurrentDepth我喜欢这个解决方案主要是因为我可以保持当前的JsonSerializerSettings
值。问题是CurrentDepthLevel
永远不会重置,所以它只在每个控制器方法上序列化一次时起作用。如果需要序列化多个对象(或多次)如果你想重新使用分解器,你会有麻烦的。
string json = JavaScriptSerializer.Serialize(newObject);
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Web;
using System.Web.Mvc;
namespace Helpers
{
public class JsonNetResult : JsonResult
{
public JsonNetResult()
{
Settings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Error
};
}
public JsonSerializerSettings Settings { get; private set; }
public override void ExecuteResult(ControllerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
if (this.JsonRequestBehavior == JsonRequestBehavior.DenyGet && string.Equals(context.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException("JSON GET is not allowed");
HttpResponseBase response = context.HttpContext.Response;
response.ContentType = string.IsNullOrEmpty(this.ContentType) ? "application/json" : this.ContentType;
if (this.ContentEncoding != null)
response.ContentEncoding = this.ContentEncoding;
if (this.Data == null)
return;
var scriptSerializer = JsonSerializer.Create(this.Settings);
using (var sw = new StringWriter())
{
if (Settings.MaxDepth != null)
{
using (var jsonWriter = new JsonNetTextWriter(sw))
{
Func<bool> include = () => jsonWriter.CurrentDepth <= Settings.MaxDepth;
var resolver = new JsonNetContractResolver(include);
this.Settings.ContractResolver = resolver;
var serializer = JsonSerializer.Create(this.Settings);
serializer.Serialize(jsonWriter, Data);
}
response.Write(sw.ToString());
}
else
{
scriptSerializer.Serialize(sw, this.Data);
response.Write(sw.ToString());
}
}
}
}
public class JsonNetTextWriter : JsonTextWriter
{
public JsonNetTextWriter(TextWriter textWriter) : base(textWriter) { }
public int CurrentDepth { get; private set; }
public override void WriteStartObject()
{
CurrentDepth++;
base.WriteStartObject();
}
public override void WriteEndObject()
{
CurrentDepth--;
base.WriteEndObject();
}
}
public class JsonNetContractResolver : DefaultContractResolver
{
private readonly Func<bool> _includeProperty;
public JsonNetContractResolver(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;
}
}
}
// 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;
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;
}
}
}
[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);
}