我尝试序列化从实体数据模型.edmx自动生成的POCO类,当我使用时
JsonConvert.SerializeObject
我收到以下错误:
错误检测到类型System.data.entity检测到自引用循环。
如何解决这个问题?
答案 0 :(得分:417)
使用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
});
选择对序列化对象有意义的内容。
答案 1 :(得分:392)
那是最好的解决方案 https://code.msdn.microsoft.com/Loop-Reference-handling-in-caaffaf7
(我和其他许多人一样选择/试过这个)
json.net序列化程序可以选择忽略循环引用。将以下代码放在WebApiConfig.cs
文件中:
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling
= Newtonsoft.Json.ReferenceLoopHandling.Ignore;
简单修复将使序列化程序忽略将导致循环的引用。但是,它有局限性:
如果要在非api ASP.NET项目中使用此修补程序,可以将上面的行添加到Global.asax.cs
,但首先添加:
var config = GlobalConfiguration.Configuration;
如果要在 .Net Core 项目中使用此功能,可以将Startup.cs
更改为:
var mvc = services.AddMvc(options =>
{
...
})
.AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
第二个修复与第一个类似。只需将代码更改为:
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling
= Newtonsoft.Json.ReferenceLoopHandling.Serialize;
config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling
= Newtonsoft.Json.PreserveReferencesHandling.Objects;
应用此设置后,数据形状将会更改。
[
{
"$id":"1",
"Category":{
"$id":"2",
"Products":[
{
"$id":"3",
"Category":{
"$ref":"2"
},
"Id":2,
"Name":"Yogurt"
},
{
"$ref":"1"
}
],
"Id":1,
"Name":"Diary"
},
"Id":1,
"Name":"Whole Milk"
},
{
"$ref":"3"
}
]
$ id和$ ref保留所有引用并使对象图层保持平坦,但客户端代码需要知道形状更改才能使用数据,它也只适用于JSON.NET序列化程序。
此修复是模型类上的decorate属性,用于控制模型或属性级别的序列化行为。要忽略该属性:
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
[JsonIgnore]
[IgnoreDataMember]
public virtual ICollection<Product> Products { get; set; }
}
JsonIgnore用于JSON.NET,IgnoreDataMember用于XmlDCSerializer。 保留参考:
// Fix 3
[JsonObject(IsReference = true)]
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
// Fix 3
//[JsonIgnore]
//[IgnoreDataMember]
public virtual ICollection<Product> Products { get; set; }
}
[DataContract(IsReference = true)]
public class Product
{
[Key]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
[DataMember]
public virtual Category Category { get; set; }
}
JsonObject(IsReference = true)]
用于JSON.NET,[DataContract(IsReference = true)]
用于XmlDCSerializer。请注意:在课程上应用DataContract
后,您需要将DataMember
添加到要序列化的属性中。
这些属性可以应用于json和xml序列化程序,并对模型类提供更多控制。
答案 2 :(得分:47)
修复是忽略循环引用而不是序列化它们。此行为在JsonSerializerSettings
。
单JsonConvert
:
JsonConvert.SerializeObject(YourObject, Formatting.Indented,
new JsonSerializerSettings() {
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
}
);
在Global.asax.cs中使用Application_Start()
中的代码全局设置:
JsonConvert.DefaultSettings = () => new JsonSerializerSettings {
Formatting = Newtonsoft.Json.Formatting.Indented,
ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
};
答案 3 :(得分:38)
最简单的方法是从nuget安装Json.NET
并将[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; }
虽然现在,我创建的模型只包含我想要传递的属性,因此它更轻,不包含不需要的集合,并且在重建生成的文件时我不会丢失更改...
答案 4 :(得分:21)
在.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));
});
}
答案 5 :(得分:7)
我们可以将这两行添加到DbContext类构造函数中以禁用自引用循环,如
public TestContext()
: base("name=TestContext")
{
this.Configuration.LazyLoadingEnabled = false;
this.Configuration.ProxyCreationEnabled = false;
}
答案 6 :(得分:5)
如果您使用的是.NET Core 2.0,请更新Startup.cs中的ConfigureServices部分
https://docs.microsoft.com/en-us/ef/core/querying/related-data#related-data-and-serialization
public void ConfigureServices(IServiceCollection services)
{
...
services.AddMvc()
.AddJsonOptions(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
...
}
答案 7 :(得分:5)
要在循环问题时序列化使用NEWTONSOFTJSON,在我的情况下我不需要修改global.asax或者apiconfig。我只是使用JsonSerializesSettings忽略循环处理。
pthread_mutex_destroy
答案 8 :(得分:4)
您也可以将属性应用于该属性。
[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
答案 9 :(得分:4)
要忽略循环引用而不是在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);
});
}
答案 10 :(得分:2)
我有这个例外,我的工作解决方案简单易用,
通过向其添加JsonIgnore属性来忽略Referenced属性:
[JsonIgnore]
public MyClass currentClass { get; set; }
反序列化时重置属性:
Source = JsonConvert.DeserializeObject<MyObject>(JsonTxt);
foreach (var item in Source)
{
Source.MyClass = item;
}
使用Newtonsoft.Json;
答案 11 :(得分:2)
对我来说,我必须走另一条路。而不是尝试修复JSON.Net序列化程序,我不得不在我的datacontext上进行延迟加载。
我刚把它添加到我的基础存储库中:
context.Configuration.ProxyCreationEnabled = false;
“context”对象是我在基本存储库中使用的构造函数参数,因为我使用了依赖注入。您可以在实例化datacontext的任何地方更改ProxyCreationEnabled属性。
http://techie-tid-bits.blogspot.com/2015/09/jsonnet-serializer-and-error-self.html
答案 12 :(得分:2)
在WebApiConfig.cs
类:
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
答案 13 :(得分:1)
人们已经讨论过[JsonIgnore]被添加到类的虚拟属性中,例如:
[JsonIgnore]
public virtual Project Project { get; set; }
我还将共享另一个选项[JsonProperty(NullValueHandling = NullValueHandling.Ignore)],该选项仅在属性为null时才将其从序列化中删除:
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public virtual Project Project { get; set; }
答案 14 :(得分:1)
团队:
这适用于ASP.NET Core;上面的挑战是如何“设置忽略设置”。根据您设置应用程序的方式,这可能会非常具有挑战性。这对我有用。
这可以放在您的公共void ConfigureServices(IServiceCollection服务)部分中。
services.AddMvc().AddJsonOptions(opt =>
{
opt.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
答案 15 :(得分:1)
对于.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/
答案 16 :(得分:1)
只需更新 Startup.cs 文件中的 services.AddControllers()
services.AddControllers()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
答案 17 :(得分:0)
只需将Configuration.ProxyCreationEnabled = false;
放在上下文文件中;这将解决问题。
public demEntities()
: base("name=demEntities")
{
Configuration.ProxyCreationEnabled = false;
}
答案 18 :(得分:0)
使用自定义配置JsonSerializerSettings解决了我的问题:
services.AddMvc(
// ...
).AddJsonOptions(opt =>
{
opt.SerializerSettings.ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Serialize;
opt.SerializerSettings.PreserveReferencesHandling =
Newtonsoft.Json.PreserveReferencesHandling.Objects;
});
答案 19 :(得分:0)
还请确保在您的方法中使用等待和异步。如果您的对象未正确序列化,则会出现此错误。
答案 20 :(得分:0)
我遇到了同样的问题,我尝试使用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; }
}
您可以在User类中看到该问题,它引用的是自引用的 CompanyUser 类。
现在,我正在调用包含所有关系属性的GetAll方法。
cs.GetAll("CompanyUsers", "CompanyUsers.User");
在此阶段,我的DotNetCore进程挂在执行JsonResult,写入值... 上,并且再也没有实现。在我的Startup.cs中,我已经设置了JsonOption。由于某些原因,EFCore包含了嵌套属性,我不要求Ef提供。
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
预期的行为应该是
嘿,EfCore能否也将“ CompanyUsers”数据包括在我的 公司类,这样我就可以轻松访问数据。
然后
嘿,EfCore还可以将“ CompanyUsers.User” 数据包括为 好吧,这样我就可以轻松访问这样的数据 Company.CompanyUsers.First()。User.DisplayName
在此阶段,我应该只获得此“ Company.CompanyUsers.First()。User.DisplayName” ,并且不应给我 Company.CompanyUsers.First()。User。导致自引用问题的CompanyUsers ;从技术上讲,它不应该给我 User.CompanyUsers ,因为CompanyUsers是导航属性。但是,EfCore感到非常兴奋,并给了我 User.CompanyUsers 。
因此,我决定编写一种扩展方法,以将属性排除在对象之外(实际上并不排除它只是将属性设置为null)。不仅如此,它还将对数组属性起作用。下面是我还将为其他用户导出nuget包的代码(不确定是否对某人有帮助)。原因很简单,因为我懒得写 .Select(n => new {n.p1,n.p2}); 我只是不想编写select语句以仅排除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,以避免自引用循环甚至数组。
Expression Builder
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()方法也是它的扩展方法,仅用于表达式构建器从数组属性中获取属性。只要有数组属性,请使用 .Explode()。YourPropertyToExclude或.Explode()。Property1.MyArrayProperty.Explode()。MyStupidProperty 。上面的代码帮助我避免了自我引用的深度。现在我可以使用GetAll并排除我不想要的属性!
感谢您阅读这篇重要文章!
答案 21 :(得分:0)
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);
答案 22 :(得分:0)
我继承了一个数据库应用程序,该应用程序将数据模型提供给网页。默认情况下,序列化将尝试遍历整个模型树,此处的大多数答案是如何防止这种情况的一个好的开始。
尚未探索的一个选项是使用界面来提供帮助。我将从先前的示例中窃取:
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设置受到损害。将LazyLoadingEnabled和/或ProxyCreationEnabled设置为false会影响您的所有后端编码,并阻止ORM工具的某些真正好处。根据您的应用程序,LazyLoading / ProxyCreation设置可以阻止在不手动加载导航属性的情况下加载它们。
这是防止导航属性序列化的更好得多的解决方案,它使用标准的json功能: How can I do JSON serializer ignore navigation properties?
答案 23 :(得分:0)
在 .Net 5.x 中,使用以下代码更新 startup.cs 中的 ConfigureServices 方法
public void ConfigureServices(IServiceCollection services)
{
----------------
----------------
services.AddMvc().AddJsonOptions(options =>
{
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve;
});
------------------
}
默认情况下,序列化(System.Text.Json.Serialization)不支持带循环的对象,也不保留重复引用。使用 Preserve 在序列化和元数据消耗时启用唯一对象引用保留,以读取反序列化时保留的引用。 MSDN Link
答案 24 :(得分:-1)
对于不循环这对我有用 -
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
我在这里解决了所有问题 - 使用.Net Core 2 WebAPI进行实体框架子级序列化 https://gist.github.com/Kaidanov/f9ad0d79238494432f32b8407942c606
欢迎任何评论。 也许有人可以在某个时候使用它。
答案 25 :(得分:-2)
我喜欢Application_Start()
中的解决方案,如答案here
显然我无法使用我的函数中的配置访问JavaScript中的json对象,就像DalSoft的回答一样,因为返回的对象有&#34; \ n \ r&#34;遍布对象的(key,val)。
无论如何,无论哪种工作都很棒(因为根据所提出的评论和问题,不同的方法可以在不同的场景中工作),尽管采用标准的方法来支持这种方法会更好。