使用在启动时全局设置的 JsonStringEnumConverter 排除模型的枚举 属性?
Exclude an enum property of a Model from using the JsonStringEnumConverter which is globally set at the Startup?
我正在使用最新的 .NET Core 3.1.1 和之前使用 Newtonsoft.Json
的 System.Text.Json
开发 ASP.NET 核心应用程序。按照 Microsoft Migration guide 中的建议
我已经完成了更改。此外,由于我的大部分枚举都需要序列化为字符串,因此我已将 Startup.cs ConfigureServices
配置为全局使用 JsonStringEnumConverter
。
public void ConfigureServices(IServiceCollection services)
{
// lines omitted for brevity
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
options.JsonSerializerOptions.IgnoreNullValues = true;
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
});
}
但最近,在发布后我们意识到只有少数枚举通过我们的 API 在 json 中作为数字给出。由于这些 API 是外部消耗的,将数字更改为字符串可能是一件代价高昂的事情。
那么,有没有办法忽略某些枚举属性的通用性,例如带有 [JsonIgnore]
属性的装饰?
JsonStringEnumConverter
is actually a subclass of JsonConverterFactory
. It manufactures a specific JsonConverterEnum
对于在序列化过程中遇到的每个具体 enum
类型,依次将该特定 enum
类型序列化为字符串。
如果您不想将某些特定的 enum
type 序列化为字符串,您可以使用 decorator pattern 并创建自己的转换器装饰 JsonStringEnumConverter
但阻止 enum
类型按如下方式转换的工厂:
public class OptOutJsonConverterFactory : JsonConverterFactoryDecorator
{
readonly HashSet<Type> optOutTypes;
public OptOutJsonConverterFactory(JsonConverterFactory innerFactory, params Type [] optOutTypes) : base(innerFactory) => this.optOutTypes = optOutTypes.ToHashSet();
public override bool CanConvert(Type typeToConvert) => base.CanConvert(typeToConvert) && !optOutTypes.Contains(typeToConvert);
}
public class JsonConverterFactoryDecorator : JsonConverterFactory
{
readonly JsonConverterFactory innerFactory;
public JsonConverterFactoryDecorator(JsonConverterFactory innerFactory)
{
if (innerFactory == null)
throw new ArgumentNullException(nameof(innerFactory));
this.innerFactory = innerFactory;
}
public override bool CanConvert(Type typeToConvert) => innerFactory.CanConvert(typeToConvert);
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => innerFactory.CreateConverter(typeToConvert, options);
}
然后在选项中使用如下:
options.Converters.Add(new OptOutJsonConverterFactory(new JsonStringEnumConverter(),
// Add here all enum types to serialize as integers:
typeof(SomeEnumNotToSerializeAsAString)
//, ...
));
备注:
如果维护要序列化为整数的枚举类型列表不方便,您可以使用一些自定义标记要序列化为整数的枚举类型 attribute,然后排除标记为整数的类型CanConvert(CanConvert(Type typeToConvert)
.
中的属性
装饰器模式是必需的,因为JsonStringEnumConverter
是密封的。
模型 fiddle #1 here.
或者,如果您不想将某些特定的 enum
属性 序列化为字符串,您可以对 属性 使用 JsonConverterAttribute
忽略传入的 JsonSerializerOptions
并生成默认序列化:
/// <summary>
/// Apply this converter to a property to force the property to be serialized with default options.
/// This converter can ONLY be applied to a property; setting it in options or on a type may cause a stack overflow exception!
/// </summary>
/// <typeparam name="T">the property's declared return type</typeparam>
public class SerializePropertyAsDefaultConverter<T> : JsonConverter<T>
{
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return JsonSerializer.Deserialize<T>(ref reader); // Ignore the incoming options!
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, value); // Ignore the incoming options!
}
}
并将其应用到您的模型中,如下所示:
public class Model
{
public StringEnum StringEnum { get; set; }
[JsonConverter(typeof(SerializePropertyAsDefaultConverter<SomeEnumNotToSerializeAsAString>))]
public SomeEnumNotToSerializeAsAString SomeEnumNotToSerializeAsAString { get; set; }
}
备注:
此解决方案利用了 documented precedence for converters:
[JsonConverter]
applied to a property.
- A converter added to the Converters collection.
[JsonConverter]
applied to a custom value type or POCO.
模型 fiddle #2 here.
我正在使用最新的 .NET Core 3.1.1 和之前使用 Newtonsoft.Json
的 System.Text.Json
开发 ASP.NET 核心应用程序。按照 Microsoft Migration guide 中的建议
我已经完成了更改。此外,由于我的大部分枚举都需要序列化为字符串,因此我已将 Startup.cs ConfigureServices
配置为全局使用 JsonStringEnumConverter
。
public void ConfigureServices(IServiceCollection services)
{
// lines omitted for brevity
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
options.JsonSerializerOptions.IgnoreNullValues = true;
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
});
}
但最近,在发布后我们意识到只有少数枚举通过我们的 API 在 json 中作为数字给出。由于这些 API 是外部消耗的,将数字更改为字符串可能是一件代价高昂的事情。
那么,有没有办法忽略某些枚举属性的通用性,例如带有 [JsonIgnore]
属性的装饰?
JsonStringEnumConverter
is actually a subclass of JsonConverterFactory
. It manufactures a specific JsonConverterEnum
对于在序列化过程中遇到的每个具体 enum
类型,依次将该特定 enum
类型序列化为字符串。
如果您不想将某些特定的 enum
type 序列化为字符串,您可以使用 decorator pattern 并创建自己的转换器装饰 JsonStringEnumConverter
但阻止 enum
类型按如下方式转换的工厂:
public class OptOutJsonConverterFactory : JsonConverterFactoryDecorator
{
readonly HashSet<Type> optOutTypes;
public OptOutJsonConverterFactory(JsonConverterFactory innerFactory, params Type [] optOutTypes) : base(innerFactory) => this.optOutTypes = optOutTypes.ToHashSet();
public override bool CanConvert(Type typeToConvert) => base.CanConvert(typeToConvert) && !optOutTypes.Contains(typeToConvert);
}
public class JsonConverterFactoryDecorator : JsonConverterFactory
{
readonly JsonConverterFactory innerFactory;
public JsonConverterFactoryDecorator(JsonConverterFactory innerFactory)
{
if (innerFactory == null)
throw new ArgumentNullException(nameof(innerFactory));
this.innerFactory = innerFactory;
}
public override bool CanConvert(Type typeToConvert) => innerFactory.CanConvert(typeToConvert);
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => innerFactory.CreateConverter(typeToConvert, options);
}
然后在选项中使用如下:
options.Converters.Add(new OptOutJsonConverterFactory(new JsonStringEnumConverter(),
// Add here all enum types to serialize as integers:
typeof(SomeEnumNotToSerializeAsAString)
//, ...
));
备注:
如果维护要序列化为整数的枚举类型列表不方便,您可以使用一些自定义标记要序列化为整数的枚举类型 attribute,然后排除标记为整数的类型
CanConvert(CanConvert(Type typeToConvert)
. 中的属性
装饰器模式是必需的,因为
JsonStringEnumConverter
是密封的。
模型 fiddle #1 here.
或者,如果您不想将某些特定的 enum
属性 序列化为字符串,您可以对 属性 使用 JsonConverterAttribute
忽略传入的 JsonSerializerOptions
并生成默认序列化:
/// <summary>
/// Apply this converter to a property to force the property to be serialized with default options.
/// This converter can ONLY be applied to a property; setting it in options or on a type may cause a stack overflow exception!
/// </summary>
/// <typeparam name="T">the property's declared return type</typeparam>
public class SerializePropertyAsDefaultConverter<T> : JsonConverter<T>
{
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return JsonSerializer.Deserialize<T>(ref reader); // Ignore the incoming options!
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, value); // Ignore the incoming options!
}
}
并将其应用到您的模型中,如下所示:
public class Model
{
public StringEnum StringEnum { get; set; }
[JsonConverter(typeof(SerializePropertyAsDefaultConverter<SomeEnumNotToSerializeAsAString>))]
public SomeEnumNotToSerializeAsAString SomeEnumNotToSerializeAsAString { get; set; }
}
备注:
此解决方案利用了 documented precedence for converters:
[JsonConverter]
applied to a property.- A converter added to the Converters collection.
[JsonConverter]
applied to a custom value type or POCO.
模型 fiddle #2 here.