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

本文关键字:枚举 序列化 字符串 设置 NET 默认 Json | 更新日期: 2023-09-27 18:01:42

我正在使用 Json.NET 7.0.1。

文件说

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

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

JsonConvert.DefaultSettings = (() =>
{
    var settings = new JsonSerializerSettings();
    settings.Converters.Add(new StringEnumConverter());
    return settings;
});

但是,在某些情况下,我希望Enums序列化为整数,例如,当我构建要存储在数据库中的 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);
    }
}

但是,即使在这种情况下,Enums也会序列化为字符串。我检查了serializerSettings没有Converters,它的ContractResolvernull.

为什么会这样?

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

发生这种情况是因为JsonConvert.SerializeObject通过在内部调用JsonSerializer.CreateDefault(JsonSerializerSettings settings)默认设置之上应用传入设置。 我不知道是否/在哪里记录了这种行为,但它在源代码中可见。 因此,除了本地指定的转换器的空列表之外,还将使用默认的转换器列表,这意味着将使用默认StringEnumConverter

您有几种方法可以解决此问题:

  1. 在不使用默认设置的情况下自行构建JsonSerializer

    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());
    
  2. 用不将
  3. 枚举序列化为字符串的StringEnumConverter取代默认,例如:

    public class IntegerEnumConverter : StringEnumConverter
    {
        public override bool CanRead { get { return false; } }
        public override bool CanWrite { get { return false; } }
    }
    

    然后像这样使用它:

    var json = JsonConvert.SerializeObject(value, Formatting.None, new JsonSerializerSettings { Converters = new JsonConverter[] { new IntegerEnumConverter() } });
    

    本地指定的转换器将优先于默认转换器。

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

原因

我们在这里看到的行为是设计使然,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]);
            }
        }

正如我们所看到的,转换器与提供的默认转换器合并,尽管优先级更高。

溶液

我们可以利用最近合并的转换器的更高优先级并防止StringEnumConverter转换。

class PreventStringEnumConverter : StringEnumConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var type = value.GetType();
        var undertype = Enum.GetUnderlyingType(type);
        var converted=Convert.ChangeType(value, undertype);
        writer.WriteValue(converted);
    }
}

用法如下:

var json = JsonConvert.SerializeObject(obj,
        new JsonSerializerSettings
        {
            Converters = new List<JsonConverter> {new PreventStringEnumConverter()}
        });