JSON.NET 检测到类型的错误自引用循环
本文关键字:错误 自引用 循环 类型 NET 检测 JSON | 更新日期: 2023-09-27 18:08:29
>我试图序列化从实体数据模型.edmx自动生成的POCO类,当我使用
JsonConvert.SerializeObject
我收到以下错误:
检测到 System.data.entity 类型的自引用循环出错。
如何解决此问题?
使用 JsonSerializerSettings
- 如果遇到引用循环,
ReferenceLoopHandling.Error
(默认值(将出错。这就是您获得例外的原因。 - 如果对象是嵌套的,
ReferenceLoopHandling.Serialize
很有用,但不是无限期的。 - 如果对象是自身的子对象,则
ReferenceLoopHandling.Ignore
不会序列化该对象。
例:
JsonConvert.SerializeObject(YourPOCOHere, Formatting.Indented,
new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Serialize
});
如果必须无限期序列化嵌套的对象,则可以使用 PreserveObjectReferences 来避免 StackOverflowException。
例:
JsonConvert.SerializeObject(YourPOCOHere, Formatting.Indented,
new JsonSerializerSettings
{
PreserveReferencesHandling = PreserveReferencesHandling.Objects
});
选择对要序列化的对象有意义的内容。
参考 http://james.newtonking.com/json/help/
解决方法是忽略循环引用,而不是序列化它们。此行为在 JsonSerializerSettings
中指定。
带过载的单JsonConvert
:
JsonConvert.SerializeObject(YourObject, Formatting.Indented,
new JsonSerializerSettings() {
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
}
);
全局设置,在 Global.asax 中Application_Start()
代码.cs:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings {
Formatting = Newtonsoft.Json.Formatting.Indented,
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
};
参考: https://github.com/JamesNK/Newtonsoft.Json/issues/78
方法是安装 Json.NET 从 nuget 并将 [JsonIgnore]
属性添加到类中的虚拟属性,例如:
public string Name { get; set; }
public string Description { get; set; }
public Nullable<int> Project_ID { get; set; }
[JsonIgnore]
public virtual Project Project { get; set; }
尽管这些天,我创建了一个模型,其中只包含我想要传递的属性,因此它更轻,不包括不需要的集合,并且在重建生成的文件时不会丢失更改......
在 .NET Core 1.0 中,可以在 Startup.cs 文件中将其设置为全局设置:
using System.Buffers;
using Microsoft.AspNetCore.Mvc.Formatters;
using Newtonsoft.Json;
// beginning of Startup class
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(options =>
{
options.OutputFormatters.Clear();
options.OutputFormatters.Add(new JsonOutputFormatter(new JsonSerializerSettings(){
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
}, ArrayPool<char>.Shared));
});
}
如果您使用的是 .NET Core 2.x,请更新Startup.cs
中的ConfigureServices
部分
https://learn.microsoft.com/en-us/ef/core/querying/related-data/serialization
public void ConfigureServices(IServiceCollection services)
{
...
services.AddMvc()
.AddJsonOptions(options =>
options.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
...
}
如果您使用的是 .NET Core 3.x - 5.0,没有 MVC,它将是:
# startup.cs
services.AddControllers()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
对于 .NET 6.0,唯一的区别是它现在进入 Program.cs
.
# program.cs
services.AddControllers()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Ignore);
如果使用实体框架和数据库优先设计模式,则此引用循环处理几乎是必需的。
我们可以将这两行添加到 DbContext 类构造函数中以禁用自引用循环,例如
public TestContext()
: base("name=TestContext")
{
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
}
当你遇到循环问题时,要序列化我们的牛顿软件json,就我而言,我不需要修改global.asax或apiconfig。我只是使用 JsonSerializesSettings 忽略循环处理。
JsonSerializerSettings jss = new JsonSerializerSettings();
jss.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
var lst = db.shCards.Where(m => m.CardID == id).ToList();
string json = JsonConvert.SerializeObject(lst, jss);
您也可以将属性应用于属性。[JsonProperty( ReferenceLoopHandling = ... )]
属性非常适合这一点。
例如:
/// <summary>
/// Represents the exception information of an event
/// </summary>
public class ExceptionInfo
{
// ...code omitted for brevity...
/// <summary>
/// An inner (nested) error.
/// </summary>
[JsonProperty( ReferenceLoopHandling = ReferenceLoopHandling.Ignore, IsReference = true )]
public ExceptionInfo Inner { get; set; }
// ...code omitted for brevity...
}
希望有帮助,Jaans
要在 MVC 6 中忽略循环引用并且不全局序列化它们,请在 startup.cs 中使用以下命令:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().Configure<MvcOptions>(options =>
{
options.OutputFormatters.RemoveTypesOf<JsonOutputFormatter>();
var jsonOutputFormatter = new JsonOutputFormatter();
jsonOutputFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
options.OutputFormatters.Insert(0, jsonOutputFormatter);
});
}
只需更新服务。AddControllers(( in Startup.cs file
services.AddControllers()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
在WebApiConfig.cs
类中使用它:
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
对我来说,我必须走一条不同的路线。 我没有尝试修复 JSON.Net 序列化程序,而是不得不在我的数据上下文上进行延迟加载。
我刚刚将其添加到我的基本存储库中:
context.Configuration.ProxyCreationEnabled = false;
"context"对象是我在基本存储库中使用的构造函数参数,因为我使用依赖注入。 您可以在实例化数据上下文的任何位置更改 ProxyCreationEnabled 属性。
http://techie-tid-bits.blogspot.com/2015/09/jsonnet-serializer-and-error-self.html
我有这个例外,我的工作解决方案是简单易行的,
通过向其添加 JsonIgnore 属性来忽略引用属性:
[JsonIgnore]
public MyClass currentClass { get; set; }
反序列化时重置属性:
Source = JsonConvert.DeserializeObject<MyObject>(JsonTxt);
foreach (var item in Source)
{
Source.MyClass = item;
}
使用Newtonsoft.Json;
团队:
这适用于 ASP.NET 核心;上述挑战在于如何"设置忽略"。 根据您设置应用程序的方式,这可能非常具有挑战性。这是对我有用的。
这可以放在您的公共无效配置服务(IServiceCollection服务(部分。
services.AddMvc().AddJsonOptions(opt =>
{
opt.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
人们已经讨论过将 [JsonIgnore] 添加到类中的虚拟属性中,例如:
[JsonIgnore]
public virtual Project Project { get; set; }
我还将分享另一个选项,[JsonProperty(NullValueHandling = NullValueHandling.Ignore(],仅当属性为空时,它才会从序列化中省略该属性:
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public virtual Project Project { get; set; }
在 .Net 5.x 中,使用以下代码在启动时更新 ConfigureServices 方法.cs
public void ConfigureServices(IServiceCollection services)
{
----------------
----------------
services.AddMvc().AddJsonOptions(options =>
{
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
});
------------------
}
默认情况下,序列化 (System.Text.Json.Serialization( 不支持具有循环的对象,并且不保留重复引用。使用"保留">在序列化时启用唯一对象引用保留,并使用元数据在反序列化时读取保留的引用。MSDN Link
C# 代码:
var jsonSerializerSettings = new JsonSerializerSettings
{
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
};
var jsonString = JsonConvert.SerializeObject(object2Serialize, jsonSerializerSettings);
var filePath = @"E:'json.json";
File.WriteAllText(filePath, jsonString);
<</div>
div class="answers">我的问题解决了自定义配置 JsonSerializerSettings:
services.AddMvc(
// ...
).AddJsonOptions(opt =>
{
opt.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Serialize;
opt.SerializerSettings.PreserveReferencesHandling =
Newtonsoft.Json.PreserveReferencesHandling.Objects;
});
对于 .NET Core 3.0,请更新 Startup.cs 类,如下所示。
public void ConfigureServices(IServiceCollection services)
{
...
services.AddControllers()
.AddNewtonsoftJson(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
...
}
请参阅:https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-core-3-0-preview-5/
还请确保在方法中使用 await 和 async。如果对象未正确序列化,则可能会出现此错误。
我继承了一个数据库应用程序,它将数据模型提供给网页。 默认情况下,序列化将尝试遍历整个模型树,这里的大多数答案都是如何防止这种情况的良好开端。
尚未探索的一种选择是使用接口来提供帮助。 我将从前面的例子中窃取:
public partial class CompanyUser
{
public int Id { get; set; }
public int CompanyId { get; set; }
public int UserId { get; set; }
public virtual Company Company { get; set; }
public virtual User User { get; set; }
}
public interface IgnoreUser
{
[JsonIgnore]
User User { get; set; }
}
public interface IgnoreCompany
{
[JsonIgnore]
User User { get; set; }
}
public partial class CompanyUser : IgnoreUser, IgnoreCompany
{
}
在上述解决方案中,没有 Json 设置受到损害。 将 LazyLoadEnabled 和/或 ProxyCreationEnabled 设置为 false 会影响您的所有后端编码,并阻止 ORM 工具的一些真正好处。 根据您的应用程序,延迟加载/代理创建设置可以阻止加载导航属性,而无需手动加载它们。
这是一个更好的解决方案,用于防止导航属性序列化,它使用标准 json 功能:如何使 JSON 序列化程序忽略导航属性?
只需将Configuration.ProxyCreationEnabled = false;
放在上下文文件中;这将解决问题。
public demEntities()
: base("name=demEntities")
{
Configuration.ProxyCreationEnabled = false;
}
我遇到了同样的问题,我尝试使用 JsonSetting 来忽略自引用错误,直到我得到一个非常深入的自引用类,我的 dot-net 过程挂在 Json 写入值上。
我的问题
public partial class Company : BaseModel
{
public Company()
{
CompanyUsers = new HashSet<CompanyUser>();
}
public string Name { get; set; }
public virtual ICollection<CompanyUser> CompanyUsers { get; set; }
}
public partial class CompanyUser
{
public int Id { get; set; }
public int CompanyId { get; set; }
public int UserId { get; set; }
public virtual Company Company { get; set; }
public virtual User User { get; set; }
}
public partial class User : BaseModel
{
public User()
{
CompanyUsers = new HashSet<CompanyUser>();
}
public string DisplayName { get; set; }
public virtual ICollection<CompanyUser> CompanyUsers { get; set; }
}
您可以在用户类中看到问题,它引用了 CompanyUser 类,这是一个自我引用。
现在,我调用包含所有关系属性的 GetAll 方法。
cs.GetAll("CompanyUsers", "CompanyUsers.User");
在这个阶段,我的DotNetCore进程挂在执行JsonResult上,写入值......永远不会到来。在我的Startup.cs中,我已经设置了JsonOption。出于某种原因,EFCore 包含嵌套属性,我不要求 Ef 提供。
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
预期行为应为此
嘿,EfCore,您能否在我的数据中包含"公司用户"数据 公司类,以便我可以轻松访问数据。
然后
嘿,EfCore,您能否将"公司用户.用户">数据作为 好吧,这样我就可以轻松访问这样的数据 Company.CompanyUsers.First((.用户.显示名称
在这个阶段,我应该只得到这个"公司.公司用户.第一((。User.DisplayName">,它不应该给我 Company.CompanyUsers.First((。用户.公司导致自引用问题的用户;从技术上讲,它不应该给我User.CompanyUsers,因为CompanyUsers是一个导航属性。但是,EfCore非常兴奋,给了我User.CompanyUsers。
因此,我决定为要从对象中排除的属性编写一个扩展方法(它实际上并不排除它只是将属性设置为 null(。不仅如此,它还适用于数组属性。下面是我还将为其他用户导出 nuget 包的代码(不确定这是否对某人有帮助(。原因很简单,因为我懒得写.Select(n => new { n.p1, n.p2}(;我只是不想写选择语句来排除 1 个属性!
这不是最好的代码(我会在某个阶段更新(,因为我匆忙编写,尽管这可能会帮助那些想要在带有数组的对象中排除(设置 null(的人。
public static class PropertyExtensions
{
public static void Exclude<T>(this T obj, Expression<Func<T, object>> expression)
{
var visitor = new PropertyVisitor<T>();
visitor.Visit(expression.Body);
visitor.Path.Reverse();
List<MemberInfo> paths = visitor.Path;
Action<List<MemberInfo>, object> act = null;
int recursiveLevel = 0;
act = (List<MemberInfo> vPath, object vObj) =>
{
// set last propert to null thats what we want to avoid the self-referencing error.
if (recursiveLevel == vPath.Count - 1)
{
if (vObj == null) throw new ArgumentNullException("Object cannot be null");
vObj.GetType().GetMethod($"set_{vPath.ElementAt(recursiveLevel).Name}").Invoke(vObj, new object[] { null });
return;
}
var pi = vObj.GetType().GetProperty(vPath.ElementAt(recursiveLevel).Name);
if (pi == null) return;
var pv = pi.GetValue(vObj, null);
if (pi.PropertyType.IsArray || pi.PropertyType.Name.Contains("HashSet`1") || pi.PropertyType.Name.Contains("ICollection`1"))
{
var ele = (IEnumerator)pv.GetType().GetMethod("GetEnumerator").Invoke(pv, null);
while (ele.MoveNext())
{
recursiveLevel++;
var arrItem = ele.Current;
act(vPath, arrItem);
recursiveLevel--;
}
if (recursiveLevel != 0) recursiveLevel--;
return;
}
else
{
recursiveLevel++;
act(vPath, pv);
}
if (recursiveLevel != 0) recursiveLevel--;
};
// check if the root level propert is array
if (obj.GetType().IsArray)
{
var ele = (IEnumerator)obj.GetType().GetMethod("GetEnumerator").Invoke(obj, null);
while (ele.MoveNext())
{
recursiveLevel = 0;
var arrItem = ele.Current;
act(paths, arrItem);
}
}
else
{
recursiveLevel = 0;
act(paths, obj);
}
}
public static T Explode<T>(this T[] obj)
{
return obj.FirstOrDefault();
}
public static T Explode<T>(this ICollection<T> obj)
{
return obj.FirstOrDefault();
}
}
上面的扩展类将使您能够将属性设置为 null,以避免自引用循环偶数组。
表达式生成器
internal class PropertyVisitor<T> : ExpressionVisitor
{
public readonly List<MemberInfo> Path = new List<MemberInfo>();
public Expression Modify(Expression expression)
{
return Visit(expression);
}
protected override Expression VisitMember(MemberExpression node)
{
if (!(node.Member is PropertyInfo))
{
throw new ArgumentException("The path can only contain properties", nameof(node));
}
Path.Add(node.Member);
return base.VisitMember(node);
}
}
用法:
模型类
public class Person
{
public string Name { get; set; }
public Address AddressDetail { get; set; }
}
public class Address
{
public string Street { get; set; }
public Country CountryDetail { get; set; }
public Country[] CountryDetail2 { get; set; }
}
public class Country
{
public string CountryName { get; set; }
public Person[] CountryDetail { get; set; }
}
虚拟数据
var p = new Person
{
Name = "Adeel Rizvi",
AddressDetail = new Address
{
Street = "Sydney",
CountryDetail = new Country
{
CountryName = "AU"
}
}
};
var p1 = new Person
{
Name = "Adeel Rizvi",
AddressDetail = new Address
{
Street = "Sydney",
CountryDetail2 = new Country[]
{
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A1" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A2" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A3" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A4" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A5" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A6" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A7" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A8" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A9" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A1" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A2" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A3" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A4" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A5" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A6" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A7" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A8" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A9" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
}
}
};
例:
情况 1:仅排除没有任何数组的属性
p.Exclude(n => n.AddressDetail.CountryDetail.CountryName);
情况 2:使用 1 个数组排除属性
p1.Exclude(n => n.AddressDetail.CountryDetail2.Explode().CountryName);
情况 3:排除具有 2 个嵌套数组的属性
p1.Exclude(n => n.AddressDetail.CountryDetail2.Explode().CountryDetail.Explode().Name);
事例 4:包含的 EF GetAll 查询
var query = cs.GetAll("CompanyUsers", "CompanyUsers.User").ToArray();
query.Exclude(n => n.Explode().CompanyUsers.Explode().User.CompanyUsers);
return query;
您已经注意到 Explode(( 方法也是一个扩展方法,仅用于我们的表达式生成器从数组属性中获取属性。只要有数组属性,请使用 .爆炸((。您的属性要排除或 .爆炸((。Property1.MyArrayProperty.Explode((.我的愚蠢财产。上面的代码帮助我避免自我引用,就像我想要的深度一样深。现在我可以使用 GetAll 并排除我不想要的属性!
感谢您阅读这篇大文章!
与这里的其他人非常相似,通过使用序列化程序设置解决。 然而,我的起源是因为我正在使用 FromObject 方面的东西,因为我需要一个 JObject 来使用。
这意味着只需要创建默认序列化程序并对其应用设置。简单的修复变成这个
var deserializerSettings = new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Serialize,
PreserveReferencesHandling = PreserveReferencesHandling.Objects,
};
var serializer = JsonSerializer.CreateDefault(deserializerSettings);
JObject jsonObject = (JObject)JToken.FromObject(mySelfRefernceObject, serializer);
此问题来自第三方 nuget 包之一。没有更改配置或序列化的选项。在我的消费者项目中安装 Newtonsoft.Json 包解决了这个问题。
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<</div>
div class="answers"> 因为没有循环这对我有用-
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
我已经在这里解决了所有问题 - 使用 .Net Core 2 WebAPI 进行实体框架子序列化https://gist.github.com/Kaidanov/f9ad0d79238494432f32b8407942c606
将不胜感激任何评论。也许有人可以在某个时候使用它。
Application_Start()
开始的解决方案,就像这里的答案一样
显然,我无法使用函数中的配置访问 JavaScript 中的 json 对象,就像 DalSoft 的答案一样,因为返回的对象在对象的(键,val(上都有"' ''r"。
无论如何,任何有效的方法都是很好的(因为根据提出的评论和问题,不同的方法在不同的场景中工作(,尽管最好使用一些支持该方法的良好文档来执行标准方法。