Подтвердить что ты не робот

Использование 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-сериализатор, я недостаточно далеко, чтобы проект был привязан к нему), чтобы игнорировать свойства производного класса при сериализации в качестве базового класса?

EDIT: Важно, что когда я сериализую производный класс напрямую, я могу произвести все свойства. Я хочу только запретить полиморфизм в объекте Parent.

4b9b3361

Ответ 1

Я использую пользовательский Контрактный резолвер, чтобы ограничить, какие из моих свойств будут сериализованы. Это может указывать на правильное направление.

например.

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

Ответ 2

У меня была такая же проблема, и я искал, как построить 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;
    }
}

Ответ 3

Просмотрите ответы в этом подобном потоке, в частности 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 };

Ответ 4

Я не использовал JSON.Net, в частности, так что не положительно, это поможет вам. Если JSON.Net получается из системы сериализации .Net, вы должны иметь возможность добавить атрибут [NonSerialized] к вашим свойствам, которые вы теперь хотите сериализовать в базовом классе. Когда вы вызываете методы сериализации в базовом классе, сериализация должна пропускать эти элементы.

Ответ 5

Если вы столкнулись с подобной проблемой, это 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. Что, в зависимости от ваших потребностей, может быть или не быть улучшением по сравнению с другими ответами, предоставленными здесь в то время.

Ответ 6

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

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