簡體   English   中英

Asp.net 核心 WebApi 中 null 值的自定義序列化

[英]Custom serialization of null values in Asp.net core WebApi

我必須按照以下規則更改 object 的默認 json 序列化/反序列化:

  1. When the C# object is null, it has to be serialize to json object with Id equals 0.
  2. When the json object has id equals 0, it has to be deserialize to C# object with null value.

我試試這個:

public class EntityConverter : JsonConverter<EventDefinition>
{
    public override EventDefinition Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        EventDefinition result = JsonSerializer.Deserialize<EventDefinition>(ref reader, options);
        if (result.EventDefinitionId == 0)
            return null;
        else return result;
    }

    public override void Write(Utf8JsonWriter writer, EventDefinition value, JsonSerializerOptions options)
    {
        if (value == null)
        {
            value = new EventDefinition();
            value.EventDefinitionId = 0;
            writer.WriteStringValue(JsonSerializer.Serialize(value));
        }
        else
            writer.WriteStringValue(JsonSerializer.Serialize(value));
    }
}

我需要替換writer.WriteStringValue因為它將整個 object 寫為字符串,我需要在修改后繼續正常序列化 object。 我怎樣才能做到這一點?

.NET 5允許自定義轉換器處理null如果他們選擇)。 如何在 .NET 中為 JSON 序列化(編組)編寫自定義轉換器:處理 null 值

要使自定義轉換器能夠處理 null 以獲取引用或值類型,請覆蓋JsonConverter<T>.HandleNull以返回 true。

因此在EntityConverter你需要添加

public override bool HandleNull => true;

但是,當您這樣做時,您會遇到第二個問題,即在Write()中,您正在將 EventDefinition 的序列化EventDefinition為雙序列化字符串值,而不是 object。 如何將其序列化為 object(替換null )? 如果您已將EntityConverter應用為:

  • 應用於屬性的[JsonConverter]
  • 添加到Converters集合的轉換器。

然后,您可以增強如何在自定義 System.Text.Json JsonConverter 中使用默認序列化的答案 包括HandleNull如下:

public class EntityConverter : DefaultConverterFactory<EventDefinition>
{
    protected override bool HandleNull => true;
    
    protected override EventDefinition Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions modifiedOptions, JsonConverter<EventDefinition> defaultConverter)
    {
        var result = base.Read(ref reader, typeToConvert, modifiedOptions, defaultConverter);
        return result?.EventDefinitionId == 0 ? null : result;
    }
    
    protected override void Write(Utf8JsonWriter writer, EventDefinition value, JsonSerializerOptions modifiedOptions, JsonConverter<EventDefinition> defaultConverter) 
    {
        value ??= new EventDefinition { EventDefinitionId = 0 };
        base.Write(writer, value, modifiedOptions, defaultConverter);
    }
}

public abstract class DefaultConverterFactory<T> : JsonConverterFactory
{
    class NullHandlingDefaultConverter : DefaultConverter
    {
        public NullHandlingDefaultConverter(JsonSerializerOptions options, DefaultConverterFactory<T> factory) : base(options, factory) { }
        public override bool HandleNull => true;
    }
    
    class DefaultConverter : JsonConverter<T>
    {
        readonly JsonSerializerOptions modifiedOptions;
        readonly DefaultConverterFactory<T> factory;
        readonly JsonConverter<T> defaultConverter;

        public DefaultConverter(JsonSerializerOptions options, DefaultConverterFactory<T> factory)
        {
            this.factory = factory ?? throw new ArgumentNullException();
            this.modifiedOptions = options.CopyAndRemoveConverter(factory.GetType());
            this.defaultConverter = (JsonConverter<T>)modifiedOptions.GetConverter(typeof(T));
        }

        public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) => factory.Write(writer, value, modifiedOptions, defaultConverter);

        public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => factory.Read(ref reader, typeToConvert, modifiedOptions, defaultConverter);
    }

    protected virtual T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions modifiedOptions, JsonConverter<T> defaultConverter)
        => defaultConverter.ReadOrSerialize<T>(ref reader, typeToConvert, modifiedOptions);

    protected virtual void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions modifiedOptions, JsonConverter<T> defaultConverter) 
        => defaultConverter.WriteOrSerialize(writer, value, modifiedOptions);

    protected virtual bool HandleNull => false;

    public override bool CanConvert(Type typeToConvert) => typeof(T) == typeToConvert;

    public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => HandleNull ? new NullHandlingDefaultConverter(options, this) : new DefaultConverter(options, this);
}

public static class JsonSerializerExtensions
{
    public static JsonSerializerOptions CopyAndRemoveConverter(this JsonSerializerOptions options, Type converterType)
    {
        var copy = new JsonSerializerOptions(options);
        for (var i = copy.Converters.Count - 1; i >= 0; i--)
            if (copy.Converters[i].GetType() == converterType)
                copy.Converters.RemoveAt(i);
        return copy;
    }

    public static void WriteOrSerialize<T>(this JsonConverter<T> converter, Utf8JsonWriter writer, T value, JsonSerializerOptions options)
    {
        if (converter != null)
            converter.Write(writer, value, options);
        else
            JsonSerializer.Serialize(writer, value, options);
    }

    public static T ReadOrSerialize<T>(this JsonConverter<T> converter, ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        if (converter != null)
            return converter.Read(ref reader, typeToConvert, options);
        else
            return (T)JsonSerializer.Deserialize(ref reader, typeToConvert, options);
    }
}

並按如下方式序列化List<EventDefinition> events

var options = new JsonSerializerOptions
{
    Converters = { new EntityConverter() },
    WriteIndented = true, // If you want
};

var json = JsonSerializer.Serialize(events, options);

筆記:

  • 在 .NET Core 3.x HandleNull中不可用 JsonConverter<T>.Write()在該版本中永遠不會傳遞 null 值。 因此,在該版本中,您需要采用不同的方法,例如為包含類型添加自定義轉換器或序列化DTO而不是“真實”對象。

    Json.NET also will never call call its JsonConverter.WriteJson() for a null value so the the two serializers are consistent in that limitation in 3.x, see How to force JsonConverter.WriteJson() to be called for a null value for confirmation. 該問題的答案顯示了使用自定義合同解析器的解決方法,因此在 3.1 中恢復到 Json.NET 可能是您的一個選項。 相比之下,System.Text.Json並未公開其合約 model

  • 如果您已使用JsonConverterAttributeEntityConverter直接應用於EventDefinition ,即:

     [JsonConverter(typeof(EntityConverter))] public class EventDefinition { public int EventDefinitionId { get; set; } }

    那么上面的方法就行不通了。 事實上,似乎沒有一種方法可以為直接應用轉換器的類型的實例生成“正常”序列化。 相反,您需要手動寫入和讀取Write()Read()中的每個必需屬性,或者編寫自己的反射代碼來自動執行此操作。

演示小提琴在這里

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM