YAML中更复杂的继承?

时间:2013-01-06 17:54:23

标签: inheritance yaml

YAML有继承权。我见过的最明显的例子是:http://blog.101ideas.cz/posts/dry-your-yaml-files.html

我需要更复杂的东西:我需要覆盖对象的对象的属性。这是一个例子:

database: &default
  server:
    ip: 192.168.1.5
    port: 2000
  db_name: test
  user: 
    name: root
    password: root

# database foo differs from default by only its port and user password
foo_database:
  <<: *default
  server:
    port: 2001
  db_name: foo
  user:
    password: foo_root

我想得到这个结果:

foo_database.server.ip -> 192.168.1.5
foo_database.server.port -> 2001
foo_database.db_name -> foo
foo_database.user.name -> root
foo_database.user.password -> foo_root

但如果你这样声明,你会得到这些属性不正确(根据预期值):

foo_database.server.ip -> will be None
foo_database.user.name -> will be None

因为新的“server”对象只有“port”属性,它会覆盖整个旧的“server”对象。

如何获得我想要实现的继承?

3 个答案:

答案 0 :(得分:31)

不幸的是,你不能得到你想要实现的那种“继承”,因为YAML的“继承”更像是一种“合并哈希”。

在您使用*default别名时扩展您的配置,您有:

foo_database:
  server:
    ip: 192.168.1.5
    port: 2000
  db_name: test
  user: 
    name: root
    password: root

如果你之后使用具有相同键的哈希,它们将完全覆盖之前声明的哈希值,留下你(原谅格式化):

foo_database:

<击>

<击>
  server:
    ip: 192.168.1.5
    port: 2000
  db_name: test
  user: 
   name: root
   password: root  

<击>

  server:
    port: 2001
  db_name: foo
  user:
    password: foo_root

因此,在您的情况下,似乎由于配置不是完全相同,使用锚点和别名来干扰您的配置可能不是正确的方法。

以下有关此问题的更多参考资料:

修改

如果您真的想,我认为您可以按照以下方式重新配置您的YAML以获得您想要的内容,但在您的情况下,我会说额外的混淆是不值得的:

server_defaults: &server_defaults
  ip: 192.168.1.5
  port: 2000

user_defaults: &user_defaults
  name: root
  password: root

database: &default
  server:
    <<: *server_defaults
  db_name: test
  user: 
    <<: *user_defaults

foo_database:
  <<: *default
  server:
    <<: *server_defaults
    port: 2001
  db_name: foo
  user:
    <<: *user_defaults
    password: foo_root

答案 1 :(得分:9)

这个怎么样?使用多个锚点。

database: &default
  server: &server
    ip: 192.168.1.5
    port: 2000
  db_name: test
  user: &user
    name: root
    password: root

foo_database:
  <<: *default
  server:
    << : *server
    port: 2001
  db_name: foo
  user:
    << : *user
    password: foo_root

这只是一个额外的工作,并且比你想要的内容更难以阅读,如你所建议的那样(我认为它也可以这样工作)。但总体来说还不错。

答案 2 :(得分:1)

针对此类问题,我创建了一个工具:jq-front。 通过使用yq + jq-fr​​ont,可以通过稍微修改输入来实现。

in.yaml:

$local: 
  database_default:
    server:
      ip: 192.168.1.5
      port: 2000
    db_name: test
    user: 
      name: root
      password: root

# database foo differs from default by only its port and user password
foo_database:
  $extends: [ database_default ]
  server:
    port: 2001
  db_name: foo
  user:
    password: foo_root

您可以通过以下命令行处理此文件。

$ yq . -j in.yaml | jq-front  | yq . -y

然后您将得到想要的输出。

foo_database:
  server:
    ip: 192.168.1.5
    port: 2001
  db_name: foo
  user:
    name: root
    password: foo_root

注意:jq-fr​​ont非常慢。在我的机器上,该命令花费了2.5秒,这对我来说并没有太大关系,因为系统配置可以读取一次,并且程序的其余部分仅使用转换后的文件。

注意:如果使用docker + bash,则通过docker安装jq-fr​​ont会容易得多。您只需要将以下功能添加到.bashrc或由其来源的文件中。

function jq-front() {
  docker run --rm -i \
    -v /:/var/lib/jf \
    -e JF_PATH_BASE="/var/lib/jf" \
    -e JF_PATH="${JF_PATH}" \
    -e JF_DEBUG=${JF_DEBUG:-disabled} \
    -e JF_CWD="$(pwd)" \
    dakusui/jq-front:"${JF_DOCKER_TAG:-latest}" "${@}"
}