Kesalahan JSON.NET loop referensi diri terdeteksi untuk tipe

495

Saya mencoba untuk membuat serial kelas POCO yang secara otomatis dihasilkan dari Entity Data Model .edmx dan ketika saya menggunakannya

JsonConvert.SerializeObject 

Saya mendapat kesalahan berikut:

Terjadi kesalahan referensi otomatis terdeteksi untuk tipe System.data.entity.

Bagaimana saya mengatasi masalah ini?

NevenHuynh
sumber
1
kemungkinan duplikat Serialize hubungan satu ke banyak di Json.net
bkaid
ketika Anda menggunakan Linq dan MVC: stackoverflow.com/a/38241856
aDDin
saat menggunakan .NET Core 2: stackoverflow.com/a/48709134/4496145
Dave Skender
2
Kesalahan ini terjadi pada saya, ketika saya ingin membuat serialisasi hasil asyncpemanggilan metode (a Task) dan lupa untuk mengawali awaitpernyataan itu.
Uwe Keim

Jawaban:

485

Itu adalah solusi terbaik https://code.msdn.microsoft.com/Loop-Reference-handling-in-caaffaf7

Perbaiki 1: Mengabaikan referensi melingkar secara global

(Saya telah memilih / mencoba yang ini, seperti yang lainnya)

Serializer json.net memiliki opsi untuk mengabaikan referensi melingkar. Masukkan kode berikut ke dalam WebApiConfig.csfile:

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

Perbaikan sederhana akan membuat serializer mengabaikan referensi yang akan menyebabkan perulangan. Namun, ia memiliki keterbatasan:

  • Data kehilangan informasi referensi perulangan
  • Perbaikan hanya berlaku untuk JSON.net
  • Tingkat referensi tidak dapat dikontrol jika ada rantai referensi yang mendalam

Jika Anda ingin menggunakan perbaikan ini dalam proyek ASP.NET non-api, Anda dapat menambahkan baris di atas Global.asax.cs, tetapi pertama-tama tambahkan:

var config = GlobalConfiguration.Configuration;

Jika Anda ingin menggunakan ini dalam proyek .Net Core , Anda dapat mengubahnya Startup.cssebagai:

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

Fix 2: Mempertahankan referensi melingkar secara global

Perbaikan kedua ini mirip dengan yang pertama. Ubah saja kodenya menjadi:

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

Bentuk data akan diubah setelah menerapkan pengaturan ini.

[
   {
      "$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 dan $ ref menjaga semua referensi dan membuat tingkat objek grafik datar, tetapi kode klien perlu mengetahui perubahan bentuk untuk mengkonsumsi data dan itu hanya berlaku untuk JSON.NET serializer juga.

Perbaiki 3: Abaikan dan pertahankan atribut referensi

Perbaikan ini menghias atribut pada kelas model untuk mengontrol perilaku serialisasi pada model atau tingkat properti. Untuk mengabaikan properti:

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

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

JsonIgnore untuk JSON.NET dan IgnoreDataMember untuk XmlDCSerializer. Untuk menyimpan referensi:

 // 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)]untuk JSON.NET dan [DataContract(IsReference = true)]untuk XmlDCSerializer. Perhatikan bahwa: setelah mendaftar DataContractdi kelas, Anda perlu menambahkan DataMemberproperti yang ingin Anda serialkan.

Atribut dapat diterapkan pada serializer json dan xml dan memberikan lebih banyak kontrol pada kelas model.

Bishoy Hanna
sumber
7
Memperbaiki 3 bekerja untuk saya. Cukup hapus atribut DataContract dan DataMember, dan masukkan JsonObject (IsReference = true) pada DTO. Dan itu berhasil. Terima kasih.
maestro
1
coba yang satu ini GlobalConfiguration.Configuration
Bishoy Hanna
1
Fix 3 memiliki keunggulan karena berfungsi pada kode klien di mana tidak ada GlobalConfiguration
dumbledad
1
@BishoyHanna, bisakah Anda mengedit jawaban Anda agar dapat digunakan dari aplikasi ASP.NET biasa? Anda dapat menggunakan edit yang saya sarankan: stackoverflow.com/review/suggested-edits/17797683
NH.
2
Menggunakan [JsonIgnore]atribut di atas bekerja untuk saya.
Nathan Beck
467

Gunakan JsonSerializerSettings

  • ReferenceLoopHandling.Error(default) akan salah jika loop referensi ditemukan. Inilah sebabnya mengapa Anda mendapatkan pengecualian.
  • ReferenceLoopHandling.Serialize berguna jika objek bersarang tetapi tidak tanpa batas.
  • ReferenceLoopHandling.Ignore tidak akan membuat cerita bersambung objek jika objek anak itu sendiri.

Contoh:

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

Jika Anda harus membuat cerita bersambung dari suatu objek yang bersarang tanpa batas waktu Anda dapat menggunakan PreserveObjectReferences untuk menghindari StackOverflowException.

Contoh:

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

Pilih apa yang masuk akal untuk objek yang Anda serialkan.

Referensi http://james.newtonking.com/json/help/

DalSoft
sumber
66
Saya mengalami kesalahan ketika membuat serial data. Saya menggunakannya ReferenceLoopHandling = ReferenceLoopHandling.Ignoreuntuk bekerja
8
Jika ada loop referensi dalam data, menggunakan ReferenceLoopHandling.Serializeakan menyebabkan serializer masuk ke loop rekursif tak terbatas dan meluap tumpukan.
Brian Rogers
1
Benar. Karena pertanyaannya adalah tentang model EF juga kekhawatiran yang valid. Diubah untuk memberikan semua opsi yang tersedia.
DalSoft
1
Saya mengalami kesalahan yang sama ketika mencoba membuat serial suatu objek ... namun, objek tersebut tidak memiliki referensi selain tipe enum ..
Marin
1
bagi saya EF adalah penyebab utama masalah ini karena entitas yang dirujuk sendiri ada di mana-mana.
Teoman shipahi
58

Cara mengatasinya adalah dengan mengabaikan referensi loop dan tidak membuat serial mereka. Perilaku ini ditentukan dalam JsonSerializerSettings.

SingleJsonConvert dengan kelebihan:

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

Pengaturan Global dengan kode di Application_Start()dalam Global.asax.cs:

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

Referensi: https://github.com/JamesNK/Newtonsoft.Json/issues/78

smockle
sumber
Mengapa Anda mengatur format ke indentasi ketika Anda melakukan pengaturan global?
Murphybro2
Benar-benar apa yang kami butuhkan untuk menyelesaikan masalah ini (ditemukan saat pemasangan)! You da man .... terima kasih telah menghemat waktu kami !!
Ryan Eastabrook
Saya memecahkan masalah saya dengan menambahkan "JsonConvert.DefaultSettings" = () => JsonSerializerSettings {....} baru di kelas "Startup.cs"
Beldi Anouar
45

Cara paling sederhana untuk melakukan ini adalah menginstal Json.NET dari nuget dan menambahkan [JsonIgnore]atribut ke properti virtual di kelas, misalnya:

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

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

Meskipun hari ini, saya membuat model dengan hanya properti yang ingin saya lewati sehingga lebih ringan, tidak termasuk koleksi yang tidak diinginkan, dan saya tidak kehilangan perubahan ketika saya membangun kembali file yang dihasilkan ...

Sam Jones
sumber
3
Jawaban terbaik menggunakan Newton JSON
Aizen
21

Di .NET Core 1.0, Anda dapat mengatur ini sebagai pengaturan global di file Startup.cs Anda:

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));
        });
    }
Caleb
sumber
Tetapi dalam hal ini, jika saya ingin mengetahui bahwa properti ini Diabaikan saya tidak akan mendapatkan pengecualian.
Mayer Spitzer
10

Jika Anda menggunakan .NET Core 2.x, perbarui bagian ConfigureServices Anda di 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
        );

    ...
    }

Jika Anda menggunakan .NET Core 3.x tanpa MVC, itu akan menjadi:

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

Penanganan loop referensi ini hampir wajib jika Anda menggunakan Entity Framework dan pola desain basis data pertama.

Dave Skender
sumber
2
bagaimana jika saya tidak menggunakan services.AddMvc()?
prisar
2
apakah ini praktik yang buruk?
Renan Coelho
Pada pandangan pertama Anda mungkin berpikir ini adalah praktik yang buruk karena mungkin mengesampingkan "desain yang disengaja" untuk menghindari masalah "loop tak terbatas" lama. Namun, jika Anda berpikir tentang kasus penggunaan Anda untuk kelas, Anda mungkin memerlukannya untuk merujuk satu sama lain. Misalnya, Anda mungkin ingin mengakses Pohon> Buah dan juga Buah> Pohon.
Dave Skender
Juga, jika Anda menggunakan pola desain pertama-database dengan sesuatu seperti Entity Framework, tergantung pada bagaimana Anda mengatur kunci asing Anda dalam database Anda, itu akan secara otomatis membuat referensi siklus ini, sehingga Anda cukup banyak harus menggunakan pengaturan ini jika Anda Sedang merekayasa ulang kelas Anda.
Dave Skender
9

Untuk membuat serial kami di NEWTONSOFTJSON ketika Anda memiliki masalah loop, dalam kasus saya, saya tidak perlu memodifikasi global.asax atau apiconfig. Saya hanya menggunakan JsonSerializesSettings mengabaikan penanganan 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);
Carlos Barini
sumber
1
Jika ada orang lain datang ke sini untuk melihat satu liner di jendela arloji sehingga dapat dicari melalui teks:Newtonsoft.Json.JsonConvert.SerializeObject(objToSerialize, new Newtonsoft.Json.JsonSerializerSettings() {ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore});
Graham
8

Kita bisa menambahkan dua baris ini ke konstruktor kelas DbContext untuk menonaktifkan loop referensi diri

public TestContext()
        : base("name=TestContext")
{
    this.Configuration.LazyLoadingEnabled = false;
    this.Configuration.ProxyCreationEnabled = false;
}
Sanjay Nishad
sumber
Ini adalah salah satu yang paling sederhana dan bekerja seperti pesona . Terpilih, terima kasih banyak ...
Murat Yıldız
Seperti yang saya tulis dalam pertanyaan lain: Saya tidak suka jawaban seperti ini karena Anda mematikan fitur EF6 yang diaktifkan secara default dan kode ini dapat merusak bagian lain dari program. Anda harus menjelaskan apa yang dilakukannya dan dampak apa yang dimilikinya.
El Mac
@ ElMac Anda benar, tetapi jika kita tidak membutuhkan fitur itu, mengapa tidak menggunakan solusi ini?
Sanjay Nishad
@SanjayNishad Saya tidak keberatan jika Anda tidak membutuhkan fitur ini. Ini hanya tentang pengguna yang tidak tahu apa yang mereka nonaktifkan.
El Mac
6

Anda dapat menerapkan atribut ke properti juga. Itu[JsonProperty( ReferenceLoopHandling = ... )] atribut baik cocok untuk ini.

Sebagai contoh:

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

Semoga itu bisa membantu, Jaans

Jaans
sumber
4

Untuk mengabaikan referensi loop dan tidak membuat serialisasi secara global di MVC 6 gunakan yang berikut ini di 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);
        });
    }
GerardBeckerleg
sumber
2

Gunakan ini di WebApiConfig.cskelas:

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

Bagi saya, saya harus menempuh rute yang berbeda. Alih-alih mencoba untuk memperbaiki serializer JSON.Net saya harus pergi setelah Pemuatan Malas pada datacontext saya.

Saya baru saja menambahkan ini ke repositori dasar saya:

context.Configuration.ProxyCreationEnabled = false;

Objek "konteks" adalah parameter konstruktor yang saya gunakan dalam repositori basis saya karena saya menggunakan injeksi dependensi. Anda bisa mengubah properti ProxyCreationEnabled di mana pun Anda instantiate datacontext Anda.

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

Xipooo
sumber
2

Saya memiliki pengecualian ini dan solusi kerja saya Mudah dan Sederhana,

Abaikan properti yang direferensikan dengan menambahkan atribut JsonIgnore ke dalamnya:

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

Setel ulang properti ketika Anda Deserialize:

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

menggunakan Newtonsoft.Json;

Mayer Spitzer
sumber
Inilah Sihir yang saya butuhkan. Selesaikan[JsonIgnore]
saviour123
2

Tim:

Ini bekerja dengan ASP.NET Core; Tantangan di atas adalah bagaimana Anda 'mengatur pengaturan untuk diabaikan'. Tergantung pada bagaimana Anda mengatur aplikasi Anda, itu bisa sangat menantang. Inilah yang bekerja untuk saya.

Ini dapat ditempatkan di bagian ConfigureServices (Layanan IServiceCollection) publik Anda.

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

Orang-orang sudah berbicara tentang [JsonIgnore] yang ditambahkan ke properti virtual di kelas, misalnya:

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

Saya juga akan berbagi opsi lain, [JsonProperty (NullValueHandling = NullValueHandling.Ignore)] yang menghilangkan properti dari serialisasi hanya jika itu nol:

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

Untuk .NET Core 3.0, perbarui kelas Startup.cs seperti yang ditunjukkan di bawah ini.

public void ConfigureServices(IServiceCollection services)
{
...

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

...
}

Lihat: https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-core-3-0-preview-5/

Jimmy
sumber
0

Cukup letakkan Configuration.ProxyCreationEnabled = false;di dalam file konteks; ini akan menyelesaikan masalah.

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

Masalah Saya Dipecahkan dengan Custom Config JsonSerializerSettings:

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

Pastikan juga untuk menggunakan wait and async dalam metode Anda. Anda bisa mendapatkan kesalahan ini jika objek Anda tidak serial dengan benar.

maxspan
sumber
0

Saya menghadapi masalah yang sama dan saya mencoba menggunakan JsonSetting untuk mengabaikan kesalahan referensi-diri itu agak berhasil sampai saya mendapat kelas yang referensi diri sangat dalam dan proses dot-net saya tergantung pada nilai penulisan Json.

Masalahku

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

}

Anda dapat melihat masalah di kelas Pengguna yang dirujuk ke CompanyUser kelas yang merupakan referensi-sendiri.

Sekarang, saya memanggil Metode GetAll yang mencakup semua properti relasional.

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

Pada tahap ini proses DotNetCore saya tergantung pada Executing JsonResult, menulis nilai ... dan tidak pernah datang. Di Startup.cs saya, saya sudah mengatur JsonOption. Untuk beberapa alasan EFCore termasuk properti bersarang yang saya tidak minta Ef berikan.

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

perilaku yang diharapkan seharusnya seperti ini

Hei EfCore dapatkah Anda memasukkan data "CompanyUsers" juga di kelas Perusahaan saya sehingga saya dapat dengan mudah mengakses data.

kemudian

Hei EfCore dapatkah Anda juga memasukkan data "CompanyUsers.User" juga sehingga saya dapat dengan mudah mengakses data seperti Perusahaan ini . CompanyUsers.First (). User.DisplayName

pada tahap ini saya seharusnya hanya mendapatkan "Perusahaan.Perusahaan Pengguna.Pertama ini (). User.DisplayName" dan seharusnya tidak memberi saya Company.Perusahaan Pengguna.Pertama (). Pengguna.Perusahaan Pengguna yang menyebabkan masalah referensi diri; Secara teknis seharusnya tidak memberi saya User.CompanyUsers sebagai CompanyUsers adalah properti navigasi. Tapi, EfCore menjadi sangat bersemangat dan memberi saya User.CompanyUsers .

Jadi, saya memutuskan untuk menulis metode ekstensi agar properti dikecualikan dari objek (sebenarnya tidak termasuk itu hanya menyetel properti ke nol). Tidak hanya itu juga akan bekerja pada properti array. di bawah ini adalah kode saya juga akan mengekspor paket nuget untuk pengguna lain (tidak yakin apakah ini bahkan membantu seseorang). Alasannya sederhana karena saya terlalu malas untuk menulis. Pilih (n => baru {n.p1, n.p2}); Saya hanya tidak ingin menulis pernyataan pilih untuk mengecualikan hanya 1 properti!

Ini bukan kode terbaik (saya akan memperbarui pada tahap tertentu) karena saya telah menulis dengan tergesa-gesa dan meskipun ini dapat membantu seseorang yang ingin mengecualikan (set null) di objek dengan array juga.

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

kelas ekstensi di atas akan memberi Anda kemampuan untuk menyetel properti menjadi nol untuk menghindari array genap referensi diri.

Pembuat Ekspresi

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

Penggunaan:

Kelas Model

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

Data Dummy

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

                }
            }
        };

Kasus:

Kasus 1: Kecualikan hanya properti tanpa array apa pun

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

Kasus 2: Kecualikan properti dengan 1 larik

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

Kasus 3: Kecualikan properti dengan 2 array bersarang

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

Kasus 4: EF GetAll Query With Include

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

Anda mengetahui bahwa metode Explode () juga merupakan metode ekstensi hanya untuk pembuat ekspresi kami untuk mendapatkan properti dari properti array. Setiap kali ada properti array gunakan .Explode (). YourPropertyToExclude atau .Explode (). Property1.MyArrayProperty.Explode (). MyStupidProperty . Kode di atas membantu saya menghindari referensi-diri sedalam yang saya inginkan. Sekarang saya dapat menggunakan GetAll dan mengecualikan properti yang tidak saya inginkan!

Terima kasih telah membaca posting besar ini!

Adeel Rizvi
sumber
-1

Untuk tidak mengulang ini bekerja untuk saya
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,

Saya sudah menyelesaikan semuanya di sini - serialisasi Entity Framework anak-anak dengan .Net Core 2 WebAPI https://gist.github.com/Kaidanov/f9ad0d79238494432f32b8407942c606

Akan menghargai komentar apa pun. mungkin seseorang bisa menggunakannya kapan-kapan.

Tzvi Gregory Kaidanov
sumber
-1

Kode 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);
Lilach Rayzin
sumber
Ini pada dasarnya adalah panduan yang sama seperti yang ditawarkan dalam jawaban @ DalSoft yang bernilai tinggi dari delapan tahun yang lalu, tetapi dengan penjelasan yang jauh lebih sedikit.
Jeremy Caney
Berharap itu akan menyelesaikan masalah tetapi tolong tambahkan penjelasan kode Anda dengan itu sehingga pengguna akan mendapatkan pemahaman yang sempurna yang ia benar-benar inginkan.
Jaimil Patel
-2

Saya menyukai solusi yang melakukannya Application_Start()seperti pada jawaban di sini

Rupanya saya tidak bisa mengakses objek json di JavaScript menggunakan konfigurasi dalam fungsi saya seperti pada jawaban DalSoft karena objek yang dikembalikan memiliki "\ n \ r" di seluruh (kunci, val) objek.

Pokoknya apa pun yang berhasil (karena pendekatan yang berbeda bekerja dalam skenario yang berbeda berdasarkan komentar dan pertanyaan yang diajukan) meskipun cara standar untuk melakukannya akan lebih baik dengan beberapa dokumentasi yang baik yang mendukung pendekatan tersebut.

rey_coder
sumber