Ответ 1
Вы можете использовать условную сериализацию. Посмотрите на ссылку . В основном вам нужно реализовать интерфейс IContractResolver
, перегрузить метод ShouldSerialize
и передать свой резольвер в конструктор Json Serializer.
С простым классом/интерфейсом, подобным этому
public interface IThing
{
string Name { get; set; }
}
public class Thing : IThing
{
public int Id { get; set; }
public string Name { get; set; }
}
Как я могу получить строку JSON только с свойством "Name" (только свойства базового интерфейса)?
Собственно, когда я делаю это:
var serialized = JsonConvert.SerializeObject((IThing)theObjToSerialize, Formatting.Indented);
Console.WriteLine(serialized);
Я получаю полный объект как JSON (Id + Name);
Спасибо заранее!
Вы можете использовать условную сериализацию. Посмотрите на ссылку . В основном вам нужно реализовать интерфейс IContractResolver
, перегрузить метод ShouldSerialize
и передать свой резольвер в конструктор Json Serializer.
Метод, который я использую,
public class InterfaceContractResolver : DefaultContractResolver
{
private readonly Type _InterfaceType;
public InterfaceContractResolver (Type InterfaceType)
{
_InterfaceType = InterfaceType;
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
//IList<JsonProperty> properties = base.CreateProperties(type, memberSerialization);
IList<JsonProperty> properties = base.CreateProperties(_InterfaceType, memberSerialization);
return properties;
}
}
// To serialize do this:
var settings = new JsonSerializerSettings() {
ContractResolver = new InterfaceContractResolver (typeof(IThing))
});
string json = JsonConvert.SerializeObject(theObjToSerialize, settings);
Вдохновленный @user3161686, здесь небольшая модификация InterfaceContractResolver
:
public class InterfaceContractResolver<TInterface> : DefaultContractResolver where TInterface : class
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
IList<JsonProperty> properties = base.CreateProperties(typeof(TInterface), memberSerialization);
return properties;
}
}
Вы можете добавить аннотацию [JsonIgnore]
, чтобы игнорировать атрибут.
Альтернативой [JsonIgnore]
являются [DataContract]
и [DataMember]
. Если ваш класс помечен тегом [DataContract]
, сериализатор будет обрабатывать только теги с атрибутом [DataMember]
(JsonIgnore
- это модель отказа, а DataContract
- "op-in" ).
[DataContract]
public class Thing : IThing
{
[DataMember]
public int Id { get; set; }
public string Name { get; set; }
}
Ограничение обоих подходов состоит в том, что они должны быть реализованы в классе, вы не можете добавить их в определение интерфейса.
Еще одна вариация здесь. Код пришел из http://www.tomdupont.net/2015/09/how-to-only-serialize-interface.html со следующими улучшениями по сравнению с другими ответами здесь
Interface2[]
внутри Interface1
, тогда она будет сериализована.Я пытался сериализовать прокси-объект WCF, и полученный JSON появился как {}
. Оказалось, что все свойства были установлены на Ignore=true
, поэтому мне пришлось добавить цикл, чтобы все они не игнорировались.
public class InterfaceContractResolver : DefaultContractResolver
{
private readonly Type[] _interfaceTypes;
private readonly ConcurrentDictionary<Type, Type> _typeToSerializeMap;
public InterfaceContractResolver(params Type[] interfaceTypes)
{
_interfaceTypes = interfaceTypes;
_typeToSerializeMap = new ConcurrentDictionary<Type, Type>();
}
protected override IList<JsonProperty> CreateProperties(
Type type,
MemberSerialization memberSerialization)
{
var typeToSerialize = _typeToSerializeMap.GetOrAdd(
type,
t => _interfaceTypes.FirstOrDefault(
it => it.IsAssignableFrom(t)) ?? t);
var props = base.CreateProperties(typeToSerialize, memberSerialization);
// mark all props as not ignored
foreach (var prop in props)
{
prop.Ignored = false;
}
return props;
}
}
в дополнение к ответу, указанному @monrow, вы можете использовать стандартные [DataContract] и [DataMember] взгляните на это
Наконец-то я получил, когда это не сработает... Если вы хотите иметь внутри другого сложного объекта, он не будет правильно сериализован.
Итак, я сделал версию, которая будет извлекать только данные, хранящиеся в конкретной сборке, и для типов, которые имеют один и тот же базовый интерфейс.
Итак, он сделан как .Net Core JsonContractResolver.
В дополнение к извлечению данных он решает:
a) преобразование camelCase перед отправкой данных клиенту
б) использует самый верхний интерфейс из разрешенной области (путем сборки)
c) исправляет порядок полей: поле из большинства базовых классов будет указано первым, а вложенный объект также будет соответствовать этому правилу.
public class OutputJsonResolver : DefaultContractResolver
{
#region Static Members
private static readonly object syncTargets = new object();
private static readonly Dictionary<Type, IList<JsonProperty>> Targets = new Dictionary<Type, IList<JsonProperty>>();
private static readonly Assembly CommonAssembly = typeof(ICommon).Assembly;
#endregion
#region Override Members
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
if (type.Assembly != OutputJsonResolver.CommonAssembly)
return base.CreateProperties(type, memberSerialization);
IList<JsonProperty> properties;
if (OutputJsonResolver.Targets.TryGetValue(type, out properties) == false)
{
lock (OutputJsonResolver.syncTargets)
{
if (OutputJsonResolver.Targets.ContainsKey(type) == false)
{
properties = this.CreateCustomProperties(type, memberSerialization);
OutputJsonResolver.Targets[type] = properties;
}
}
}
return properties;
}
protected override string ResolvePropertyName(string propertyName)
{
return propertyName.ToCase(Casing.Camel);
}
#endregion
#region Assistants
private IList<JsonProperty> CreateCustomProperties(Type type, MemberSerialization memberSerialization)
{
// Hierarchy
IReadOnlyList<Type> types = this.GetTypes(type);
// Head
Type head = types.OrderByDescending(item => item.GetInterfaces().Length).FirstOrDefault();
// Sources
IList<JsonProperty> sources = base.CreateProperties(head, memberSerialization);
// Targets
IList<JsonProperty> targets = new List<JsonProperty>(sources.Count);
// Repository
IReadOnlyDistribution<Type, JsonProperty> repository = sources.ToDistribution(item => item.DeclaringType);
foreach (Type current in types.Reverse())
{
IReadOnlyPage<JsonProperty> page;
if (repository.TryGetValue(current, out page) == true)
targets.AddRange(page);
}
return targets;
}
private IReadOnlyList<Type> GetTypes(Type type)
{
List<Type> types = new List<Type>();
if (type.IsInterface == true)
types.Add(type);
types.AddRange(type.GetInterfaces());
return types;
}
#endregion
}