如何使用 YAML 配置文件实例化带有对象参数的新对象?

How to instantiate new objects with object parameters using YAML config file?

我正在尝试使用配置文件来允许用户选择实现某些抽象 class 或接口的具体聚类算法。每种算法的先决条件输入可能略有不同。一个概念性的小例子:

两者的共同点在摘要中定义class。

为了使事情更通用,我编了一个虚拟示例。假设我有一个 Configuration class 将被传递给各种“引擎”或“驱动程序”以根据生成的 Configuration 实例完成工作。

public class Configuration {

    private AbstractAlgorithm algo;
    private AbstractParameter param1;
    private ConcreteEnum.Enum1 param2;
    private ConcreteEnum.Enum2 param3;
   
    // getters and setters...

然后我有 classes

abstract class AbstractAlgorithm{
    AbstractParameter abstractParam;
    public void setParam(AbstractParam p) {
        this.abstractParam = p;
    }

    public RandomObject runAlgo(DataObject data);
}

ConcreteAlgorithm

public class ConcreteAlgorithm extends AbstractAlgorithm {

    ConcreteEnum.enum1 concreteParam1;
    ConcreteEnum.enum2 concreteParam2;

    public ConcreteAlgorithm(ConcreteParameter p1, ConcreteParameter p2) {
        this.concreteParam1 = p1;
        this.concreteParam2 = p2;
    }

    public abstract void runAlgo(DataObject data) {
        if (abstractParam == null) {
            throw new IllegalStateException();
        } else {
        // do calculations based on enums and abstractParam value
        }
    }

我的问题是: 随附的 .yaml 文件看起来如何,以便它可以被解析为实例化的 Configuration 对象,可以传递在使用它的东西上?

与序列化一样,您的 classes 应该有一个不带参数的构造函数:

abstract class AbstractAlgorithm {
    public AbstractParameter abstractParam;
}

public class ConcreteAlgorithm extends AbstractAlgorithm {
    public ConcreteEnum.Enum1 param1;
    public ConcreteEnum.Enum2 param2;
}

除了字段之外,他们有什么界面与这个问题无关。我创建了这些字段 public ,因为当你进行反序列化时,它们没有意义,除非你想在 setter 中处理给定的值,在这种情况下你会使用 setter。

现在定义了这些类型,加载将按如下方式进行:

Constructor constructor = new Constructor();
constructor.addTypeDescription(
    new TypeDescription(ConcreteAlgorithm.class, new Tag("!concrete")));
yaml = new Yaml(constructor);
AbstractAlgorithm = yaml.loadAs(someInput, AbstractAlgorithm.class);

加载的 YAML 将如下所示:

--- !concrete
abstractParam: spam
param1: egg
param2: sausage

--- 指令结束标记 ,它开始文档的内容并且是可选的。我们在这里使用它来明确以下标记适用于文档的根节点。

!concrete 是根节点的标记。由于我们使用 SnakeYAML 注册它,它将导致 SnakeYAML 从该文件构造一个 ConcreteAlgorithm 对象。

以下键值对为 ConcreteAlgorithm 的每个字段赋值,包括继承的字段。 SnakeYAML 将根据使用的 classes 的性质处理字段值 – AbstractParameter 可能希望在 YAML 文件中有一个嵌套映射,如果它是 class。如果 AbstractParameter 是另一个抽象 class 并且有 ConcreteParameter 作为实现,你会做

--- !concrete
abstractParam: !cp
  droggel: jug
param1: egg
param2: sausage

并将 !cp 注册为 ConcreteParameter 的标签。包含键 droggel 的嵌套映射将生成 ConcreteParameter 的实例并分配给字段 abstractParam.

由于您可以在任何级别上提供标签,因此您可以在将 YAML 加载到的结构中的任何位置嵌套带有抽象 class 类型的字段。