JsonConverterAttribute 不适用于 ASP.NET Core 3.1 / 5.0 中的反序列化

JsonConverterAttribute is not working for Deserialization in ASP.NET Core 3.1 / 5.0

我想在运行时设置 属性 名称。我已经为 序列化 .

实现了这个

例如。我有一个简单的模型,如下所示:

[JsonConverter(typeof(DataModelConverter))]
public class DataModel
{
    public string Name { get; set; }
    public int Age { get; set; }
}

我有一个简单的 DataModelConverter,它继承自 JsonConverter:

public class DataModelConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Type type = value.GetType();

        JObject jo = new JObject();

        foreach (PropertyInfo prop in type.GetProperties())
        {
            jo.Add(prop.Name == "Name" ? "FullName" : prop.Name, new JValue(prop.GetValue(value)));
        }

        jo.WriteTo(writer);
    }

    public override bool CanRead
    {
        get { return false; }
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(DataModel);
    }
}

我有一个简单的控制器,如下所示:

[Route("api/[controller]")]
[ApiController]
public class NewtonController : ControllerBase
{
    public IEnumerable<DataModel> GetNewtonDatas([FromBody] DataModel input)
    {
        return new List<DataModel>()
        {
            new DataModel
            {
                Name="Ramil",
                Age=25
            },
            new DataModel
            {
                Name="Yusif",
                Age=26
            }
        };
    }
}

如果我调用这个 API,结果将如下所示(显示全名而不是姓名):

[
   {
       "FullName": "Ramil",
       "Age": 25
   },
   {
       "FullName": "Yusif",
       "Age": 26
   }
]

但是我有一个问题。这不适用于 反序列化

例如:如果我用这个正文调用这个API,那么Namenull

{
   "FullName":"Ramil"
}

我的属性不适用于反序列化。我想通过 属性 设置 属性 名称以便在运行时进行反序列化。

我不想使用一些中间件,我只想通过在运行时使用 any 属性来实现。我必须从我的 appsettings.json 文件中读取 JSON 属性 个名字。

感谢帮助!

您已将 CanRead 覆盖为 return false:

public override bool CanRead
{
    get { return false; }
}

这会导致 Json.NET 在反序列化期间不调用转换器的 DataModelConverter.ReadJson() 方法,而是使用默认反序列化。由于 "FullName"Name 属性 的名称不同(大小写不变),因此它永远不会被设置,并保持 null.

要解决此问题,删除 CanRead 的覆盖(默认实现 returns true)并实现 ReadJson(),例如如下:

public class DataModelConverter : NameRemappingConverterBase
{
    static string AlternateName => "FullName";
    static string OriginalName => "Name";

    public override bool CanConvert(Type objectType) => objectType == typeof(DataModel);
    
    // Replace the below logic with name mappings from appsettings.json

    protected override string ToJsonPropertyName(JsonProperty property) => 
        string.Equals(property.UnderlyingName, OriginalName, StringComparison.OrdinalIgnoreCase) ? AlternateName : base.ToJsonPropertyName(property);
    
    protected override string FromJsonPropertyName(string name) => 
        string.Equals(name, AlternateName, StringComparison.OrdinalIgnoreCase) ? OriginalName : base.FromJsonPropertyName(name);
}

public abstract class NameRemappingConverterBase : JsonConverter
{
    protected virtual string ToJsonPropertyName(JsonProperty property) => property.PropertyName;
    
    protected virtual string FromJsonPropertyName(string name) => name;

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
            return null;
        if (reader.TokenType != JsonToken.StartObject)
            throw new JsonSerializationException(string.Format("Unexpected token {0}", reader.TokenType));
        var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
        var value = existingValue ?? contract.DefaultCreator();
        while (reader.ReadToContentAndAssert().TokenType != JsonToken.EndObject)
        {
            if (reader.TokenType != JsonToken.PropertyName)
                throw new JsonSerializationException(string.Format("Unexpected token {0}", reader.TokenType));
            var name = FromJsonPropertyName((string)reader.Value);
            reader.ReadToContentAndAssert();
            var property = contract.Properties.GetProperty(name, StringComparison.OrdinalIgnoreCase);
            if (!ShouldDeserialize(property))
            {
                reader.Skip();
            }
            else
            {
                var propertyValue = serializer.Deserialize(reader, property.PropertyType);
                property.ValueProvider.SetValue(value, propertyValue);
            }
        }
        return value;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType());
        writer.WriteStartObject();
        foreach (var property in contract.Properties.Where(p => ShouldSerialize(p, value)))
        {
            var propertyValue = property.ValueProvider.GetValue(value);
            if (propertyValue == null && serializer.NullValueHandling == NullValueHandling.Ignore)
                continue;
            var name = ToJsonPropertyName(property);
            writer.WritePropertyName(name);
            serializer.Serialize(writer, propertyValue);
        }
        writer.WriteEndObject();
    }

    protected virtual bool ShouldDeserialize(JsonProperty property) =>
        property != null && property.Writable;

    protected virtual bool ShouldSerialize(JsonProperty property, object value) =>
        property.Readable && !property.Ignored && (property.ShouldSerialize == null || property.ShouldSerialize(value));
}

public static partial class JsonExtensions
{
    public static JsonReader ReadToContentAndAssert(this JsonReader reader) =>
        reader.ReadAndAssert().MoveToContentAndAssert();

    public static JsonReader MoveToContentAndAssert(this JsonReader reader)
    {
        if (reader == null)
            throw new ArgumentNullException();
        if (reader.TokenType == JsonToken.None)       // Skip past beginning of stream.
            reader.ReadAndAssert();
        while (reader.TokenType == JsonToken.Comment) // Skip past comments.
            reader.ReadAndAssert();
        return reader;
    }

    public static JsonReader ReadAndAssert(this JsonReader reader)
    {
        if (reader == null)
            throw new ArgumentNullException();
        if (!reader.Read())
            throw new JsonReaderException("Unexpected end of JSON stream.");
        return reader;
    }
}

演示 fiddle here.