YamlDotNet: как обращаться с !!set

Я пытаюсь десериализовать некоторый YAML, который имеет !!set в строке, созданной SnakeYaml при сериализации Java HashSet. Различные универсальные типы сериализуются, например, HashSet и с пользовательским типом HashSet.

Пример YAML:

holidays: !!set
    ? DDMMYYYY: 25/12/2042
      MMDDYYYY: 12/25/2042
      date:
        chronology: &id001
          calendarType: iso8601
          id: ISO
        dayOfMonth: 25
        dayOfWeek: THURSDAY
        dayOfYear: 359
        era: CE
        leapYear: false
        month: DECEMBER
        monthValue: 12
        year: 2042
      serialValue: 52225
    : null

Сначала я получаю исключение:

Дополнительная информация: не удалось загрузить файл или сборку «2002: набор» или одну из ее зависимостей. Система не может найти указанный файл.

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

{"tag:yaml.org,2002:set", typeof (HashSet<object>)}

Затем я получаю исключение:

В YamlDotNet.dll произошло первое случайное исключение типа «YamlDotNet.Core.YamlException». «SequenceStart» получил «MappingStart» (строка: 4, столбец: 23, Idx: 108).

Я бы подумал, что обработка наборов является очень распространенным требованием для YAML, но я не могу понять, как это исправить.

Может кто подскажет как лечить?


person Chill    schedule 24.09.2015    source источник


Ответы (1)


Проблема в том, что HashSet<T> не реализует IDictionary<TKey, TValue>, а затем десериализуется как последовательность, а не отображение.

Вам нужно будет создать собственную реализацию набора, возможно, расширив HashSet<T> и внедрив IDictionary<T, object> следующим образом:

public class YamlSet<T> : HashSet<T>, IDictionary<T, object>
{
    void IDictionary<T, object>.Add(T key, object value)
    {
        Add(key);
    }

    object IDictionary<T, object>.this[T key]
    {
        get
        {
            throw new NotImplementedException();
        }
        set
        {
            Add(key);
        }
    }
    // ...
}

Есть и другие элементы IDictionary<T, object>, которые вам придется реализовать, но это те, которые необходимы для работы десериализации.

см. полностью рабочий пример здесь

person Antoine Aubry    schedule 25.09.2015
comment
Спасибо, Антуан. Я попытался это сделать, и он получил исходное исключение, но затем выдал System.InvalidCastException... Объект должен реализовать IConvertible. Затем я изменил YamlSet для реализации IConvertible. Кажется, требуется метод ToType (Тип преобразования типа, провайдер IFormatProvider). КонвертацияType здесь — это System.Collections.ISet типа, которого я ожидал. Я пробовал разные вещи в этом методе, например, возвращать это, converType, но это дает исключение приведения. Не могли бы вы объяснить, что здесь нужно делать? Был бы очень признателен. - person Chill; 01.10.2015
comment
Вы можете либо заставить YamlSet<T> реализовать интерфейс ISet, либо использовать TypeConverter для регистрации преобразователя, который может преобразовать YamlSet<T> в ISet. - person Antoine Aubry; 02.10.2015
comment
Извините, Антуан, мне кажется, я что-то упускаю... YamlSet уже расширяет HashSet, который реализует ISet, поэтому я думаю, что изменение YamlSet для реализации ISet ничего не даст. Я попытался зарегистрировать преобразователь типов, который принимает{return type == typeof(YamlSet‹object›);}, но я не уверен, что добавить в ReadYaml. Я просмотрел это и добавил: public объект ReadYaml (парсер IParser, тип типа) {parser.MoveNext(); var value = ((Scalar)parser.Current).Value; return new HashSet‹object›{value}; } ... - person Chill; 02.10.2015
comment
... но затем я получаю сообщение об ошибке, в котором говорится, что объект должен реализовать IConvertible. Какие-либо предложения? - person Chill; 02.10.2015