Использование 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
.
Ответы
Ответ 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);