【问题标题】:How can i ignore errors in json deserialization using [FromBody]?如何使用 [FromBody] 忽略 json 反序列化中的错误?
【发布时间】:2019-10-15 18:38:06
【问题描述】:

我使用asp net core 3.0,但遇到问题:

型号:

public class Location
{
    public string Name { get; set; }
    public double Latitude { get; set; }
    public double Longitude { get; set; }
}

控制器中的方法

[HttpPost("api/somePost")]
public Task Test([FromBody] Location location)
{
    // Do somethimg
    return Task.CompletedTask;
}

启动:

services.AddMvc().AddNewtonsoftJson(options =>
        {
            options.SerializerSettings.Error = (sender, args) =>
            {
                args.ErrorContext.Handled = true;
            };
        });

我用 Postman 发送 json

并在控制器方法中的位置参数中获取 null!

如何忽略类型转换的错误并在“经度”属性中获取默认值(0)?

【问题讨论】:

  • 如果locationHttpPost 中是null,则您的JSON 可能有问题。你如何发布它?您是否使用了正确的荣誉 ({})?您不能期望 string 被序列化为 double,请参阅 Longitude
  • 您可以尝试使用邮递员调用您的 web api,然后更新问题吗?添加您的邮递员请求和收到的错误。
  • json不需要用{}括起来吗?
  • 所有数据都合理时位置不为空吗?
  • OP 对此代码或请求没有问题,他只是想以某种方式处理模型状态验证,如果出现反序列化错误,它将使用默认值处理。

标签: c# asp.net-core asp.net-core-3.0


【解决方案1】:

听起来您需要一个自定义转换器(在 cmets 中也提到过)。

public class YourCustomConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsAssignableFrom(typeof(double));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        try
        {
            return serializer.Deserialize<double>(reader);
        }
        catch
        {
            return 0d;
        }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}

现在您只需使用JsonConverter 装饰您希望其有效的属性。对于您的示例,它看起来像这样:

public class Location
{
    public string Name { get; set; }

    [JsonConverter(typeof(YourCustomConverter))]
    public double Latitude { get; set; }

    [JsonConverter(typeof(YourCustomConverter))]
    public double Longitude { get; set; }
}

现在,如果您发送无效数据,反序列化器将引发异常,该异常会被自定义转换器捕获,并返回 0(或您希望的任何值)。

更新:

免责声明:这是未经测试的!

您可能可以制作一个通用转换器来处理所有情况:

public class YourCustomConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsAssignableFrom(typeof(T));
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        try
        {
            return serializer.Deserialize<T>(reader);
        }
        catch
        {
            return default(T);
        }
    }
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}

您仍然可以将其用作属性[JsonConverter(typeof(YourCustomConverter&lt;double&gt;)]

还有一个选项可以使用此代码为所有 JSON 序列化/反序列化使用自定义转换器:

services.AddMvc().AddNewtonsoftJson(options =>
{
    // Configure a custom converter
    options.SerializerOptions.Converters.Add(new SomeOtherCustomJsonConverter());
});

但是,这不能真正成为通用的,因此您必须编写自己的所有可能类型的处理。

【讨论】:

  • 谢谢,这是可行的,但我需要更统一的解决方案,女巫将适用于所有值类型且没有属性。
【解决方案2】:

这个JsonConverter 为我工作:

public class DoubleJsonConverter : JsonConverter<double>
{
    public override double Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        try
        {
            return reader.GetDouble();
        }
        catch (Exception)
        {
            return 0.0;
        }
    }

    public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options)
    {
        try
        {
            writer.WriteNumberValue((decimal)value);
        }
        catch (Exception)
        {
            writer.WriteNumberValue(0.0);
        }
    }
}

别忘了

using System.Text.Json.Serialization;

public class Location
{
    public string Name { get; set; }

    [JsonConverter(typeof(DoubleJsonConverter))]
    public double Latitude { get; set; }

    [JsonConverter(typeof(DoubleJsonConverter))]
    public double Longitude { get; set; }
}

【讨论】:

    【解决方案3】:

    实际的解决方案是GTHvidstens answerOPs answer的组合。

    使用通用的JsonConverter&lt;T&gt; 是正确的,但您不应该使用Convert.ChangeTypeActivator.CreateInstance,这部分在GTHvidstens 答案中更好。

    以下是最终版本,它应该比使用 default(T)serialize.Deserialize 的 OP 版本更快(并且可以说更正确),同时还利用了 OP 答案中使用的通用 JsonConverter&lt;T&gt;

    public class ErrorIgnoringJsonConverter<T> : JsonConverter<T>
    {
        public override bool CanWrite => false;
        public override bool CanRead => true;
    
        public override T ReadJson(JsonReader reader, Type objectType, T existingValue, bool hasExistingValue, JsonSerializer serializer)
        {
            try
            {
                return serializer.Deserialize<T>(reader);
            }
            catch
            {
                return default(T);
            }
        }
    
        public override void WriteJson(JsonWriter writer, T value, JsonSerializer serializer)
        {
            throw new NotImplementedException();
        }
    }
    

    你用这个转换器做什么并不重要。一种可行的解决方案是已经发布的 OP - 我认为这种方法没有任何问题。

    services
        .AddMvc()
        .AddNewtonsoftJson(options =>
            {
                options.SerializerSettings.Converters.Add(new ErrorIgnoringJsonConverter<bool>());
                options.SerializerSettings.Converters.Add(new ErrorIgnoringJsonConverter<int>());
                options.SerializerSettings.Converters.Add(new ErrorIgnoringJsonConverter<long>());
                options.SerializerSettings.Converters.Add(new ErrorIgnoringJsonConverter<float>());
                options.SerializerSettings.Converters.Add(new ErrorIgnoringJsonConverter<double>());
                options.SerializerSettings.Converters.Add(new ErrorIgnoringJsonConverter<decimal>());
                options.SerializerSettings.Converters.Add(new ErrorIgnoringJsonConverter<DateTime>());
            });
    

    【讨论】:

      【解决方案4】:

      感谢大家的帮助,我的解决方案是:

      public class ErrorIgnoringJsonConverter<T> : JsonConverter<T>
      {
          public override bool CanWrite => false;
      
          public override T ReadJson(JsonReader reader, Type objectType, T existingValue, bool hasExistingValue, JsonSerializer serializer)
          {
              try
              {
                  return (T)Convert.ChangeType(reader.Value, typeof(T));
              }
              catch (Exception)
              {
                  return Activator.CreateInstance<T>();
              }
          }
      
          public override void WriteJson(JsonWriter writer, T value, JsonSerializer serializer)
          {
              throw new NotImplementedException();
          }
      }
      

      并在启动时添加:

      services.AddMvc().AddNewtonsoftJson(options =>
              {
                  options.SerializerSettings.Converters.Add(new ErrorIgnoringJsonConverter<bool>());
                  options.SerializerSettings.Converters.Add(new ErrorIgnoringJsonConverter<int>());
                  options.SerializerSettings.Converters.Add(new ErrorIgnoringJsonConverter<long>());
                  options.SerializerSettings.Converters.Add(new ErrorIgnoringJsonConverter<float>());
                  options.SerializerSettings.Converters.Add(new ErrorIgnoringJsonConverter<double>());
                  options.SerializerSettings.Converters.Add(new ErrorIgnoringJsonConverter<decimal>());
                  options.SerializerSettings.Converters.Add(new ErrorIgnoringJsonConverter<DateTime>());
              });
      

      【讨论】:

      • 您不应为此使用Convert.ChangeTypeActivator.CreateInstanceGTHvidstens answer 展示了一个转换器,它做同样的事情,但只是更好。你应该使用default(T) 而不是Activator.CreateInstance&lt;T&gt;()。而不是Convert.ChangeType,您应该使用serializer.Deserialize&lt;T&gt; 将值反序列化为正确的类型。它不仅更正确,而且我认为它也会更快。从他们的答案中取出转换器并将其重命名为ErrorIgnoringJsonConverter。您在启动时的代码可以保留。
      • 我只是注意到他们没有使用通用版本。实际的解决方案是您和他们的转换器的组合。我会添加一个答案。
      • 我已添加my answer。正如我所说,您和 GTHvidstens 的答案都有好的和坏的功能。我结合了好的,同时保持界面不变,这样你就可以像以前一样使用它。希望它有所帮助:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-01
      • 2012-02-08
      • 1970-01-01
      • 1970-01-01
      • 2017-02-01
      • 2012-02-02
      相关资源
      最近更新 更多