gpt4 book ai didi

c# - Asp.net core WebApi 中空值的自定义序列化

转载 作者:行者123 更新时间:2023-12-05 06:06:24 27 4
gpt4 key购买 nike

我必须按照以下规则更改对象的默认 json 序列化/反序列化:

  1. 当C#对象为null时,必须序列化为Id为0的json对象。
  2. 当 json 对象的 id 等于 0 时,它必须反序列化为空值的 C# 对象。

我试试这个:

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 因为它将整个对象写为字符串,而我需要的是在修改后继续对象的正常序列化。我怎样才能做到这一点?

最佳答案

.NET 5 允许自定义转换器处理 null如果他们选择。来自 How to write custom converters for JSON serialization (marshalling) in .NET: Handle null values :

To enable a custom converter to handle null for a reference or value type, override JsonConverter<T>.HandleNull to return true.

因此在EntityConverter你需要添加

public override bool HandleNull => true;

但是,当您这样做时,您会遇到第二个问题,即在 Write() 中,您正在为 EventDefinition 编写序列化的 JSON作为双序列化字符串值,而不是作为对象。如何将其序列化为一个对象(替换为 null)?如果您申请了EntityConverter作为:

  • A [JsonConverter]应用于属性(property)。
  • 添加到 Converters 的转换器收藏。

然后你可以增强answer来自 How to use default serialization in a custom 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() 在该版本中永远不会传递空值。因此,在该版本中,您需要采用不同的方法,例如为包含类型添加自定义转换器或序列化 DTOs。而不是“真实的”对象。

    Json.NET 也永远不会调用它的 JsonConverter.WriteJson() 对于 null 值,以便两个序列化程序在 3.x 中的该限制中保持一致,请参阅 How to force JsonConverter.WriteJson() to be called for a null value 进行确认。该问题的答案显示了使用自定义契约(Contract)解析器的解决方法,因此在 3.1 中恢复到 Json.NET 可能是您的一个选项。 System.Text.Json对比does not make its contract model public .

  • 如果您申请了EntityConverter直接到EventDefinition使用 JsonConverterAttribute ,即:

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

    那么上面的做法就不行了。事实上,似乎没有办法为直接应用转换器的类型的实例生成“正常”序列化。相反,您需要在 Write() 中手动写入和读取每个必需的属性。和 Read() ,或编写您自己的反射代码以自动执行此操作。

演示 fiddle here .

关于c# - Asp.net core WebApi 中空值的自定义序列化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65744572/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com