Свернуть single-child bean в SnakeYAML

У меня есть тип, который представляет собой простую оболочку вокруг Map<String, Integer>, которую я выгружаю в YAML с помощью SnakeYAML.

Например:

class Flags {
    private final Map<String, Boolean> _flags = new HashMap<>();
    Boolean get(String flag) {
        return _flags.containsKey(flag) ? _flags.get(flag) : false;
    }
    boolean put(String flag, Boolean value) {
        return _flags.put(flag, value);
    }
}

В настоящее время я использую DumperOptions с allowReadOnlyProperties = true и BeanAccess из FIELD, чтобы правильно сбросить этот класс. Когда я помещаю в содержащий класс, я получаю dumpAsMap YAML следующим образом:

flags:
    _flags: {}

Я бы хотел, чтобы SnakeYAML сбросил это вместо этого:

flags: {}

Как я могу выполнить это сглаживание одноэлементного слоя? Поскольку внутренняя переменная является частной, а тип упаковки должен эффективно действовать как обернутый тип, имеет смысл сериализовать тип упаковки, как если бы он был обернутым типом.

По сути, я хочу, чтобы тип упаковки был сериализован , как если бы это был тип оболочки. Нет и никогда не будет других переменных, которые необходимо сериализовать в этом классе вне обернутая переменная. Я открыт для изменения объявления типа типа упаковки, но тип упаковки должен остаться для моего варианта использования.

Полный исходный код для работоспособного варианта воспроизведения:

import org.yaml.snakeyaml.*;
import org.yaml.snakeyaml.introspector.*;
import java.util.*;

public class Example {
    private final Flags flags = new Flags();
    public Flags getFlags() { return flags; }

    public static void main(String[] args) {
        DumperOptions options = new DumperOptions();
        options.setAllowReadOnlyProperties(true);
        Yaml yaml = new Yaml(options);
        yaml.setBeanAccess(BeanAccess.FIELD);
        System.out.println(yaml.dumpAsMap(new Example()));
    }
}

class Flags {
    private final Map<String, Boolean> _flags = new HashMap<>();
    Boolean get(String flag) {
        return _flags.containsKey(flag) ? _flags.get(flag) : false;
    }
    boolean put(String flag, Boolean value) {
        return _flags.put(flag, value);
    }
}

person CAD97    schedule 21.11.2016    source источник


Ответы (1)


Что-то вроде этого может сработать (не тестировал код):

class FlagsRepresenter extends Representer {
    public FlagsRepresenter() {
        this.representers.put(Flags.class, new RepresentFlags());
    }

    private class RepresentFlags implements Represent {
        public Node representData(Object data) {
            // going the hacky, painful way of accessing a private field here.
            // YMMV.
            try {
                final Field f = data.getClass().getDeclaredField("_flags");
                f.setAccessible(true);
                final Map<String, Boolean> inner =
                        (Map<String, Boolean>) f.get(data);
                return representMapping(Tag.MAP, inner, null);
            } catch (final Exception ignored) {
                // will not occur as long as field _flags exists and has the
                // expected type.
                return null;
            }

        }
    }
}

Используйте это так:

Yaml yaml = new Yaml(new FlagsRepresenter(), new DumperOptions());
person flyx    schedule 21.11.2016
comment
Я бы хотел, чтобы это было возможно без написания пользовательского Represent для каждого типа оболочки, но, похоже, это лучший способ на данный момент. Замечание о десериализации сделало бы это идеальным ответом. - person CAD97; 21.11.2016
comment
Для десериализации легко адаптировать пример кода из документация, как я сделал для представления. - person flyx; 21.11.2016
comment
И вот чего я не смог найти. Спасибо! - person CAD97; 21.11.2016