Выборочное исключение свойства в одной сериализации JSON, но не в другой
у меня такой ПОКО
public class Foo {
public string PartitionKey => $"foo-{Bar}";
public string Bar { get; set; }
}
Я храню этот POCO как сериализованный JSON в базе данных (в частности, Azure Cosmos DB) и делаю его доступным для клиентов через ASP.NET WebApi.
При сериализации этого документа для Cosmos DB мне нужен PartitionKey, поэтому я не могу полностью исключить его, используя[JsonIgnore]
. Но я не хочу, чтобы он был включен в мой ответ API. Так как же проще всего этого добиться? В идеале я не хочу писать свой собственный JsonSerializer, но, возможно, использую какой-то пользовательский атрибут?
Ожидаемые результаты:
Для базы данных:
{
"partitionKey": "foo-alice",
"bar": "alice"
}
Для ответа API:
{
"bar": "alice"
}
Использование .NET7 иSystem.Text.Json
1 ответ
У вас есть несколько вариантов выборочного исключения свойства во время сериализации.
Во-первых, поскольку свойство доступно только для чтения, вы можете установитьJsonSerializerOptions.IgnoreReadOnlyProperties = true
:
var options = new JsonSerializerOptions
{
IgnoreReadOnlyProperties = true,
// Add other options as required
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true,
};
var json = JsonSerializer.Serialize(foo, options);
Это исключит все свойства только для чтения, включая .
Во-вторых, если вам нужно исключить только, но включить другие свойства, доступные только для чтения, или исключить изменяемые свойства, или иным образом нуждаться в большем контроле над тем, что сериализуется, вы можете добавить модификатор DefaultJsonTypeInfoResolver, чтобы исключить нежелательные члены.
Чтобы исключить свойство по имени, добавьте следующий метод расширения:
public static partial class JsonSerializerExtensions
{
public static DefaultJsonTypeInfoResolver Exclude(this DefaultJsonTypeInfoResolver resolver, Type type, params string [] membersToExclude)
{
if (resolver == null || membersToExclude == null)
throw new ArgumentNullException();
var membersToExcludeSet = membersToExclude.ToHashSet();
resolver.Modifiers.Add(typeInfo =>
{
if (typeInfo.Kind == JsonTypeInfoKind.Object && type.IsAssignableFrom(typeInfo.Type)) // Or type == typeInfo.Type if you don't want to exclude from subtypes
foreach (var property in typeInfo.Properties)
if (property.GetMemberName() is {} name && membersToExcludeSet.Contains(name))
property.ShouldSerialize = static (obj, value) => false;
});
return resolver;
}
public static string? GetMemberName(this JsonPropertyInfo property) => (property.AttributeProvider as MemberInfo)?.Name;
}
И теперь вы сможете:
var options = new JsonSerializerOptions
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
.Exclude(typeof(Foo), nameof(Foo.PartitionKey)),
// Add other options as required
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true,
};
var json = JsonSerializer.Serialize(foo, options);
Если вы хотите исключить по пользовательскому атрибуту, а не по имени, введите следующий метод расширения:
public static partial class JsonSerializerExtensions
{
public static DefaultJsonTypeInfoResolver ExcludeByAttribute<TAttribute>(this DefaultJsonTypeInfoResolver resolver) where TAttribute : System.Attribute
{
if (resolver == null)
throw new ArgumentNullException();
var attr = typeof(TAttribute);
resolver.Modifiers.Add(typeInfo =>
{
if (typeInfo.Kind == JsonTypeInfoKind.Object)
foreach (var property in typeInfo.Properties)
if (property.AttributeProvider?.IsDefined(attr, true) == true)
property.ShouldSerialize = static (obj, value) => false;
});
return resolver;
}
}
Затем изменитеFoo
следующее:
[System.AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class JsonExludeFromResponseAttribute : System.Attribute { }
public class Foo {
[JsonExludeFromResponseAttribute]
public string PartitionKey => $"foo-{Bar}";
public string Bar { get; set; }
}
И исключить следующим образом:
var options = new JsonSerializerOptions
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
.ExcludeByAttribute<JsonExludeFromResponseAttribute>(),
// Add other options as required
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true,
};
var json = JsonSerializer.Serialize(foo, options);
Примечания:
Настройка контракта — новая функция в .NET 7.
Если у вас много разных типов с
PartitionKey
свойство, которое вы всегда хотите исключить, вы можете передать вtypeof(object)
как базовый класс, из которого следует исключить свойство.
Демонстрационная скрипка здесь.