63

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

因为新的“服务器”对象只有“端口”属性,它会覆盖整个旧的“服务器”对象。

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

编辑

这是我在LiveScript中使用工作代码的确切意图:

config = 
  default: 
    ip: \192.168.1.5
    port: 2000
    name: \root 
    password: \root 
    db:
      name: \default
      location: \LA

  foo-database:~ -> @default `merge` do 
    ip: \11.11.11.11
    db:
      name: \my-foo 

  bar-database:~ -> @foo-database `merge` do 
    password: \1234 
    db:
      location: \SF

config.default 
# => {"ip":"192.168.1.5","port":2000,"name":"root","password":"root","db":{"name":"default","location":"LA"}}
config.foo-database  
# => {"ip":"11.11.11.11","port":2000,"name":"root","password":"root","db":{"name":"my-foo","location":"LA"}}
config.bar-database  
# => {"ip":"11.11.11.11","port":2000,"name":"root","password":"1234","db":{"name":"my-foo","location":"SF"}}
4

3 回答 3

50

不幸的是,你无法获得你想要实现的那种“继承”,因为 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
于 2013-01-06T23:36:25.143 回答
18

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

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

这只是一些额外的工作,并且比如果你想要的内容按照你的建议内置到 YAML 中稍微难读(我认为它也可以这样工作)。但总体还不错。

于 2014-02-25T19:47:23.770 回答
1

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

在.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}" "${@}"
}
于 2019-11-16T23:57:20.923 回答