Lỗi JSON.NET Vòng lặp tự tham chiếu được phát hiện cho loại


495

Tôi đã cố gắng tuần tự hóa lớp POCO được tạo tự động từ Mô hình dữ liệu thực thể .edmx và khi tôi sử dụng

JsonConvert.SerializeObject 

Tôi đã nhận được lỗi sau:

Lỗi Vòng lặp tham chiếu tự phát hiện cho loại System.data.entity xảy ra.

Làm thế nào để tôi giải quyết vấn đề này?


1
có thể trùng lặp nối tiếp một đến nhiều mối quan hệ trong Json.net
bkaid

khi bạn đang sử dụng Linq và MVC: stackoverflow.com/a/38241856
aDDin

khi sử dụng .NET Core 2: stackoverflow.com/a/48709134/4496145
Dave Skender

2
Lỗi này xảy ra với tôi, khi tôi muốn tuần tự hóa kết quả của một asynccuộc gọi phương thức (a Task) và quên tiền tố awaitcâu lệnh.
Uwe Keim

Câu trả lời:


485

Đó là giải pháp tốt nhất https://code.msdn.microsoft.com/Loop-Reference-handling-in-caaffaf7

Khắc phục 1: Bỏ qua tham chiếu vòng tròn trên toàn cầu

(Tôi đã chọn / thử cái này, cũng như nhiều cái khác)

Trình tuần tự json.net có một tùy chọn để bỏ qua các tham chiếu vòng tròn. Đặt mã sau vào WebApiConfig.cstệp:

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

Cách khắc phục đơn giản sẽ khiến serializer bỏ qua tham chiếu sẽ gây ra vòng lặp. Tuy nhiên, nó có những hạn chế:

  • Dữ liệu mất thông tin tham chiếu lặp
  • Bản sửa lỗi chỉ áp dụng cho JSON.net
  • Mức độ tham chiếu không thể được kiểm soát nếu có chuỗi tham chiếu sâu

Nếu bạn muốn sử dụng bản sửa lỗi này trong dự án ASP.NET không phải là api, bạn có thể thêm dòng trên vào Global.asax.cs, nhưng trước tiên hãy thêm:

var config = GlobalConfiguration.Configuration;

Nếu bạn muốn sử dụng điều này trong dự án .Net Core , bạn có thể thay đổi Startup.csthành:

  var mvc = services.AddMvc(options =>
        {
           ...
        })
        .AddJsonOptions(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);

Cách khắc phục 2: Bảo quản tài liệu tham khảo thông tư trên toàn cầu

Sửa lỗi thứ hai này tương tự như lần đầu tiên. Chỉ cần thay đổi mã thành:

config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling 
     = Newtonsoft.Json.ReferenceLoopHandling.Serialize;     
config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling 
     = Newtonsoft.Json.PreserveReferencesHandling.Objects;

Hình dạng dữ liệu sẽ được thay đổi sau khi áp dụng cài đặt này.

[
   {
      "$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 và $ ref giữ tất cả các tham chiếu và làm cho mức đồ thị đối tượng phẳng, nhưng mã máy khách cần biết thay đổi hình dạng để tiêu thụ dữ liệu và nó cũng chỉ áp dụng cho trình tuần tự hóa JSON.NET.

Khắc phục 3: Bỏ qua và bảo toàn các thuộc tính tham chiếu

Khắc phục sự cố này là trang trí các thuộc tính trên lớp mô hình để kiểm soát hành vi tuần tự hóa ở cấp độ mô hình hoặc thuộc tính. Để bỏ qua tài sản:

 public class Category 
    { 
        public int Id { get; set; } 
        public string Name { get; set; } 

        [JsonIgnore] 
        [IgnoreDataMember] 
        public virtual ICollection<Product> Products { get; set; } 
    } 

JsonIgnore dành cho JSON.NET và IgnoreDataMember dành cho XmlDCSerializer. Để bảo quản tài liệu tham khảo:

 // 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)]dành cho JSON.NET và [DataContract(IsReference = true)]dành cho XmlDCSerializer. Lưu ý rằng: sau khi đăng ký DataContracttrên lớp, bạn cần thêm DataMembervào các thuộc tính mà bạn muốn tuần tự hóa.

Các thuộc tính có thể được áp dụng trên cả tuần tự json và xml và cung cấp thêm các điều khiển trên lớp mô hình.


7
Fix 3 được làm việc cho tôi. Chỉ cần xóa các thuộc tính DataContract và DataMember và đặt JsonObject (IsReference = true) trên DTOs. Và nó hoạt động. Cảm ơn.
maestro

1
thử cái này GlobalConfiguration.Configuration
Bishoy Hanna

1
Fix 3 có lợi thế là nó hoạt động trên mã máy khách, nơi không có GlobalConfiguration
dumbledad

1
@BishoyHanna, bạn có thể chỉnh sửa câu trả lời của mình để cho phép nó được sử dụng từ các ứng dụng ASP.NET thông thường không? Bạn có thể sử dụng chỉnh sửa được đề xuất của tôi: stackoverflow.com/review/suggested-edits/17797683
NH.

2
Sử dụng [JsonIgnore]ở trên thuộc tính làm việc cho tôi.
Nathan Beck

467

Sử dụng JsonSerializerS Settings

  • ReferenceLoopHandling.Error(mặc định) sẽ báo lỗi nếu gặp vòng lặp tham chiếu. Đây là lý do tại sao bạn nhận được một ngoại lệ.
  • ReferenceLoopHandling.Serialize là hữu ích nếu các đối tượng được lồng nhưng không vô thời hạn.
  • ReferenceLoopHandling.Ignore sẽ không tuần tự hóa một đối tượng nếu nó là một đối tượng con của chính nó.

Thí dụ:

JsonConvert.SerializeObject(YourPOCOHere, Formatting.Indented, 
new JsonSerializerSettings { 
        ReferenceLoopHandling = ReferenceLoopHandling.Serialize
});

Nếu bạn phải tuần tự hóa một đối tượng được lồng vô thời hạn, bạn có thể sử dụng PreserveObjectReferences để tránh StackOverflowException.

Thí dụ:

JsonConvert.SerializeObject(YourPOCOHere, Formatting.Indented, 
new JsonSerializerSettings { 
        PreserveReferencesHandling = PreserveReferencesHandling.Objects
});

Chọn những gì có ý nghĩa cho các đối tượng bạn đang tuần tự hóa.

Tham khảo http://james.newtonking.com/json/help/


66
Tôi đã gặp lỗi khi tuần tự hóa một dữ liệu. Tôi đã sử dụng ReferenceLoopHandling = ReferenceLoopHandling.Ignoređể nó hoạt động

8
Nếu có các vòng lặp tham chiếu trong dữ liệu, việc sử dụng ReferenceLoopHandling.Serializesẽ khiến serializer đi vào một vòng lặp đệ quy vô hạn và tràn vào ngăn xếp.
Brian Rogers

1
Chính xác. Vì câu hỏi là về một mô hình EF cũng là một mối quan tâm hợp lệ. Sửa đổi để cung cấp cho tất cả các tùy chọn có sẵn.
DalSoft

1
Tôi đã gặp phải lỗi tương tự khi cố gắng tuần tự hóa một đối tượng ... tuy nhiên, đối tượng không có bất kỳ tham chiếu nào ngoài loại enum ..
Marin

1
Đối với tôi, EF là nguyên nhân chính cho vấn đề này bởi vì các thực thể tự tham chiếu ở khắp mọi nơi.
Teoman shipahi

58

Cách khắc phục là bỏ qua các tham chiếu vòng lặp và không nối tiếp chúng. Hành vi này được chỉ định trong JsonSerializerSettings.

Độc thânJsonConvert với tình trạng quá tải:

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

Cài đặt toàn cầu với mã Application_Start()trong Global.asax.cs:

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

Tham khảo: https://github.com/JamesNK/Newtonsoft.Json/issues/78


Tại sao bạn đặt định dạng thành thụt lề khi bạn thực hiện cài đặt chung?
Murphybro2

Hoàn toàn những gì chúng ta cần để giải quyết vấn đề này (được phát hiện trong quá trình triển khai)! Bạn da man .... cảm ơn vì đã tiết kiệm thời gian cho chúng tôi !!
Ryan Eastabrook

Tôi đã giải quyết ngay lập tức bằng cách thêm "JsonConvert.DefaultSinstall" = () => new JsonSerializerSinstall {....} trong lớp "Startup.cs"
Beldi Anouar

45

Cách đơn giản nhất để làm điều này là cài đặt Json.NET từ nuget và thêm [JsonIgnore]thuộc tính vào thuộc tính ảo trong lớp, ví dụ:

    public string Name { get; set; }
    public string Description { get; set; }
    public Nullable<int> Project_ID { get; set; }

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

Mặc dù những ngày này, tôi tạo một mô hình chỉ với các thuộc tính tôi muốn chuyển qua để nó nhẹ hơn, không bao gồm các bộ sưu tập không mong muốn và tôi không mất các thay đổi của mình khi tôi xây dựng lại các tệp được tạo ...


3
Câu trả lời hay nhất bằng Newton JSON
Aizen

21

Trong .NET Core 1.0, bạn có thể đặt cài đặt này làm cài đặt chung trong tệp 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));
        });
    }

Nhưng trong trường hợp này, nếu tôi muốn biết rằng tài sản này bị bỏ qua thì tôi sẽ không nhận được bất kỳ ngoại lệ nào.
Mayer Spitzer

10

Nếu bạn đang sử dụng .NET Core 2.x, hãy cập nhật phần ConfigureService của bạn trong Startup.cs

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

    ...
    }

Nếu bạn đang sử dụng .NET Core 3.x mà không có MVC, thì đó là:

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

Việc xử lý vòng lặp tham chiếu này gần như là bắt buộc nếu bạn đang sử dụng Entity Framework và mẫu thiết kế cơ sở dữ liệu đầu tiên.


2
Nếu tôi không sử dụng services.AddMvc()thì sao?
prisar

2
Đây có phải là một thực hành xấu?
Renan Coelho

Thoạt nhìn bạn có thể nghĩ rằng đây là một thực tiễn tồi vì nó có thể ghi đè lên "thiết kế có chủ ý" để tránh vấn đề "vòng lặp vô hạn" cũ. Tuy nhiên, nếu bạn nghĩ về các trường hợp sử dụng của bạn cho các lớp, bạn có thể cần chúng để tham khảo lẫn nhau. Ví dụ: bạn có thể muốn truy cập cả Cây> Trái cây và Trái cây> Cây.
Dave Skender

Ngoài ra, nếu bạn đang sử dụng mẫu thiết kế đầu tiên cho cơ sở dữ liệu với Entity Framework, tùy thuộc vào cách bạn thiết lập khóa ngoại trong cơ sở dữ liệu của mình, nó sẽ tự động tạo các tham chiếu theo chu kỳ này, do đó bạn sẽ phải sử dụng cài đặt này nếu bạn đảo ngược kỹ thuật các lớp học của bạn.
Dave Skender

9

Để tuần tự hóa việc sử dụng NEWTONSOFTJSON khi bạn gặp vấn đề về vòng lặp, trong trường hợp của tôi, tôi không cần sửa đổi global.asax hoặc apiconfig. Tôi chỉ sử dụng JsonSerializesS Settings bỏ qua xử lý Looping.

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
Nếu bất cứ ai khác đến đây để có một ống lót đi vào cửa sổ đồng hồ để nó có thể tìm kiếm văn bản:Newtonsoft.Json.JsonConvert.SerializeObject(objToSerialize, new Newtonsoft.Json.JsonSerializerSettings() {ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore});
Graham

8

Chúng ta có thể thêm hai dòng này vào hàm tạo của lớp DbContext để vô hiệu hóa vòng lặp Tham chiếu, như

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

Đây là một trong những đơn giản nhất và làm việc như một lá bùa . Bỏ phiếu, cảm ơn rất nhiều ...
Murat Yıldız

Giống như tôi đã viết trong câu hỏi khác: Tôi không thích loại câu trả lời này vì bạn đang tắt một tính năng của EF6 được bật theo mặc định và đoạn mã này có thể phá vỡ các phần khác của chương trình. Bạn nên giải thích những gì nó làm và những hậu quả của nó.
El Mac

@ElMac bạn đúng, nhưng nếu chúng tôi không cần tính năng đó thì tại sao không thể sử dụng giải pháp này?
Sanjay Nishad

@SanjayNishad Tôi không phiền nếu bạn không cần tính năng này. Đó chỉ là về những người dùng không biết họ đang vô hiệu hóa cái gì.
El Mac

6

Bạn cũng có thể áp dụng một thuộc tính cho thuộc tính. Các [JsonProperty( ReferenceLoopHandling = ... )]thuộc tính rất thích hợp cho việc này.

Ví dụ:

/// <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...    
}

Hy vọng rằng sẽ giúp, Jaans


4

Để bỏ qua các tham chiếu vòng lặp và không tuần tự hóa chúng trên toàn cầu trong MVC 6, hãy sử dụng cách sau trong 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

Sử dụng cái này trong WebApiConfig.cslớp:

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

2

Đối với tôi, tôi đã phải đi một con đường khác. Thay vì cố gắng sửa lỗi trình tuần tự hóa JSON.Net, tôi đã phải truy cập vào Lazy Loading trên datacontext của mình.

Tôi vừa thêm nó vào kho lưu trữ cơ sở của mình:

context.Configuration.ProxyCreationEnabled = false;

Đối tượng "bối cảnh" là một tham số hàm tạo mà tôi sử dụng trong kho lưu trữ cơ sở của mình vì tôi sử dụng phép nội xạ phụ thuộc. Bạn có thể thay đổi thuộc tính ProxyCreationEnables bất cứ nơi nào bạn khởi tạo datacontext của mình.

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


2

Tôi đã có ngoại lệ này và giải pháp làm việc của tôi là Dễ dàng và Đơn giản,

Bỏ qua thuộc tính được tham chiếu bằng cách thêm thuộc tính JsonIgnore vào nó:

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

Đặt lại thuộc tính khi bạn Hủy bỏ nó:

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

sử dụng Newtonsoft.Json;


Đây là Phép thuật tôi cần. Giải quyết nó[JsonIgnore]
saviour123

2

Đội:

Điều này hoạt động với ASP.NET Core; Thách thức ở trên là cách bạn 'đặt cài đặt thành bỏ qua'. Tùy thuộc vào cách bạn thiết lập ứng dụng của mình, nó có thể khá khó khăn. Đây là những gì làm việc cho tôi.

Điều này có thể được đặt trong phần void voidService (dịch vụ IServiceCollection) của bạn.

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

2

Mọi người đã nói về [JsonIgnore] được thêm vào tài sản ảo trong lớp, ví dụ:

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

Tôi cũng sẽ chia sẻ một tùy chọn khác, [JsonProperty (NullValueHandling = NullValueHandling.Ignore)] mà bỏ qua thuộc tính từ tuần tự hóa chỉ khi nó là null:

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


0

Đơn giản chỉ cần đặt Configuration.ProxyCreationEnabled = false;bên trong tệp ngữ cảnh; Điều này sẽ giải quyết vấn đề.

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

0

Vấn đề của tôi được giải quyết với cấu hình tùy chỉnh JsonSerializerSinstall:

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

0

Vui lòng đảm bảo sử dụng await và async trong phương thức của bạn. Bạn có thể gặp lỗi này nếu đối tượng của bạn không được đăng tuần tự.


0

Tôi đã phải đối mặt với cùng một vấn đề và tôi đã thử sử dụng JsonSetting để bỏ qua lỗi tự tham chiếu của nó cho đến khi tôi có một lớp tự tham chiếu rất sâu và quá trình dot-net của tôi bị treo trên giá trị viết Json.

Vấn đề của tôi

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

}

Bạn có thể thấy vấn đề trong lớp Người dùng đang đề cập đến lớp CompanyUser là tự tham khảo.

Bây giờ, tôi đang gọi Phương thức GetAll bao gồm tất cả các thuộc tính quan hệ.

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

Trong giai đoạn này, quy trình DotNetCore của tôi bị treo trên Thi hành JsonResult, viết giá trị ... và không bao giờ đến. Trong Startup.cs của tôi, tôi đã thiết lập JsonOption. Vì một số lý do, EFCore bao gồm cả tài sản lồng nhau mà tôi không yêu cầu Ef đưa ra.

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

hành vi dự kiến ​​nên thế này

Xin chào EfCore, bạn có thể vui lòng bao gồm dữ liệu "CompanyUsers" trong lớp Công ty của tôi để tôi có thể dễ dàng truy cập dữ liệu.

sau đó

Xin chào EfCore, bạn cũng có thể vui lòng bao gồm dữ liệu "CompanyUsers.User" để tôi có thể dễ dàng truy cập dữ liệu như Company.CompanyUsers.First (). User.DisplayName

ở giai đoạn này tôi chỉ nên lấy "Company.CompanyUsers.First (). User.DisplayName" này và nó không nên cung cấp cho tôi Company.CompanyUsers.First (). User.CompanyUsers gây ra vấn đề tự tham chiếu; Về mặt kỹ thuật, nó không nên cung cấp cho tôi User.CompanyUsersCompanyUsers là một tài sản điều hướng. Nhưng, EfCore rất phấn khích và mang đến cho tôi User.CompanyUsers .

Vì vậy, tôi quyết định viết một phương thức mở rộng để loại trừ thuộc tính khỏi đối tượng (thực tế không loại trừ nó chỉ đặt thuộc tính thành null). Không chỉ vậy nó cũng sẽ làm việc trên các thuộc tính mảng. bên dưới là mã tôi cũng sẽ xuất gói nuget cho những người dùng khác (không chắc điều này có giúp được ai đó không). Lý do rất đơn giản vì tôi quá lười viết .Select (n => new {n.p1, n.p2}); Tôi chỉ không muốn viết câu lệnh chọn để loại trừ chỉ 1 thuộc tính!

Đây không phải là mã tốt nhất (tôi sẽ cập nhật ở một số giai đoạn) vì tôi đã viết vội vàng và mặc dù điều này có thể giúp ai đó muốn loại trừ (đặt null) trong đối tượng bằng các mảng.

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

lớp mở rộng ở trên sẽ cung cấp cho bạn khả năng đặt thuộc tính thành null để tránh vòng lặp tự tham chiếu ngay cả các mảng.

Trình tạo biểu thức

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

Tập quán:

Lớp học mẫu

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

Dữ liệu giả

           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" } } },

                }
            }
        };

Các trường hợp:

Trường hợp 1: Chỉ loại trừ tài sản mà không có bất kỳ mảng nào

p.Exclude(n => n.AddressDetail.CountryDetail.CountryName);

Trường hợp 2: Loại trừ tài sản với 1 mảng

p1.Exclude(n => n.AddressDetail.CountryDetail2.Explode().CountryName);

Trường hợp 3: Loại trừ tài sản với 2 mảng lồng nhau

p1.Exclude(n => n.AddressDetail.CountryDetail2.Explode().CountryDetail.Explode().Name);

Trường hợp 4: Truy vấn Get Get của EF với Bao gồm

var query = cs.GetAll("CompanyUsers", "CompanyUsers.User").ToArray();
query.Exclude(n => n.Explode().CompanyUsers.Explode().User.CompanyUsers);
return query;

Bạn có lưu ý rằng phương thức Explode () cũng là một phương thức mở rộng chỉ dành cho trình xây dựng biểu thức của chúng ta để lấy thuộc tính từ thuộc tính mảng. Bất cứ khi nào có thuộc tính mảng, hãy sử dụng .Explode (). YourPropertyToExclude hoặc .Explode (). Property1.MyArrayProperty.Explode (). MyStoolProperty . Mã trên giúp tôi tránh được việc tự giới thiệu quá sâu như tôi muốn. Bây giờ tôi có thể sử dụng GetAll và loại trừ tài sản mà tôi không muốn!

Cảm ơn bạn đã đọc bài viết lớn này!



-1

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

Đây thực chất là hướng dẫn tương tự như được đưa ra trong câu trả lời được đánh giá cao của @ DalSoft từ tám năm trước, nhưng với rất ít lời giải thích.
Jeremy Caney

Hy vọng Nó sẽ giải quyết vấn đề nhưng vui lòng thêm giải thích về mã của bạn với nó để người dùng sẽ hiểu hoàn hảo những gì anh ấy / cô ấy thực sự muốn.
Jaimil Patel

-2

Tôi thích giải pháp thực hiện nó Application_Start()như trong câu trả lời ở đây

Rõ ràng tôi không thể truy cập các đối tượng json trong JavaScript bằng cách sử dụng cấu hình trong chức năng của mình như trong câu trả lời của DalSoft vì đối tượng được trả về có "\ n \ r" trên toàn bộ (khóa, val) của đối tượng.

Dù sao, bất cứ điều gì cũng tốt (vì các cách tiếp cận khác nhau hoạt động theo kịch bản khác nhau dựa trên các nhận xét và câu hỏi được hỏi) mặc dù cách làm tiêu chuẩn sẽ tốt hơn với một số tài liệu tốt hỗ trợ cách tiếp cận.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.