десериализовать json с массивом enum - PullRequest
3 голосов
/ 31 января 2020

Используя enum:

namespace AppGlobals
{
    [JsonConverter(typeof(JsonStringEnumConverter))]
    public enum BoardSymbols
    {
        [EnumMember(Value = "X")]
        First = 'X',
        [EnumMember(Value = "O")]
        Second = 'O',
        [EnumMember(Value = "?")]
        EMPTY = '?'
    }
}

Я хотел бы определить модель для моего API:

using System;
using System.ComponentModel.DataAnnotations;
using System.Text.Json.Serialization;
using Newtonsoft.Json;

namespace Assignment_1
{
    public class MyRequest
    {
//...
        [Required]
        [MinLength(9)]
        [MaxLength(9)]
        [JsonProperty("changeTypes", ItemConverterType = typeof(JsonStringEnumConverter))]
        public AppGlobals.BoardSymbols[] GameBoard { get; set; }
    }
}

Где GameBoard должен сериализоваться в JSON как массив строки с именами, указанными атрибутами EnumMember. Этот подход адаптирован из десериализации json символа в качестве перечисления . Однако это не работает. Это сработает, если я изменю перечисление на:

    [JsonConverter(typeof(JsonStringEnumConverter))]
    public enum BoardSymbols
    {
      X='X',
      Y='Y'
    }

Но я, очевидно, достиг предела «пустого» перечисления. Как я могу это сделать?

обновление 2:

У меня не было AddNewtonsoftJson() в моем запуске, полностью преобразовав в Newtonsoft. Теперь моя ошибка, возможно, более действенная:

System.InvalidCastException: Unable to cast object of type 'CustomJsonStringEnumConverter' to type 'Newtonsoft.Json.JsonConverter'.
   at Newtonsoft.Json.Serialization.JsonTypeReflector.CreateJsonConverterInstance(Type converterType, Object[] args)

Это имеет смысл, решение, предписанное мне здесь , определило JsonConverterFactory .. Мне просто нужен необработанный JsonConverter для моего варианта использования.

1 Ответ

0 голосов
/ 02 февраля 2020

TL / DR: у вас есть две основные проблемы c:

  1. . NET Core 3.0+ имеет новый встроенный JSON сериализатор System.Text.Json, и вы смешиваете атрибуты и классы между этим новым сериализатором и Json. NET. Это очень легко сделать, когда оба установлены, потому что они имеют некоторые имена классов, такие как JsonSerializer и JsonConverter.

  2. Новый сериализатор используется по умолчанию , но пока не поддерживает сериализацию перечислений в виде строк с именами пользовательских значений; см. System.Text. Json: Как указать пользовательское имя для значения перечисления? для подробностей.

Самый простой способ решить вашу проблему - переключиться обратно на Json. NET, как показано здесь и использовать атрибуты, преобразователи и пространства имен исключительно из этого сериализатора.

Сначала давайте разберем различия и сходства между двумя сериализаторами:

  1. System.Text.Json:

  2. Json. NET:

Имея это в виду, какой сериализатор вы используете в своем коде? Поскольку вы любезно включили пространства имен в свой вопрос, мы можем проверить:

using System.Text.Json.Serialization; // System.Text.Json
using Newtonsoft.Json;                // Json.NET

namespace Assignment_1
{
    public class MyRequest
    {
//...
        [JsonProperty(                                         // JsonProperty from Newtonsoft
            "changeTypes", 
            ItemConverterType = typeof(JsonStringEnumConverter)// JsonStringEnumConverter from System.Text.Json
        )]
        public AppGlobals.BoardSymbols[] GameBoard { get; set; }
    }
}

Итак, как вы можете видеть, вы смешиваете атрибуты из Newtonsoft с конвертерами из System.Text.Json, что не будет работать , (Возможно, вы выбрали пространства имен из щелчка правой кнопкой мыши «Разрешить -> используя ...» в Visual Studio?)

Итак, как решить проблему? Поскольку Json. NET поддерживает переименование значений перечисления «из коробки», самый простой способ решить вашу проблему - использовать этот сериализатор. Хотя, возможно, он не такой производительный, как System.Text.Json, он гораздо более полный и полнофункциональный.

Для этого удалить пространства имен System.Text.Json.Serialization и System.Text.Json и ссылки на тип JsonStringEnumConverter из вашего кода и измените MyRequest и BoardSymbols следующим образом:

using System.Runtime.Serialization;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json;

namespace Assignment_1
{
    public class MyRequest
    {
//...
        [Required]
        [MinLength(9)]
        [MaxLength(9)]
        [JsonProperty("changeTypes")] // No need to add StringEnumConverter here since it's already applied to the enum itself
        public AppGlobals.BoardSymbols[] GameBoard { get; set; }
    }
}

namespace AppGlobals
{
    [JsonConverter(typeof(StringEnumConverter))]
    public enum BoardSymbols
    {
        [EnumMember(Value = "X")]
        First = 'X',
        [EnumMember(Value = "O")]
        Second = 'O',
        [EnumMember(Value = "?")]
        EMPTY = '?'
    }
}

Затем NuGet Microsoft.AspNetCore.Mvc.NewtonsoftJson и в Startup.ConfigureServices вызов AddNewtonsoftJson():

services.AddMvc()
    .AddNewtonsoftJson();

Или, если вы предпочитаете использовать StringEnumConverter в глобальном масштабе:

services.AddMvc()
    .AddNewtonsoftJson(o => o.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()));

Обратите внимание на следующий комментарий из документов

Примечание: Если метод AddNewtonsoftJson недоступен, убедитесь, что вы установили Microsoft.AspNetCore. Mvc .Newtonsoft Json. Распространенной ошибкой является установка пакета Newtonsoft. Json вместо Microsoft.AspNetCore. Mvc .Newtonsoft Json.

Макетная скрипка здесь .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...