C# 即使使用默认设置,Json.NET也会将枚举序列化为字符串

C# 即使使用默认设置,Json.NET也会将枚举序列化为字符串,c#,json,json.net,C#,Json,Json.net,我使用的是Json.NET7.0.1 报告说 Enum[序列化为]整数(可以是StringEnumConverter的枚举值名称) 在my Global.asax.cs中,我指定默认设置如下: JsonConvert.DefaultSettings = (() => { var settings = new JsonSerializerSettings(); settings.Converters.Add(new StringEnumConverter()); re

我使用的是Json.NET7.0.1

报告说

Enum
[序列化为]整数(可以是StringEnumConverter的枚举值名称)

在my Global.asax.cs中,我指定默认设置如下:

JsonConvert.DefaultSettings = (() =>
{
    var settings = new JsonSerializerSettings();
    settings.Converters.Add(new StringEnumConverter());
    return settings;
});
但是,在某些情况下,我希望
enum
被序列化为整数,例如,当我构建一个要存储在数据库中的JSON时

我是这样做的:

public class JsonSerializedType<T> : IUserType where T : class
{
    private static readonly JsonSerializerSettings serializerSettings = new JsonSerializerSettings();

    public void NullSafeSet(IDbCommand cmd, object value, int index)
    {
        cmd.Parameters[index].Value = JsonConvert.SerializeObject(value as T, serializerSettings);
    }
}
公共类JsonSerializedType:IUserType其中T:class
{
私有静态只读JsonSerializerSettings serializerSettings=新JsonSerializerSettings();
public void NullSafeSet(IDbCommand cmd,对象值,int索引)
{
cmd.Parameters[index].Value=JsonConvert.SerializeObject(值为T,serializerSettings);
}
}
但是,即使在这种情况下,
枚举
也被序列化为字符串。我检查了
serializerSettings
是否没有
converter
,其
ContractResolver
是否为
null

为什么会这样?

原因 我们在这里看到的行为是经过设计的,
JsonConvert
方法中传递的设置与提供的
DefaultSettings
合并,下面是JsonSerializer类中Json.Net源代码的一小部分,以澄清这种情况

 private static void ApplySerializerSettings(JsonSerializer serializer, JsonSerializerSettings settings)
    {
        if (!CollectionUtils.IsNullOrEmpty(settings.Converters))
        {
            // insert settings converters at the beginning so they take precedence
            // if user wants to remove one of the default converters they will have to do it manually
            for (int i = 0; i < settings.Converters.Count; i++)
            {
                serializer.Converters.Insert(i, settings.Converters[i]);
            }
        }
其用法如下所示:

var json = JsonConvert.SerializeObject(obj,
        new JsonSerializerSettings
        {
            Converters = new List<JsonConverter> {new PreventStringEnumConverter()}
        });
var json=JsonConvert.SerializeObject(obj,
新JsonSerializerSettings
{
转换器=新列表{new PreventStringEnumConverter()}
});
之所以发生这种情况,是因为通过内部调用将传入设置应用于默认设置之上。我不知道是否/在何处记录了此行为,但它在中可见。因此,除了本地指定转换器的空列表外,还将使用默认的转换器列表,这意味着将使用默认的
StringEnumConverter

您有几个选项可以解决此问题:

  • 不使用默认设置:

    public static class JsonExtensions
    {
        public static string SerializeObjectNoDefaultSettings(object value, Formatting formatting, JsonSerializerSettings settings)
        {
            var jsonSerializer = JsonSerializer.Create(settings);
            jsonSerializer.Formatting = formatting;
    
            StringBuilder sb = new StringBuilder(256);
            StringWriter sw = new StringWriter(sb, CultureInfo.InvariantCulture);
            using (JsonTextWriter jsonWriter = new JsonTextWriter(sw))
            {
                jsonWriter.Formatting = jsonSerializer.Formatting;
                jsonSerializer.Serialize(jsonWriter, value);
            }
    
            return sw.ToString(); 
        }
    }
    
    然后像这样使用它:

    var json = JsonExtensions.SerializeObjectNoDefaultSettings(value, Formatting.None, new JsonSerializerSettings());
    
    var json = JsonConvert.SerializeObject(value, Formatting.None, new JsonSerializerSettings { Converters = new JsonConverter[] { new IntegerEnumConverter() } });
    
  • 将默认的
    StringEnumConverter
    替换为不将枚举序列化为字符串的转换器,例如:

    public class IntegerEnumConverter : StringEnumConverter
    {
        public override bool CanRead { get { return false; } }
    
        public override bool CanWrite { get { return false; } }
    }
    
    然后像这样使用它:

    var json = JsonExtensions.SerializeObjectNoDefaultSettings(value, Formatting.None, new JsonSerializerSettings());
    
    var json = JsonConvert.SerializeObject(value, Formatting.None, new JsonSerializerSettings { Converters = new JsonConverter[] { new IntegerEnumConverter() } });
    
    本地指定的转换器将优先于默认转换器

  • 暂时清空将是一个坏主意,因为它不是线程安全的