Özelliklerin serisini kaldırmak için Json.NET dönüştürücülerini kullanma


88

Arabirim döndüren bir özellik içeren bir sınıf tanımım var.

public class Foo
{ 
    public int Number { get; set; }

    public ISomething Thing { get; set; }
}

Json.NET kullanarak Foo sınıfını serileştirmeye çalışmak bana "ISomething" türünde bir örnek oluşturulamadı. ISomething bir arayüz veya soyut sınıf olabilir. "Gibi bir hata mesajı veriyor.

Seri durumdan çıkarma Somethingsırasında kullanılacak somut bir sınıf belirtmeme izin veren bir Json.NET özniteliği veya dönüştürücü var mı?


Bir şey alan / ayarlayan bir özellik adı belirtmeniz gerektiğine inanıyorum
ram

Sahibim. C # 3.5'te tanıtılan otomatik uygulanan özellikler için steno kullanıyorum. msdn.microsoft.com/en-us/library/bb384054.aspx
dthrasher

4
Tipik bir şey değil. Bence ram haklı, hala bir mülk adına ihtiyacınız var. Bunun sorununuzla ilgili olmadığını biliyorum, ancak yukarıdaki yorumunuz, .NET'te isimsiz bir özellik belirlemenize izin veren bazı yeni özellikleri kaçırdığımı düşündürdü.
Bay Moose

Yanıtlar:


92

Json.NET ile yapabileceğiniz şeylerden biri şudur:

var settings = new JsonSerializerSettings();
settings.TypeNameHandling = TypeNameHandling.Objects;

JsonConvert.SerializeObject(entity, Formatting.Indented, settings);

TypeNameHandlingBayrak katacak $typeJson.NET bunun içine nesneyi serisini ihtiyacı somut hangi tip bilmesini sağlar JSON'dan Mülkü. Bu, bir arabirimi veya soyut temel sınıfı yerine getirirken bir nesnenin serisini kaldırmanıza olanak tanır.

Ancak dezavantajı, bunun çok Json.NET'e özgü olmasıdır. $typeEğer tip bilgi ile seri konum eğer öyleyse, tam olarak nitelenmiş tür olacaktır ,, deserializer ihtiyaçları yanı anlamak mümkün.

Belgeler: Json.NET ile Serileştirme Ayarları


İlginç. Bununla uğraşmam gerekecek. Güzel ipucu!
dthrasher

2
Newtonsoft.Json için benzer çalışır, ancak özellik "$ type"
Jaap

Bu çok kolaydı!
Shimmy Weitzhandler

1
Kullanırken buradaki olası güvenlik sorunlarına dikkat edin TypeNameHandling. Ayrıntılar için Newtonsoft Json'daki TypeNameHandling uyarısına bakın.
dbc

Dün dönüştürücülerle çılgınca mücadele ettim ve bu çok daha iyi ve daha anlaşılır oldu, teşekkürler !!!
Horothenic

52

Bunu JsonConverter sınıfını kullanarak başarabilirsiniz. Arabirim özelliğine sahip bir sınıfınız olduğunu varsayalım;

public class Organisation {
  public string Name { get; set; }

  [JsonConverter(typeof(TycoonConverter))]
  public IPerson Owner { get; set; }
}

public interface IPerson {
  string Name { get; set; }
}

public class Tycoon : IPerson {
  public string Name { get; set; }
}

JsonConverter'ınız, temel alınan özelliği serileştirmekten ve serileştirmeden sorumludur;

public class TycoonConverter : JsonConverter
{
  public override bool CanConvert(Type objectType)
  {
    return (objectType == typeof(IPerson));
  }

  public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
  {
    return serializer.Deserialize<Tycoon>(reader);
  }

  public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
  {
    // Left as an exercise to the reader :)
    throw new NotImplementedException();
  }
}

Json.Net aracılığıyla serileştirilmiş bir Kuruluşla çalışırken, Owner özelliğinin temelindeki IPerson Tycoon türünde olacaktır.


Çok hoş. Dönüştürücüyü bir denemem gerekecek.
dthrasher

4
"[JsonConverter (typeof (TycoonConverter))]" etiketi, arayüzün bir listesindeyse yine de çalışır mıydı?
Zwik

40

Daha önce belirtildiği gibi, özelleştirilmiş bir JsonSerializerSettings nesnesini TypeNameHandling.Objects seçeneğiyle JsonConvert.SerializeObject () 'e iletmek yerine, bu belirli arabirim özelliğini bir öznitelikle işaretleyebilirsiniz, böylece oluşturulan JSON "$ type" özellikleriyle şişirilmez HER nesnede:

public class Foo
{
    public int Number { get; set; }

    // Add "$type" property containing type info of concrete class.
    [JsonProperty( TypeNameHandling = TypeNameHandling.Objects )]
    public ISomething { get; set; }
}

Parlak. Teşekkürler :)
Darren Young

5
Arayüz veya soyut sınıf koleksiyonları için özellik "ItemTypeNameHandling" dir. örneğin: [JsonProperty (ItemTypeNameHandling = TypeNameHandling.Auto)]
Anthony F

Bunun için teşekkür ederim!
brudert

24

Üçüncü taraf Newtonsoft Json dönüştürücüsünün en son sürümünde, arabirim özelliğiyle ilgili somut tipte bir kurucu ayarlayabilirsiniz.

public class Foo
{ 
    public int Number { get; private set; }

    public ISomething IsSomething { get; private set; }

    public Foo(int number, Something concreteType)
    {
        Number = number;
        IsSomething = concreteType;
    }
}

Bir şey ISomething uyguladığı sürece bu çalışmalıdır. Ayrıca, JSon dönüştürücünün bunu kullanmaya çalışması durumunda varsayılan boş bir kurucu koymayın, somut türü içeren yapıcıyı kullanmaya zorlamalısınız.

PS. bu aynı zamanda ayarlayıcılarınızı özel yapmanıza da olanak tanır.


6
Bu çatılardan haykırılmalı! Doğru, somut uygulamaya kısıtlamalar ekler, ancak kullanılabileceği durumlar için diğer yaklaşımlardan çok daha basittir.
Mark Meuer

3
Ya birden fazla beton türü olan 1'den fazla kurucumuz varsa, yine de bilecek mi?
Teoman shipahi

1
Bu cevap, aksi takdirde yapmak zorunda kalacağın tüm kıvrımlı saçmalıklara kıyasla çok zarif. Kabul edilen cevap bu olmalıdır. Yine de benim durumumda bir uyarı, çalışabilmesi için [JsonConstructor] 'ı kurucudan önce eklemem gerektiğiydi ... Bunu beton kurucularınızdan yalnızca BİR tanesinde kullanmanın (4 yaşındaki) probleminizi çözeceğinden şüpheleniyorum @Teomanshipahi
nacitar sevaht

@nacitarsevaht Şimdi geri dönüp sorunumu çözebilirim :) neyse ne olduğunu bile hatırlamıyorum, ama tekrar baktığımda bu bazı durumlar için iyi bir çözüm.
Teoman shipahi

bunu da kullanıyoruz, ancak çoğu durumda dönüştürmeyi tercih ediyorum çünkü beton tipini kurucuya bağlamak, mülk için bir arayüz kullanma amacını ilk etapta bozar!
gabe

19

Aynı sorunla karşılaştım, bu yüzden bilinen türler argümanını kullanan kendi Dönüştürücümü buldum.

public class JsonKnownTypeConverter : JsonConverter
{
    public IEnumerable<Type> KnownTypes { get; set; }

    public JsonKnownTypeConverter(IEnumerable<Type> knownTypes)
    {
        KnownTypes = knownTypes;
    }

    protected object Create(Type objectType, JObject jObject)
    {
        if (jObject["$type"] != null)
        {
            string typeName = jObject["$type"].ToString();
            return Activator.CreateInstance(KnownTypes.First(x =>typeName.Contains("."+x.Name+",")));
        }

        throw new InvalidOperationException("No supported type");
    }

    public override bool CanConvert(Type objectType)
    {
        if (KnownTypes == null)
            return false;

        return (objectType.IsInterface || objectType.IsAbstract) && KnownTypes.Any(objectType.IsAssignableFrom);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // Load JObject from stream
        JObject jObject = JObject.Load(reader);
        // Create target object based on JObject
        var target = Create(objectType, jObject);
        // Populate the object properties
        serializer.Populate(jObject.CreateReader(), target);
        return target;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Seri durumdan çıkarmak ve serileştirmek için iki uzantı yöntemi tanımladım:

public static class AltiJsonSerializer
{
    public static T DeserializeJson<T>(this string jsonString, IEnumerable<Type> knownTypes = null)
    {
        if (string.IsNullOrEmpty(jsonString))
            return default(T);

        return JsonConvert.DeserializeObject<T>(jsonString,
                new JsonSerializerSettings
                {
                    TypeNameHandling = TypeNameHandling.Auto, 
                    Converters = new List<JsonConverter>
                        (
                            new JsonConverter[]
                            {
                                new JsonKnownTypeConverter(knownTypes)
                            }
                        )
                }
            );
    }

    public static string SerializeJson(this object objectToSerialize)
    {
        return JsonConvert.SerializeObject(objectToSerialize, Formatting.Indented,
        new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto});
    }
}

Dönüştürmelerdeki türleri karşılaştırmak ve tanımlamak için kendi yönteminizi tanımlayabilirsiniz, yalnızca sınıf adını kullanıyorum.


1
Bu JsonConverter harika, kullandım ama bu şekilde çözdüğüm birkaç sorunla karşılaştım: - Doldurmak yerine JsonSerializer.CreateDefault () kullanarak, çünkü nesnemin daha derin bir hiyerarşisi vardı. - Oluşturucuyu geri almak ve Create () yönteminde Instanciate için yansımayı kullanma
Aurel

3

Normalde çözümü her zaman TypeNameHandlingDanielT tarafından önerildiği gibi kullandım, ancak buradaki durumlarda gelen JSON üzerinde kontrolüm yok (ve bu nedenle bir $typeözellik içerdiğinden emin olamıyorum ) Sadece açıkça belirtmenize izin veren özel bir dönüştürücü yazdım beton türü:

public class Model
{
    [JsonConverter(typeof(ConcreteTypeConverter<Something>))]
    public ISomething TheThing { get; set; }
}

Bu yalnızca somut türü açıkça belirtirken Json.Net'teki varsayılan serileştirici uygulamasını kullanır.

Kaynak kodu ve bir genel bakış bu blog gönderisinde mevcuttur .


1
Bu harika bir çözüm. Şerefe.
JohnMetta

2

@Daniel T.'nin bize yukarıda gösterdiği örneği tamamlamak istedim:

Nesnenizi serileştirmek için bu kodu kullanıyorsanız:

var settings = new JsonSerializerSettings();
settings.TypeNameHandling = TypeNameHandling.Objects;
JsonConvert.SerializeObject(entity, Formatting.Indented, settings);

JSON'un serisini kaldıracak kod şu şekilde görünmelidir:

var settings = new JsonSerializerSettings(); 
settings.TypeNameHandling = TypeNameHandling.Objects;
var entity = JsonConvert.DeserializeObject<EntityType>(json, settings);

Bu, TypeNameHandlingbayrağı kullanırken bir json'a nasıl uyum sağladığını gösterir :görüntü açıklamasını buraya girin


-5

Ben de aynı şeyi merak ettim ama korkarım bu yapılamaz.

Buna bu şekilde bakalım. JSon.net'e bir veri dizesi ve seriyi kaldırılacak bir tür verirsiniz. JSON.net bu ISomething'e çarptığında ne yapacak? Yeni bir ISomething türü yaratamaz çünkü ISomething bir nesne değildir. Ayrıca ISomething'i uygulayan bir nesne yaratamaz, çünkü ISomething'i miras alabilecek birçok nesneden hangisini kullanması gerektiğine dair bir ipucu yok. Arayüzler, otomatik olarak serileştirilebilen, ancak otomatik olarak serileştirilemeyen bir şeydir.

Yapacağım şey, ISomething'i temel sınıfla değiştirmeye bakmak olurdu. Bunu kullanarak aradığınız etkiyi elde edebilirsiniz.


1
"Kutudan çıktığı gibi" çalışmayacağının farkındayım. Ama somut bir sınıf sağlamak için kullanabileceğim "[JsonProperty (typeof (SomethingBase))]" gibi bazı nitelikler olup olmadığını merak ediyordum.
dthrasher

Öyleyse neden yukarıdaki kodda ISomething yerine SomethingBase kullanılmasın? Buna yanlış bir şekilde baktığımız da iddia edilebilir, çünkü arayüzler serileştirmede kullanılmamalıdır, çünkü bunlar belirli bir sınıfla iletişim "arayüzünü" basitçe tanımlarlar. Bir arabirimi teknik olarak serileştirmek, soyut bir sınıfı serileştirmek gibi saçmadır. Yani "yapılabilir" olsa da, "yapılmaması gerektiğini" iddia ediyorum.
Timothy Baldridge

Newtonsoft.Json.Serialization Ad Alanındaki sınıflardan herhangi birine baktınız mı? özellikle JsonObjectContract sınıfı?
johnny

-9

İşte ScottGu tarafından yazılmış bir makaleye referans .

Buna dayanarak, yardımcı olabileceğini düşündüğüm bazı kodlar yazdım

public interface IEducationalInstitute
{
    string Name
    {
        get; set;
    }

}

public class School : IEducationalInstitute
{
    private string name;
    #region IEducationalInstitute Members

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    #endregion
}

public class Student 
{
    public IEducationalInstitute LocalSchool { get; set; }

    public int ID { get; set; }
}

public static class JSONHelper
{
    public static string ToJSON(this object obj)
    {
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        return serializer.Serialize(obj);
    }
    public  static string ToJSON(this object obj, int depth)
    {
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        serializer.RecursionLimit = depth;
        return serializer.Serialize(obj);
    }
}

Ve bu nasıl adlandırılır

School myFavSchool = new School() { Name = "JFK High School" };
Student sam = new Student()
{
    ID = 1,
    LocalSchool = myFavSchool
};
string jSONstring = sam.ToJSON();

Console.WriteLine(jSONstring);
//Result {"LocalSchool":{"Name":"JFK High School"},"ID":1}

Doğru anladıysam, JSON serileştirmesi için arabirimi uygulayan somut bir sınıf belirtmeniz gerektiğini düşünmüyorum.


1
Örneğiniz, .NET Framework'teki bir sınıf olan JavaScriptSerializer'ı kullanıyor. Serileştiricim olarak Json.NET kullanıyorum. codeplex.com/Json
dthrasher

3
Orijinal soruya atıfta bulunmaz, Json.NET burada açıkça belirtilmiştir.
Oliver
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.