유형에 대해 JSON.NET 오류 자체 참조 루프가 발견되었습니다.


494

Entity Data Model .edmx에서 자동으로 생성 된 POCO 클래스를 직렬화하려고했습니다.

JsonConvert.SerializeObject 

다음과 같은 오류가 발생했습니다.

오류 System.data.entity 유형에 대해 자체 참조 루프가 발견되었습니다.

이 문제를 어떻게 해결합니까?



Linq 및 MVC를 사용하는 경우 : stackoverflow.com/a/38241856
aDDin

사용하는 경우 .NET 코어 2 : stackoverflow.com/a/48709134/4496145
데이브 Skender

2
이 오류는 async메소드 호출 (a Task) 의 결과를 직렬화 하고 await명령문 접두어를 잊어 버렸을 때 나에게 발생했습니다 .
Uwe Keim

답변:


485

이것이 최고의 솔루션이었습니다 https://code.msdn.microsoft.com/Loop-Reference-handling-in-caaffaf7

수정 1 : 전체적으로 순환 참조 무시

(다른 많은 사람들과 마찬가지로 이것을 선택 / 시도했습니다)

json.net 시리얼 라이저에는 순환 참조를 무시하는 옵션이 있습니다. 다음 코드를 WebApiConfig.cs파일에 넣으십시오 .

 config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling 
= Newtonsoft.Json.ReferenceLoopHandling.Ignore; 

간단한 수정으로 인해 serializer가 루프를 유발하는 참조를 무시하게됩니다. 그러나 다음과 같은 제한이 있습니다.

  • 데이터에서 루핑 참조 정보가 손실됩니다
  • 수정 사항은 JSON.net에만 적용됩니다.
  • 딥 레퍼런스 체인이있는 경우 레퍼런스 레벨을 제어 할 수 없습니다

비 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);

수정 2 : 전체적으로 순환 참조 유지

이 두 번째 수정은 첫 번째 수정과 유사합니다. 코드를 다음과 같이 변경하십시오.

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 직렬 변환기에만 적용됩니다.

수정 3 : 참조 속성 무시 및 보존

이 수정 사항은 모델 또는 속성 수준에서 직렬화 동작을 제어하기 위해 모델 클래스의 특성을 장식합니다. 속성을 무시하려면

 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 serializer 모두에 적용 할 수 있으며 모델 클래스에 대한 추가 제어를 제공합니다.


7
수정 3이 나를 위해 일했습니다. DataContract 및 DataMember 속성을 제거하고 DTO에 JsonObject (IsReference = true)를 추가하기 만하면됩니다. 그리고 작동합니다. 감사.
maestro

1
이 것을 시도해보십시오 GlobalConfiguration.Configuration
Bishoy Hanna

1
Fix 3은 GlobalConfiguration이없는 클라이언트 코드에서 작동한다는 장점이 있습니다.
dumbledad

1
@BishoyHanna, 일반 ASP.NET 응용 프로그램에서 사용할 수 있도록 답변을 편집 할 수 있습니까? 내 제안 된 편집을 사용할 수 있습니다 : stackoverflow.com/review/suggested-edits/17797683
NH.

2
[JsonIgnore]위의 속성을 사용하면 나를 위해 일했습니다.
Nathan Beck

467

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/


66
데이터 테이블을 직렬화 할 때 오류가 발생했습니다. 내가 사용하는 ReferenceLoopHandling = ReferenceLoopHandling.Ignore작업에 대한

8
데이터에 참조 루프가있는 경우을 사용 ReferenceLoopHandling.Serialize하면 직렬 변환기가 무한 재귀 루프로 들어가 스택에 오버플로가 발생합니다.
Brian Rogers

1
옳은. 질문은 EF 모델에 관한 것이므로 유효한 관심사입니다. 사용 가능한 모든 옵션을 제공하도록 수정되었습니다.
DalSoft

1
객체를 직렬화하려고 할 때 이와 동일한 오류가 발생했습니다. 그러나 객체에 열거 형 이외의 참조가 없습니다.
Marin

1
나에게 EF는 자체 참조 엔터티가 모든 곳에 있기 때문에이 문제의 주요 원인입니다.
Teoman shipahi

57

수정은 루프 참조를 무시하고 직렬화하지 않는 것입니다. 이 동작은에 지정되어 JsonSerializerSettings있습니다.

JsonConvert과부하가있는 싱글 :

JsonConvert.SerializeObject(YourObject, Formatting.Indented,
    new JsonSerializerSettings() {
        ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
    }
);

Application_Start()Global.asax.cs에 코드를 사용한 전역 설정 :

JsonConvert.DefaultSettings = () => new JsonSerializerSettings {
     Formatting = Newtonsoft.Json.Formatting.Indented,
     ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
};

참조 : https://github.com/JamesNK/Newtonsoft.Json/issues/78


전역 설정을 할 때 왜 형식을 들여 쓰기로 설정합니까?
Murphybro2

이 문제를 해결하기 위해 반드시 필요한 것 (배포 중에 발견)! 당신은 남자 .... 시간을 절약 주셔서 감사합니다 !!
Ryan Eastabrook

클래스 "Startup.cs"에 "JsonConvert.DefaultSettings"= () => 새 JsonSerializerSettings {....}를 추가하여 issus를 해결했습니다.
Beldi Anouar

45

가장 간단한 방법은 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; }

요즘에는 통과하려는 속성 만 사용하여 모델을 만들므로 더 가볍고 원치 않는 컬렉션이 포함되지 않으며 생성 된 파일을 다시 빌드 할 때 변경 사항을 잃지 않습니다 ...


3
Newton JSON을 사용한 베스트 답변
Aizen

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));
        });
    }

그러나이 경우이 속성이 무시된다는 것을 알고 싶다면 예외가 발생하지 않습니다.
메이어 스피처

10

.NET Core 2.x를 사용하는 경우 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
        );

    ...
    }

MVC없이 .NET Core 3.x를 사용하는 경우 다음과 같습니다.

services.AddControllers()
  .AddNewtonsoftJson(options =>
      options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
   );

이 참조 루프 처리는 Entity Framework 및 데이터베이스 우선 디자인 패턴을 사용하는 경우 거의 필수입니다.


2
내가 사용하지 않으면 어떻게 services.AddMvc()됩니까?
prisar

2
이것은 나쁜 습관입니까?
Renan Coelho

언뜻보기에 이것은 오래된 "무한 루프"문제를 피하는 "의도적 디자인"을 무시할 수 있기 때문에 이것이 나쁜 습관이라고 생각할 수 있습니다. 그러나 클래스의 사용 사례에 대해 생각하면 서로 참조해야 할 수도 있습니다. 예를 들어 Trees> Fruits와 Fruits> Trees에 모두 액세스 할 수 있습니다.
Dave Skender

또한 Entity Framework와 같은 데이터베이스 최초 디자인 패턴을 사용하는 경우 데이터베이스에서 외래 키를 설정하는 방법에 따라 이러한 순환 참조가 자동으로 생성 되므로이 설정을 사용해야합니다. 수업을 리버스 엔지니어링하고 있습니다.
Dave Skender

9

루프 문제가있을 때 NEWTONSOFTJSON에서 직렬화하기 위해 필자의 경우 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);

1
다른 사람이 한 창을보기 위해 한 창을 보러 갔다면 텍스트를 검색 할 수 있습니다.Newtonsoft.Json.JsonConvert.SerializeObject(objToSerialize, new Newtonsoft.Json.JsonSerializerSettings() {ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore});
Graham

8

이 두 줄을 DbContext 클래스 생성자에 추가하여 다음과 같이 자체 참조 루프를 비활성화 할 수 있습니다.

public TestContext()
        : base("name=TestContext")
{
    this.Configuration.LazyLoadingEnabled = false;
    this.Configuration.ProxyCreationEnabled = false;
}

이것은 가장 간단한 것 중 하나이며 매력처럼 작동합니다 . 투표, 감사합니다 ...
Murat Yıldız

다른 질문에서 쓴 것처럼 : 기본적으로 활성화 된 EF6 기능을 해제 하고이 코드 조각이 프로그램의 다른 부분을 손상시킬 수 있기 때문에 이런 종류의 답변을 좋아하지 않습니다. 이것이 무엇을하고 어떤 영향을 미치는지 설명해야합니다.
El Mac

@ElMac이 맞습니다. 그러나 해당 기능이 필요하지 않은 경우이 솔루션을 사용할 수없는 이유는 무엇입니까?
Sanjay Nishad

@SanjayNishad이 기능이 필요하지 않더라도 상관 없습니다. 무엇을 사용 중지했는지 모르는 사용자에 관한 것입니다.
El Mac

6

속성에도 속성을 적용 할 수 있습니다. [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...    
}

도움이 되길 바랍니다


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);
        });
    }

2

WebApiConfig.cs클래스 에서 이것을 사용하십시오 :

var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);

2

나를 위해 나는 다른 길을 가야했다. JSON.Net 직렬 변환기를 수정하는 대신 데이터 컨텍스트에서 지연 로딩을 수행해야했습니다.

방금 이것을 기본 저장소에 추가했습니다.

context.Configuration.ProxyCreationEnabled = false;

"context"객체는 의존성 주입을 사용하기 때문에 기본 리포지토리에서 사용하는 생성자 매개 변수입니다. 대신 데이터 컨텍스트를 인스턴스화하는 모든 위치에서 ProxyCreationEnabled 속성을 변경할 수 있습니다.

http://techie-tid-bits.blogspot.com/2015/09/jsonnet-serializer-and-error-self.html


2

나는이 예외가 있었고 작업 솔루션은 쉽고 간단합니다.

JsonIgnore 속성을 추가하여 Referenced 속성을 무시하십시오.

[JsonIgnore]
public MyClass currentClass { get; set; }

역 직렬화 할 때 속성을 재설정하십시오.

Source = JsonConvert.DeserializeObject<MyObject>(JsonTxt);
foreach (var item in Source)
        {
            Source.MyClass = item;
        }

Newtonsoft.Json 사용;


이것이 내가 필요한 마술입니다. 해결[JsonIgnore]
saviour123

2

팀:

이것은 ASP.NET Core에서 작동합니다. 위의 과제는 '설정을 무시하도록 설정하는 방법'입니다. 응용 프로그램 설정 방법에 따라 매우 어려울 수 있습니다. 여기 나를 위해 일한 것이 있습니다.

이것은 공공 void ConfigureServices (IServiceCollection services) 섹션에 배치 할 수 있습니다.

services.AddMvc().AddJsonOptions(opt => 
        { 
      opt.SerializerSettings.ReferenceLoopHandling =
      Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        });

2

사람들은 이미 클래스의 가상 속성에 [JsonIgnore]가 추가되는 것에 대해 이야기했습니다.

[JsonIgnore]
public virtual Project Project { get; set; }

또한 다른 옵션 인 [JsonProperty (NullValueHandling = NullValueHandling.Ignore)]를 공유하여 속성이 null 인 경우에만 직렬화에서 속성을 생략합니다.

[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public virtual Project Project { get; set; }


0

간단하게 배치 Configuration.ProxyCreationEnabled = false;상황에 맞는 파일 내부; 문제가 해결됩니다.

public demEntities()
    : base("name=demEntities")
{
    Configuration.ProxyCreationEnabled = false;
}

0

사용자 정의 구성 JsonSerializer 설정으로 문제가 해결되었습니다.

services.AddMvc(
  // ...
               ).AddJsonOptions(opt =>
                 {
                opt.SerializerSettings.ReferenceLoopHandling =
                    Newtonsoft.Json.ReferenceLoopHandling.Serialize;
                opt.SerializerSettings.PreserveReferencesHandling =
                    Newtonsoft.Json.PreserveReferencesHandling.Objects;
                 });

0

또한 메소드에서 await 및 async를 사용하십시오. 개체가 올바르게 직렬화되지 않으면이 오류가 발생할 수 있습니다.


0

나는 같은 문제에 직면 해 있었고 자체 참조 오류를 무시하기 위해 JsonSetting을 사용하려고 시도했습니다.

내 문제

    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를 참조하는 User 클래스에서 문제를 볼 수 있습니다.자체 참조 인 클래스를 .

이제 모든 관계형 속성을 포함하는 GetAll 메서드를 호출합니다.

cs.GetAll("CompanyUsers", "CompanyUsers.User");

이 단계에서 내 DotNetCore 프로세스는 JsonResult 실행, 값 쓰기 ... 에 멈추고 결코 오지 않습니다. 내 Startup.cs에서 이미 JsonOption을 설정했습니다. 어떤 이유로 EFCore는 Ef에게 묻지 않는 중첩 된 속성을 포함합니다.

    options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;

예상되는 동작은 다음과 같습니다

EfCore, 회사 클래스에 "CompanyUsers"데이터를 포함 시켜서 데이터에 쉽게 액세스 할 수 있도록하십시오.

그때

EfCore 도이 Company.CompanyUsers.First (). User.DisplayName 과 같은 데이터에 쉽게 액세스 할 수 있도록 "CompanyUsers.User" 데이터 도 포함 시키십시오 .

이 단계에서는이 "Company.CompanyUsers.First (). User.DisplayName"만 가져와야 하며 자체 참조 문제를 일으키는 Company.CompanyUsers.First (). User.CompanyUsers 를 제공해서는 안됩니다 . 기술적으로 그것은 UserUser 를 제공해서는 안됩니다. CompanyUsers 는 탐색 속성입니다. 그러나 EfCore는 매우 흥분되어 User.CompanyUsers를 제공합니다 .

그래서 객체에서 속성이 제외되도록 확장 메소드를 작성하기로 결정했습니다 (실제로는 속성을 null로 설정하는 것을 제외하지는 않습니다). 뿐만 아니라 배열 속성에서도 작동합니다. 아래는 다른 사용자를 위해 너겟 패키지를 내보내는 코드입니다 (이것이 누군가에게 도움이되는지 확실하지 않습니다). 쓰기가 너무 게으 르기 때문에 이유는 간단합니다. .Select (n => new {n.p1, n.p2}); 단지 1 개의 속성 만 제외하도록 select 문을 작성하고 싶지 않습니다!

이것은 서둘러 작성 한 최고의 코드 (일부 단계에서 업데이트 할 것)는 아니지만 배열을 사용하여 객체에서 제외 (널 설정)하려는 사람에게 도움이 될 수 있습니다.

    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 (). YourPropertyToExclude 또는 .Explode (). Property1.MyArrayProperty.Explode (). MyStupidProperty를 사용하십시오 . 위의 코드는 내가 원하는 깊이의 자체 참조를 피하는 데 도움이됩니다. 이제 GetAll을 사용하고 내가 원하지 않는 속성을 제외시킬 수 있습니다!

이 큰 글을 읽어 주셔서 감사합니다!



-1

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);

이것은 본질적으로 8 년 전에 @DalSoft의 높은 답변에서 제공 한 것과 동일한 지침이지만 설명은 훨씬 적습니다.
Jeremy Caney

희망 그것은 문제를 해결하지만 코드에 대한 설명을 추가하여 사용자가 실제로 원하는 것을 완벽하게 이해할 수 있도록하십시오.
Jaimil Patel

-2

나는에서 그것을 수행하는 솔루션 좋아 Application_Start()대답에서와 같이 여기를

분명히 반환 된 객체가 객체의 (키, val) 전체에 "\ n \ r"을 가지고 있기 때문에 DalSoft의 답변과 같이 내 기능 내 구성을 사용하여 JavaScript의 json 객체에 액세스 할 수 없었습니다.

어쨌든 모든 작업은 훌륭합니다 (다른 접근법은 요청과 의견에 따라 다른 시나리오에서 작동하기 때문에). 표준 작업 방식은 접근 방식을 지원하는 훌륭한 문서로 선호됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.