сериализация пользовательского перечисления c # Newtonsoft.json

#c# #json

#c# #json

Вопрос:

Мне нужно настроить способ Newtonsoft.Json сериализует объект, в частности, о типах перечислений. Учитывая пример класса, подобного этому:

     public class TestEnumClass
    {
        public Enum ReferencedEnum { get; set; }

        public string OtherProperty { get; set; }

        public StringSplitOptions OtherEnum { get; set; }
    }
  

Сериализация по умолчанию будет происходить таким образом:

 var testEnumClass = new TestEnumClass
                            {
                                ReferencedEnum = StringComparison.OrdinalIgnoreCase,
                                OtherProperty = "Something",
                                OtherEnum = StringSplitOptions.None
                            };

var serialized = JsonConvert.SerializeObject(testEnumClass, Formatting.Indented);
  

И сериализованная строка будет:

 {
  "ReferencedEnum": 5,
  "OtherProperty": "Something",
  "OtherEnum": 0
}
  

Здесь у меня 2 проблемы:

  1. Я не могу гарантировать, что порядок перечислений останется прежним (здесь я использую перечисления, включенные в фреймворк, но в моем проекте есть другие перечисления, производные от «: Enum»), поэтому я не могу сохранить число в качестве сериализованного значения перечисления.
  2. Во-вторых, и это более важно, это тот факт, что поле «ReferencedEnum» объявлено как «Enum», и в это поле может быть записано любое перечисление (ReferencedEnum = AnyEnum.AnyEnumValue). Это приводит к тому, что при десериализации значения мне нужно знать исходный тип перечисления (в примере это сравнение строк).

Я думал об использовании конвертера (производного от «: JsonConverter») и манипулировании тем, что записывается и читается. Результат, о котором я думал, был примерно таким:

 {
  "ReferencedEnum": { 
    "EnumType": "StringComparison",
    "EnumValue": "OrdinalIgnoreCase"
  },
  "OtherProperty": "Something",
  "OtherEnum": "StringSplitOptions.None"
}
  

Таким образом, десериализатор будет знать:

  1. для свойств «Enum» укажите исходный тип и строковое значение.
  2. для свойств «типизированного перечисления» (specific enum) — полный тип и значение.

Что я не могу абсолютно добавить, так это ссылку на конвертер в классе модели, подобную этой:

 [JsonConverter(typeof(EnumConverter))]
public Enum ReferencedEnum { get; set; }
  

И я бы также избегал наличия поля «$ type» в сериализованной строке (за исключением случаев, когда это единственное решение).

Кстати, я могу добавить общий атрибут, подобный этому:

 [IsEnum]
public Enum ReferencedEnum { get; set; }
  

Есть ли у кого-нибудь идеи о том, как я могу получить необходимый результат?

Спасибо!

Ответ №1:

Я столкнулся с той же проблемой и разработал пакет nuget с именем StringTypeEnumConverter, который решает ее. Вы можете проверить проект здесь. Использование будет таким же простым, как и любой другой конвертер.

Этот конвертер является производным от уже существующего «StringEnumConverter», который записывает строковое значение перечисления вместо его числового аналога. Я также добавил поддержку написания имени типа.

Результат будет выглядеть следующим образом: "StringSplitOptions.None" (это строковое значение, а не объект).

Обратите внимание, что этот конвертер следует применять как для записи, так и для чтения, поскольку результирующий json не будет совместим с другими программами чтения, не включающими этот конвертер.

Вам следует рассмотреть возможность использования этого пакета, только если вы не можете избежать использования перечислений в вашей модели. Я бы также посоветовал вам потратить время, чтобы проверить, могут ли (пользовательские) перечисления быть преобразованы в классы.

J.

Комментарии:

1. Спасибо, похоже, это решает мою проблему. Я согласен с вами, что лучше избегать использования перечислений при сериализации, но в моем случае я застрял с данной моделью.