Използвайки JSON.net, как да предотвратя сериализиране на свойства на производен клас, когато се използва в контекст на базов клас?

Даден модел на данни:

[DataContract]
public class Parent
{
    [DataMember]
    public IEnumerable<ChildId> Children { get; set; }
}

[DataContract]
public class ChildId
{
    [DataMember]
    public string Id { get; set; }
}

[DataContract]
public class ChildDetail : ChildId
{
    [DataMember]
    public string Name { get; set; }
}

От съображения за удобство на изпълнението, има моменти, когато ChildId обектите на Parent са всъщност ChildDetail обекти. Когато използвам JSON.net за сериализиране на Parent, те се записват с всички свойства на ChildDetail.

Има ли някакъв начин да инструктирам JSON.net (или който и да е друг сериализатор на JSON, не съм достатъчно далеч в проекта, за да се ангажирам с такъв) да игнорира свойствата на производния клас при сериализиране като базов клас?

РЕДАКТИРАНЕ: Важно е, когато сериализирам директно производния клас, да мога да създам всички свойства. Искам само да инхибирам полиморфизма в обекта Parent.


person Christopher Currie    schedule 03.05.2011    source източник


Отговори (6)


Използвам персонализиран Contract Resolver, за да огранича кои от моите свойства да сериализирам. Това може да ви насочи в правилната посока.

e.g.

/// <summary>
/// json.net serializes ALL properties of a class by default
/// this class will tell json.net to only serialize properties if they MATCH 
/// the list of valid columns passed through the querystring to criteria object
/// </summary>
public class CriteriaContractResolver<T> : DefaultContractResolver
{
    List<string> _properties;

    public CriteriaContractResolver(List<string> properties)
    {
        _properties = properties
    }

    protected override IList<JsonProperty> CreateProperties(
        JsonObjectContract contract)
    {
        IList<JsonProperty> filtered = new List<JsonProperty>();

        foreach (JsonProperty p in base.CreateProperties(contract))
            if(_properties.Contains(p.PropertyName)) 
                filtered.Add(p);

        return filtered;
    }
}

Във функцията за заместване IList можете да използвате отражение, за да попълните списъка само с родителските свойства може би.

Резолверът на договори се прилага към вашия сериализатор json.net. Този пример е от приложение asp.net mvc.

JsonNetResult result = new JsonNetResult();
result.Formatting = Formatting.Indented;
result.SerializerSettings.ContractResolver = 
    new CriteriaContractResolver<T>(Criteria);
person Jason Watts    schedule 03.05.2011
comment
Точно това търсех. Частичен отговор въз основа на полета, предадени в URI. Благодаря! [Чудя се защо това не е вграден инструмент за разрешаване на договори] - person David Kassa; 28.04.2012
comment
Имате ли пример за класа IRestCriteria‹T›? Откъде идва това? - person Micah; 12.06.2012
comment
Мика - Вероятно не трябваше просто да изрязвам и поставям. Оттогава промених този клас, за да приема списък‹низ› от имена на свойства. - person Jason Watts; 12.06.2012
comment
Не мисля, че трябва да извиквате base.CreateProperties, защото не извиквате новия метод за заместване, който сте написали (с приема само един параметър), така че трябваше да предадете 2 параметъра, вместо просто да сключите договор. - person Gui; 13.08.2012
comment
Това прави това, от което се нуждая - освен как мога да задам това поведение само за определени типове класове или да има различни набори от свойства, включени за различни типове? - person Rn222; 19.09.2012
comment
Това беше полезно!! Бих предложил да използвате HashSet вместо List, тъй като това ще направи if (_properties.Contains(p.PropertyName)) по-бързо. - person Lea Hayes; 08.05.2014
comment
Нещо интересно -- това не работи при наследяване от CamelCasePropertyNamesContractResolver. Този клас кешира резултатите от генерирането на свойство и така ще получавате едни и същи резултати всеки път. Наследяването от DefaultContractResolver (както по-горе) обаче работи добре. - person Rich; 07.01.2016
comment
@Rich Ако имате нужда от камилска кутия, можете просто да промените JsonProperty.PropertyName на вашата версия на камилска кутия. Много лесно за изпълнение сами - person fjch1997; 13.06.2017

Имах абсолютно същия проблем и потърсих как да изградя ContractResolver, който всъщност търсех, и това по-добре да отговори на този въпрос. Това само сериализира свойствата на Тип T, който всъщност искате да сериализирате, но с този пример можете лесно да изградите подобни подходи:

public class TypeOnlyContractResolver<T> : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);
        property.ShouldSerialize = instance => property.DeclaringType == typeof (T);
        return property;
    }
}
person Akku    schedule 03.05.2013
comment
Точно това, което търсих - имам базов клас, който съдържа някои свойства, които не искам да бъдат изложени. - person Edi; 25.05.2016
comment
Помислете за промяна на property.ShouldSerialize = instance => property.DeclaringType == typeof(T) || typeof(T).IsSubclassOf(property.DeclaringType);, ако класът, който се опитвате да сериализирате, е подклас на друг клас - person fjch1997; 13.06.2017

След като срещнах подобен проблем, това е ContractResolver, което измислих:

public class StrictTypeContractResolver : DefaultContractResolver
{
    private readonly Type _targetType;

    public StrictTypeContractResolver( Type targetType ) => _targetType = targetType;

    protected override IList<JsonProperty> CreateProperties( Type type, MemberSerialization memberSerialization )
        => base.CreateProperties
        (
            _targetType.IsAssignableFrom( type ) ? _targetType : type,
            memberSerialization
        );
}

Той отрязва само свойствата на наследниците на targetType, без да засяга свойствата на неговите базови класове или на други типове, които свойствата на targetType могат да препращат. Което, в зависимост от вашите нужди, може или не може да бъде подобрение спрямо другите отговори, предоставени тук по това време.

person HellBrick    schedule 17.05.2017
comment
По мое мнение най-доброто решение, тъй като работи и с реферирани обекти/вложени структури. - person martinoss; 24.11.2017

Вижте отговорите в тази подобна нишка, особено IgnorableSerializerContractResolver в моя отговор и по-хубавия ламбда версия

Употреба:

var jsonResolver = new IgnorableSerializerContractResolver();
// ignore single property
jsonResolver.Ignore(typeof(Company), "WebSites");
// ignore single datatype
jsonResolver.Ignore(typeof(System.Data.Objects.DataClasses.EntityObject));
var jsonSettings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, ContractResolver = jsonResolver };
person drzaus    schedule 05.06.2013

Не съм използвал специално JSON.Net, така че не е сигурно, че това ще ви помогне. Ако JSON.Net се извлича от системата за сериализация .Net, тогава трябва да можете да добавите атрибута [NonSerialized] към вашите свойства, които сега искате да бъдат сериализирани в основния клас. Когато извиквате методите за сериализиране на базовия клас, сериализирането трябва да пропусне тези елементи.

person Bueller    schedule 03.05.2011
comment
Малко по-различно от това, което искам, което е, че всички свойства на базовия клас се произвеждат и нито едно от свойствата на производния клас. Не искам да добавя [NonSeralized] към свойствата на производния клас, защото има моменти, в които искам производният клас да бъде сериализиран такъв, какъвто е, просто не когато се използва в контекст на базов клас. - person Christopher Currie; 03.05.2011
comment
Тези връзки може да помогнат. Изглежда, че ще трябва да извършите персонализирана сериализация. Статия на MSDN, Статия за CodeProject - person Bueller; 03.05.2011
comment
@Bueller Това не помага, ако класът, от който наследявате, е роден клас, който не можете да модифицирате. - person Brad Moore; 27.07.2014

Не съм сравнявал последиците за производителността, но това също е работещо решение и работи и с вложени/препратени обекти.

Derived d = new Derived();           
string jsonStringD = JsonConvert.SerializeObject(d);
Base b = new Base();
JsonConvert.PopulateObject(jsonStringD, b);
string jsonStringB = JsonConvert.SerializeObject(b);
person martinoss    schedule 24.11.2017