OkaeriPoland / okaeri-configs

Simple Java/POJO config library written with love and Lombok
MIT License
77 stars 11 forks source link

Nested Object Serialization #21

Closed Silthus closed 2 years ago

Silthus commented 2 years ago

Describe the bug I have the following config and want to use the already registered serializer for the message text of the object:

public class ConfigTest extends OkaeriConfig {

    List<Message> messages = new ArrayList<>(List.of(
            new Message(text("Hi")),
            new Message(text("there!"))
    ));
}

@Data
public class Message {

    private final Component text;

    public Message(Component text) {
        this.text = text;
    }
}

final class MessageSerializer implements ObjectSerializer<Message> {

    @Override
    public boolean supports(Class<? super Message> type) {
        return Message.class.isAssignableFrom(type);
    }

    @Override
    public void serialize(Message object, SerializationData data) {
        data.add("text", object.getText());
    }

    @Override
    public Message deserialize(DeserializationData data, GenericsDeclaration generics) {
        return new Message(data.get("text", Component.class));
    }
}

final class MiniMessageComponentSerializer extends BidirectionalTransformer<String, Component> {

    private final MiniMessage parser = MiniMessage.miniMessage();

    @Override
    public GenericsPair<String, Component> getPair() {
        return new GenericsPair<>(GenericsDeclaration.of(String.class), GenericsDeclaration.of(Component.class));
    }

    @Override
    public Component leftToRight(final @NonNull String data, final @NonNull SerdesContext serdesContext) {
        return parser.deserialize(data);
    }

    @Override
    public String rightToLeft(final @NonNull Component data, final @NonNull SerdesContext serdesContext) {
        return parser.serialize(data);
    }
}

@PluginMain
public class TemplatePlugin extends JavaPlugin {

    @Getter
    @Accessors(fluent = true)
    private ConfigTest config;

    public TemplatePlugin() {
    }

    public TemplatePlugin(
            JavaPluginLoader loader, PluginDescriptionFile description, File dataFolder, File file) {
        super(loader, description, dataFolder, file);
    }

    @Override
    public void onEnable() {
        loadConfig();
    }

    private void loadConfig() {
        this.config = ConfigManager.create(ConfigTest.class, config -> config
                .withConfigurer(new YamlBukkitConfigurer(), new SerdesBukkit(), new SerdesCommons(), new ExamplePack())
                .withBindFile("config.yml")
                .saveDefaults()
                .load(true));
    }
}

To Reproduce See the following repository with a failing test: https://github.com/sVoxelDev/okaeri-nested-serialization-bug

Expected behavior The serializers should leverage already existing serializers.

dasavick commented 2 years ago

My memory was a little rusty but thanks to your example project I was able to figure this out again quickly. In the cases where inheritance occurs, intended serialization type shall be provided via SerializationData typed methods.

Replace:

data.add("text", object.getText());

With:

data.add("text", object.getText(), Component.class);

Also take a look for other typed methods, for collections, maps, etc.: https://github.com/OkaeriPoland/okaeri-configs/blob/master/core/src/main/java/eu/okaeri/configs/serdes/SerializationData.java

Note third section of typed add method:

    /**
     * Adds value to the serialization data under specific key.
     * Provided value is simplified using attached Configurer.
     * <p>
     * This method allows to narrow target simplification type
     * and is recommended to be used with non-primitive classes.
     * <p>
     * Specifying target simplification type allows to make sure
     * correct serializer is used, e.g. interface type instead
     * of some implementation type that would otherwise inferred.
     *
     * @param key       target key
     * @param value     target value
     * @param valueType type of value for simplification process
     * @param <T>       type of value
     */
    public <T> void add(@NonNull String key, Object value, @NonNull Class<T> valueType) {
        GenericsDeclaration genericType = GenericsDeclaration.of(valueType);
        this.add(key, value, genericType);
    }