Как я могу игнорировать ошибки в десериализации JSON с помощью [FromBody]? - PullRequest
2 голосов
/ 14 октября 2019

Я использую 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;
            };
        });

Я отправляю json с почтальоном

postman request

и получаю нулевое значение параметра местоположения в методе контроллера!

error handler

и

controller method

Как можно игнорировать ошибку преобразования типов и получить значение по умолчанию (0) в свойстве "Долгота"?

Ответы [ 4 ]

0 голосов
/ 14 октября 2019

Фактическое решение - это комбинация GTHvidstens ответа и OP-ответа .

Использование универсального JsonConverter<T> является правильным, но вы не должны использовать Convert.ChangeTypeи Activator.CreateInstance, эта часть была лучше в ответе GTHvidstens.

Ниже приведена окончательная версия, которая должна быть быстрее (и, возможно, просто более правильной), чем версия OP, используя default(T) и serialize.Deserialize, а такжевоспользоваться общим JsonConverter<T>, используемым в ответах OP.

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();
    }
}

То, что вы делаете с этим конвертером, не имеет значения. Одно из возможных решений, которое будет работать, - это уже опубликованный ОП - я не вижу проблем с этим подходом.

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>());
        });
0 голосов
/ 14 октября 2019

У меня 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; }
}
0 голосов
/ 14 октября 2019

Спасибо всем за помощь, решение для меня было:

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>());
        });
0 голосов
/ 14 октября 2019

Похоже, вам нужен пользовательский конвертер (как также упоминалось в комментариях).

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<double>)].

Также есть возможность использовать пользовательский конвертер для всей сериализации / десериализации JSON с использованием этого кода:

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

Тем не менее, это не может быть сделано в общем, поэтому вам придется написать свою собственную обработку всех возможных типов.

...