Bir özelliği json.net ile serileştirmeden serileştirmek ancak serileştirmemek


124

C # nesnelerini Json.net ile serileştirerek oluşturulmuş bazı yapılandırma dosyalarımız var.

Serileştirilmiş sınıfın bir özelliğini basit bir enum özelliği olmaktan çıkarıp bir sınıf özelliğine geçirmek istiyoruz.

Bunu yapmanın kolay bir yolu, eski enum özelliğini sınıfta bırakmak ve yapılandırmayı yüklediğimizde Json.net'in bu özelliği okumasını, ancak nesneyi bir sonraki serileştirdiğimizde tekrar kaydetmemesini sağlamaktır. Eski numaralandırmadan yeni sınıf oluşturmayı ayrı ayrı ele alacağız.

Bir C # nesnesinin bir özelliğini işaretlemenin (örneğin özniteliklerle) basit bir yolu var mı, böylece Json.net onu YALNIZCA serileştirirken yok sayacak, ancak seriyi kaldırırken ona katılacak mı?


Özel bir dönüştürücüye ne dersiniz: bunu mülkünüzde bir öznitelik olarak kullanabilir, ReadJson ve WriteJson'ı farklı bileşenlerle geçersiz kılabilirsiniz, hayır? Örnek (tam olarak ihtiyacınız olan şey değil, ama ...) weblogs.asp.net/thangchung/archive/2010/08/26/…
Raphaël Althaus

OnDeserialized niteliği sizin için geçici bir çözüm olabilir
Estefany Velez

"[JsonIgnore] 'özniteliğini kullanarak bu mümkün olmamalı mı ?? james.newtonking.com/archive/2009/10/23/…
Juri

Q'nun son paragrafına göre, bunun yalnızca bir yönde nasıl kullanılabileceğini genişletebilir misin?
Will Dean

[JsonIgnore] 'u, [JsonProperty] niteliği ile dekore edilmiş ikincil bir özel ayarlayıcı ile birlikte kullanmak mümkündür. Birkaç basit çözüm daha var. Ayrıntılı bir yazı ekledim.
Brian Rogers

Yanıtlar:


133

Aslında istediğiniz sonuca ulaşmak için kullanabileceğiniz oldukça basit birkaç yaklaşım vardır.

Örneğin, sınıflarınızın şu anda şu şekilde tanımlandığını varsayalım:

class Config
{
    public Fizz ObsoleteSetting { get; set; }
    public Bang ReplacementSetting { get; set; }
}

enum Fizz { Alpha, Beta, Gamma }

class Bang
{
    public string Value { get; set; }
}

Ve bunu yapmak istiyorsun:

string json = @"{ ""ObsoleteSetting"" : ""Gamma"" }";

// deserialize
Config config = JsonConvert.DeserializeObject<Config>(json);

// migrate
config.ReplacementSetting = 
    new Bang { Value = config.ObsoleteSetting.ToString() };

// serialize
json = JsonConvert.SerializeObject(config);
Console.WriteLine(json);

Bunu almak için:

{"ReplacementSetting":{"Value":"Gamma"}}

Yaklaşım 1: ShouldSerialize yöntemi ekleyin

Json.NET, ShouldSerializesınıfta karşılık gelen yöntemleri arayarak özellikleri koşullu olarak serileştirme yeteneğine sahiptir .

Bu özelliği kullanmak ShouldSerializeBlah()için, sınıfınıza Blah, serileştirmek istemediğiniz özelliğin adıyla değiştirilen bir boole yöntemi ekleyin . Bu yöntemin uygulanmasını her zaman geri döndürün false.

class Config
{
    public Fizz ObsoleteSetting { get; set; }

    public Bang ReplacementSetting { get; set; }

    public bool ShouldSerializeObsoleteSetting()
    {
        return false;
    }
}

Not: Bu yaklaşımı beğendiyseniz, ancak bir ShouldSerializeyöntem sunarak sınıfınızın genel arayüzünü bulanıklaştırmak IContractResolveristemiyorsanız, aynı şeyi programlı olarak yapmak için bir kullanabilirsiniz . Belgelerdeki Koşullu Özellik Serileştirme konusuna bakın .

Yaklaşım 2: JSON'u JObjects ile değiştirin

JsonConvert.SerializeObjectSerileştirmeyi yapmak için kullanmak yerine , config nesnesini bir içine yükleyin JObject, ardından yazmadan önce istenmeyen özelliği JSON'dan kaldırın. Sadece birkaç ekstra kod satırı.

JObject jo = JObject.FromObject(config);

// remove the "ObsoleteSetting" JProperty from its parent
jo["ObsoleteSetting"].Parent.Remove();

json = jo.ToString();

Yaklaşım 3: Özelliklerin akıllıca (ab) kullanımı

  1. [JsonIgnore]Serileştirilmesini istemediğiniz özelliğe bir öznitelik uygulayın .
  2. Orijinal özellik ile aynı türdeki sınıfa alternatif, özel bir özellik ayarlayıcı ekleyin . Bu özelliğin uygulanmasının orijinal özelliği ayarlamasını sağlayın.
  3. [JsonProperty]Alternatif ayarlayıcıya orijinal özellik ile aynı JSON adını vererek bir öznitelik uygulayın .

İşte revize edilmiş Configsınıf:

class Config
{
    [JsonIgnore]
    public Fizz ObsoleteSetting { get; set; }

    [JsonProperty("ObsoleteSetting")]
    private Fizz ObsoleteSettingAlternateSetter
    {
        // get is intentionally omitted here
        set { ObsoleteSetting = value; }
    }

    public Bang ReplacementSetting { get; set; }
}

7
Get-properties'i dahili olarak ayarlayarak (üst sınıf özelliklerinin hiçbirinin serileştirilmemesi gereken, temel modelin dahili bütünleşmeye özgü süper kümesini kullanan) projemizde çözdük. Ortak ayarlayıcılara sahip olmak, Web Api'nin özellikleri ayarlamasına izin verdi, ancak bunları serileştirmesini durdurdu.
Daniel Saidi

7
Kullanılarak ile birlikte JsonPropertyAttributeC # 6.0, kullanabilirsiniz nameofyerine "sihirli dizeleri" kullanmanın anahtar kelime. Bu, yeniden düzenlemeyi çok daha kolay ve hatasız hale getirir - ayrıca, herhangi bir olayı yeniden adlandırmayı kaçırırsanız, derleyici sizi yine de uyaracaktır. @ Brian'ın örneğini kullanarak, kullanım şu olacaktır:[JsonProperty(nameof(ObsoleteSetting))]
Geoff James

1
JsonProperty bildirimlerinde, özellikle bu eski senaryoda nameof () kullanmak kötü bir fikirdir. JSON, başka bir arabirimle harici (ve umarız ebedi) bir sözleşmeyi temsil eder ve yeniden düzenleme yaparsanız JSON özelliğinin adını kesinlikle değiştirmek istemezsiniz. Bu formatta JSON oluşturan tüm mevcut JSON dosyalarının ve bileşenlerinin uyumluluğunu bozarsınız. Aslında, özelliği daha sonra yeniden adlandırırsanız değişmediklerinden emin olmak için her serileştirilmiş özelliğe tam adı JsonProperty (…) koymanız daha iyi olur.
Cephane Goettsch

36

Yalnızca serileştirme özelliğinizin dahili olarak işaretlenmesinin kabul edilebilir olduğu her durumda, özniteliklere hiç bağlı olmayan oldukça basit bir çözüm vardır. Özelliği dahili alma olarak işaretlemeniz yeterlidir, ancak genel küme:

public class JsonTest {

    public string SomeProperty { internal get; set; }

}

Bu, varsayılan ayarlar / çözücüler / vb. Kullanılarak doğru serileştirme ile sonuçlanır, ancak özellik serileştirilmiş çıktıdan çıkarılır.


Basit ama akıllı çözüm.
hbulens

Özelliğin doğrulama modülü tarafından da göz ardı edileceğini unutmayın. (Artık seri durumdan çıkarma için [Gerekli] olarak işaretleyemezsiniz, çünkü bu genel bir getyönteme dayalıdır ).
Martin Hansen

2
Bu internalne ile çalışmaz private. Her zaman serileştirilir.
Paul

Bu benim için işe yaramadı. Seri durumdan çıkarılırken özellik bulunamadı hatası var.
Drew Sumido

31

Bu özellikte özniteliklere bağlı kalmaktan hoşlanıyorum, işte bir özelliği serileştirmeye ihtiyaç duyduğumda ancak onu serileştirmemeye ihtiyaç duyduğumda veya tam tersi olduğunda kullandığım yöntem.

1. ADIM - Özel özelliği oluşturun

public class JsonIgnoreSerializationAttribute : Attribute { }

ADIM 2 - Özel bir Sözleşme Yeniden Toplama Oluşturun

class JsonPropertiesResolver : DefaultContractResolver
{
    protected override List<MemberInfo> GetSerializableMembers(Type objectType)
    {
        //Return properties that do NOT have the JsonIgnoreSerializationAttribute
        return objectType.GetProperties()
                         .Where(pi => !Attribute.IsDefined(pi, typeof(JsonIgnoreSerializationAttribute)))
                         .ToList<MemberInfo>();
    }
}

ADIM 3 - Serileştirmenin gerekli olmadığı, ancak serileştirmenin kaldırıldığı yerlerde öznitelik ekleyin

    [JsonIgnoreSerialization]
    public string Prop1 { get; set; } //Will be skipped when serialized

    [JsonIgnoreSerialization]
    public string Prop2 { get; set; } //Also will be skipped when serialized

    public string Prop3 { get; set; } //Will not be skipped when serialized

ADIM 4 - Kullanın

var sweet = JsonConvert.SerializeObject(myObj, new JsonSerializerSettings { ContractResolver = new JsonPropertiesResolver() });

Bu yardımcı olur umarım! Ayrıca, seriyi kaldırma gerçekleştiğinde bu özelliklerin de yok sayılacağını belirtmekte fayda var, ders serisini kaldırırken dönüştürücüyü sadece geleneksel şekilde kullanıyorum.

JsonConvert.DeserializeObject<MyType>(myString);

Bu faydalı uygulama için teşekkürler. Temel uygulamasını GetSerializableMemberstamamen geçersiz kılmak yerine genişletmenin bir yolu var mı ?
Alain

4
Boşver, bunun şu kadar basit olduğunu fark ettim:return base.GetSerializableMembers(objectType).Where(pi => !Attribute.IsDefined(pi, typeof(JsonIgnoreSerializationAttribute))).ToList();
Alain

Bunun neden en çok oy alan cevap olmadığından emin değilim. temiz, newtonsoft'un kalıplarını takip eder ve uygulaması kolaydır. Ekleyeceğim tek şey, bunu kullanarak global olarak kurabilmenizdirJsonConvert.DefaultSettings = () => new JsonSerializerSettings { ContractResolver = new JsonPropertiesResolver() }
Matt M

1
boşver, bu aslında soruyu soranın istediği şeyi yapmaz. bu temelde JsonIgnore'u yeniden yaratmaktır. Bu, serileştirme için özelliği atlamaz, ancak seriyi kaldırma sırasında özelliği ayarlar çünkü GetSerializableMembers yönteminin bir okuma veya yazma olup olmadığını bilmesinin bir yolu olmadığından, her ikisinin özelliklerini de hariç tutarsınız. bu çözüm işe yaramıyor.
Matt M

7

Ayarlayıcı özelliğini kullanın:

[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { _ignoreOnSerializing = value; } }

[JsonIgnore]
private string _ignoreOnSerializing;

[JsonIgnore]
public string IgnoreOnSerializing
{
    get { return this._ignoreOnSerializing; }
    set { this._ignoreOnSerializing = value; }
}

Umarım bu yardımcı olur.


1
Teşekkürler. JsonProperty öğesinin özelliğe IgnoreOnSerializingeşit bir büyük harfe sahip olması gerektiğini unutmayın . nameof(IgnoreOnSerializing)Yeniden adlandırma durumunda sihirli dizeden kaçınmak için kullanmanızı öneririm .
Bendik August Nesbø

5

Oldukça uzun bir süre bir sınıf özelliğinin Serileştirilebilir ve Serileştirilemez olarak nasıl işaretleneceğini araştırdıktan sonra, bunu yapacak böyle bir şeyin olmadığını fark ettim; bu yüzden iki farklı kitaplığı veya serileştirme tekniğini (System.Runtime.Serialization.Json & Newtonsoft.Json) birleştiren bir çözüm buldum ve benim için aşağıdaki gibi çalıştı:

  • tüm sınıflarınızı ve alt sınıflarınızı "DataContract" olarak işaretleyin.
  • sınıfınızın ve alt sınıflarınızın tüm özelliklerini "DataMember" olarak işaretleyin.
  • Sınıfınızın ve alt sınıflarınızın tüm özelliklerini, serileştirilmesini istemedikleriniz dışında "JsonProperty" olarak işaretleyin.
  • şimdi, "JsonIgnore" olarak serileştirilmesini İSTEMEDİĞİNİZ özellikleri işaretleyin.
  • daha sonra "Newtonsoft.Json.JsonConvert.SerializeObject" kullanarak Seri hale getirin ve "System.Runtime.Serialization.Json.DataContractJsonSerializer" kullanarak Serialize yapın.

    using System;
    using System.Collections.Generic;
    using Newtonsoft.Json;
    using System.Runtime.Serialization;
    using System.IO;
    using System.Runtime.Serialization.Json;
    using System.Text;
    
    namespace LUM_Win.model
    {
        [DataContract]
        public class User
        {
            public User() { }
            public User(String JSONObject)
            {
                MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(JSONObject));
                DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(User));
    
                User user = (User)dataContractJsonSerializer.ReadObject(stream);
                this.ID = user.ID;
                this.Country = user.Country;
                this.FirstName = user.FirstName;
                this.LastName = user.LastName;
                this.Nickname = user.Nickname;
                this.PhoneNumber = user.PhoneNumber;
                this.DisplayPicture = user.DisplayPicture;
                this.IsRegistred = user.IsRegistred;
                this.IsConfirmed = user.IsConfirmed;
                this.VerificationCode = user.VerificationCode;
                this.Meetings = user.Meetings;
            }
    
            [DataMember(Name = "_id")]
            [JsonProperty(PropertyName = "_id")]
            public String ID { get; set; }
    
            [DataMember(Name = "country")]
            [JsonProperty(PropertyName = "country")]
            public String Country { get; set; }
    
            [DataMember(Name = "firstname")]
            [JsonProperty(PropertyName = "firstname")]
            public String FirstName { get; set; }
    
            [DataMember(Name = "lastname")]
            [JsonProperty(PropertyName = "lastname")]
            public String LastName { get; set; }
    
            [DataMember(Name = "nickname")]
            [JsonProperty(PropertyName = "nickname")]
            public String Nickname { get; set; }
    
            [DataMember(Name = "number")]
            [JsonProperty(PropertyName = "number")]
            public String PhoneNumber { get; set; }
    
            [DataMember(Name = "thumbnail")]
            [JsonProperty(PropertyName = "thumbnail")]
            public String DisplayPicture { get; set; }
    
            [DataMember(Name = "registered")]
            [JsonProperty(PropertyName = "registered")]
            public bool IsRegistred { get; set; }
    
            [DataMember(Name = "confirmed")]
            [JsonProperty(PropertyName = "confirmed")]
            public bool IsConfirmed { get; set; }
    
            [JsonIgnore]
            [DataMember(Name = "verification_code")]
            public String VerificationCode { get; set; }
    
            [JsonIgnore]
            [DataMember(Name = "meeting_ids")]
            public List<Meeting> Meetings { get; set; }
    
            public String toJSONString()
            {
                return JsonConvert.SerializeObject(this, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
            }
        }
    }

Umarım yardımcı olur ...


1
Bravo Ahmed Abulazm. teşekkürler beni birçok işten kurtardı. :)
Sike12

2

@ ThoHo'nun çözümüne referansla, ek etiket olmadan ayarlayıcıyı kullanmak aslında gereken tek şeydir.

Benim için daha önce, yüklemek ve yeni referans kimlikleri koleksiyonuna eklemek istediğim tek bir referans kimliğim vardı. Referans kimliğinin tanımını yalnızca yeni koleksiyona değer katan bir ayarlayıcı yöntemi içerecek şekilde değiştirerek. Özelliğin bir getiri yoksa Json değeri geri yazamaz ; yöntem.

// Old property that I want to read from Json, but never write again. No getter.
public Guid RefId { set { RefIds.Add(value); } }

// New property that will be in use from now on. Both setter and getter.
public ICollection<Guid> RefIds { get; set; }

Bu sınıf artık önceki sürümle geriye doğru uyumludur ve yalnızca yeni sürümler için RefIds'leri kaydeder .


1

Tho Ho'nun cevabına dayanmak için, bu alanlar için de kullanılabilir.

[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { IgnoreOnSerializing = value; } }

[JsonIgnore]
public string IgnoreOnSerializing;

1

Bunun uygulamada nerede gerçekleştiğine bağlı olarak ve yalnızca bir özellik ise, bunu manuel olarak yapmanın bir yolu, özellik değerini null olarak ayarlamaktır ve ardından modelde, değer null ise özelliğin yok sayılacağını belirtebilirsiniz:

[JsonProperty(NullValueHandling = NullValue.Ignore)]
public string MyProperty { get; set; }

Bir ASP.NET Core web uygulaması üzerinde çalışıyorsanız, bunu Startup.cs dosyanızda ayarlayarak bunu tüm modellerdeki tüm özellikler için genel olarak ayarlayabilirsiniz:

public void ConfigureServices(IServiceCollection services) {
    // other configuration here
    services.AddMvc()
        .AddJsonOptions(options => options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore);
}

0

JsonConvert kullanıyorsanız, IgnoreDataMemberAttribute tamam.Standart kitaplığım Newton.Json'a başvurmuyor ve nesne serileştirmeyi kontrol etmek için [IgnoreDataMember] kullanıyorum.

Gönderen Newton.net yardım belgesinde.


0

Bir C # nesnesinin bir özelliğini işaretlemenin (örneğin özniteliklerle) basit bir yolu var mı, böylece Json.net onu YALNIZCA serileştirirken yok sayacak, ancak seriyi kaldırırken ona katılacak mı?

Bu yazıda bulduğum en kolay yol, bu mantığı IContractResolver'ınıza dahil etmektir .

Gelecek nesil için buraya kopyalanmış yukarıdaki bağlantıdan örnek kod:

public class Employee
{
    public string Name { get; set; }
    public Employee Manager { get; set; }

    public bool ShouldSerializeManager()
    {
        // don't serialize the Manager property if an employee is their own manager
        return (Manager != this);
    }
}

public class ShouldSerializeContractResolver : DefaultContractResolver
{
    public new static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        if (property.DeclaringType == typeof(Employee) && property.PropertyName == "Manager")
        {
            property.ShouldSerialize =
                instance =>
                {
                    Employee e = (Employee)instance;
                    return e.Manager != e;
                };
        }

        return property;
    }
}

Tüm cevaplar güzel ama bu yaklaşım en temiz yol gibi görünüyordu. Aslında bunu SkipSerialize ve SkipDeserialize özelliğinde bir öznitelik arayarak uyguladım, böylece kontrol ettiğiniz herhangi bir sınıfı işaretleyebilirsiniz. Harika soru!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.