【问题标题】:How to instantiate new objects with object parameters using YAML config file?如何使用 YAML 配置文件使用对象参数实例化新对象?
【发布时间】:2021-08-05 21:27:20
【问题描述】:

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

  • KMedoids 只需要用户设置k 即可。
  • SpectralClustering 还需要 k,但还需要其他一些东西,例如 Enums 表示图的类型,以及在其内部计算中使用的拉普拉斯算子类型。

两者的共同点都定义在一个抽象类中。

为了让事情更笼统,我做了一个虚拟的例子。假设我有一个 Configuration 类,该类将被传递给某种“引擎”或“驱动程序”,以根据生成的 Configuration 实例完成一项工作。

public class Configuration {

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

那我上课了

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 对象,该对象可以传递给使用它的对象? p>

【问题讨论】:

  • 这是一个相当宽泛的问题,对于 SO 来说可能过于宽泛。我想到的一种通用方法是算法的具体类型必须在 yaml 配置中进行编码(例如,通过字段type),并且每个实现类都有一个它“监听”的type。下一步是获取抽象类的所有具体实现,然后在读取 yaml 时,找到属于该类型的具体实现。
  • @Turing85 您介意将其扩展为答案吗?看起来您确实有一个想法,但我很难将您的评论拼凑成可操作的建议(可能是因为我上周刚刚学习了 YAML 语法)。
  • 如果您的问题是关于 YAML,请使用yaml 进行标记,而不仅仅是snakeyaml。熟悉多个 YAML 实现的人更可能遵循通用标签而不是某些特定实现的标签。
  • sigma 必须与implementation 处于同一缩进级别,因为它似乎是同级节点。 implementation 不能同时具有标量值 GaussianSimilarity 和嵌套映射。至于其他错误,请提出一个包含minimal, reproducible example 的新问题。得到一个广泛的答案后,不要将你的广泛问题重塑为具体问题。
  • 由于sigma 错误只是一个拼写错误并且与所讨论的问题无关,我认为最好撤消您的编辑。 StackOverflow 有很多关于如何解决 mapping values are not allowed here 的问题,所以我们可以在这里提出关于反序列化专业化/实现类的问题。

标签: java algorithm config snakeyaml


【解决方案1】:

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

abstract class AbstractAlgorithm {
    public AbstractParameter abstractParam;
}

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

他们除了字段之外的任何接口都与这个问题无关。我公开了这些字段,因为当你进行反序列化时,它们并没有真正的意义,除非你想在 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 将根据使用的类的性质处理字段值——AbstractParameter 可能希望在 YAML 文件中具有嵌套映射(如果它是一个类)。如果AbstractParameter 是另一个抽象类并且有ConcreteParameter 作为实现,你会这样做

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

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

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

【讨论】:

  • 谢谢。我刚刚辞职,只是将 YAML 作为地图阅读,并为对象生成制作了一堆 if 语句。给我一些时间根据您的回答制定解决方案,然后我会接受。
  • 在过去的几天里,我一直在回顾您关于 snayaml 的许多答案并阅读文档。我想我已经取得了进展,但我似乎仍然被传递参数所绊倒,这些参数是具有自己参数的类。也许这与我不完全理解的“注册标签”有关。我已经编辑了我的问题以包含一个简单但不平凡的工作示例。也许它会说明我缺乏理解的地方。
  • 我还尝试实例化我想要的对象,然后转储它们以查看 yaml 的样子。
  • “注册一个标签”只是意味着你告诉 SnakeYaml “看我想使用!foo 而不是!!com.ara.jcluster.algorithms.SpectralClustering,因为那太长了”。
  • 谢谢,这个问题与您对我的问题的评论有关,即sigma 需要与实施处于同一水平。如果您想相应地编辑您的答案,我会接受。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-13
  • 1970-01-01
  • 2013-04-19
  • 1970-01-01
相关资源
最近更新 更多