Existe sintaxe YAML para compartilhar parte de uma lista ou mapa?

94

Então, eu sei que posso fazer algo assim:

sitelist: &sites
  - www.foo.com
  - www.bar.com

anotherlist: *sites

E têm siteliste anotherlistambos contêm www.foo.come www.bar.com. No entanto, o que eu realmente quero é que anotherlista também contêm www.baz.com, sem ter que repetir www.foo.come www.baz.com.

Isso me dá um erro de sintaxe no analisador YAML:

sitelist: &sites
  - www.foo.com
  - www.bar.com

anotherlist: *sites
  - www.baz.com

Apenas usando âncoras e aliases, não parece possível fazer o que eu quero sem adicionar outro nível de subestrutura, como:

sitelist: &sites
  - www.foo.com
  - www.bar.com

anotherlist:
  - *sites
  - www.baz.com

O que significa que o consumidor deste arquivo YAML deve estar ciente disso.

Existe uma maneira pura do YAML de fazer algo assim? Ou terei que usar algum processamento pós-YAML, como a implementação da substituição de variável ou levantamento automático de certos tipos de subestrutura? Já estou fazendo esse tipo de pós-processamento para lidar com alguns outros casos de uso, então não sou totalmente avesso a isso. Mas meus arquivos YAML serão escritos por humanos, não gerados por máquina, então eu gostaria de minimizar o número de regras que precisam ser memorizadas por meus usuários em cima da sintaxe YAML padrão.

Eu também gostaria de poder fazer o mesmo com mapas:

namedsites: &sites
  Foo: www.foo.com
  Bar: www.bar.com

moresites: *sites
  Baz: www.baz.com

Fiz uma pesquisa nas especificações YAML e não consegui encontrar nada, então suspeito que a resposta seja apenas "não, você não pode fazer isso". Mas se alguém tiver alguma ideia, isso seria ótimo.


EDIT: Como não houve respostas, estou presumindo que ninguém descobriu nada que eu não tenha na especificação YAML e que isso não pode ser feito na camada YAML. Portanto, estou abrindo a questão para a ideia de pós-processamento do YAML para ajudar com isso, caso alguém encontre essa questão no futuro.

Ben
fonte
Observação: esse problema também pode ser resolvido com o uso padrão de âncoras e aliases em YAML. Consulte também: Como mesclar arrays YAML?
dreftymac

Respostas:

53

O tipo de chave de mesclagem é provavelmente o que você deseja. Ele usa uma <<chave de mapeamento especial para indicar mesclagens, permitindo que um alias para um mapeamento (ou uma sequência de tais aliases) seja usado como um inicializador para mesclar em um único mapeamento. Além disso, você ainda pode substituir valores explicitamente ou adicionar mais que não estavam presentes na lista de mesclagem.

É importante observar que ele funciona com mapeamentos, não com sequências como seu primeiro exemplo. Isso faz sentido quando você pensa sobre isso e seu exemplo parece que provavelmente não precisa ser sequencial de qualquer maneira. Basta alterar seus valores de sequência para as chaves de mapeamento, como no exemplo a seguir (não testado):

sitelist: &sites
  ? www.foo.com  # "www.foo.com" is the key, the value is null
  ? www.bar.com

anotherlist:
  << : *sites    # merge *sites into this mapping
  ? www.baz.com  # add extra stuff

Algumas coisas a serem observadas. Em primeiro lugar, como <<é uma chave, ela só pode ser especificada uma vez por nó. Em segundo lugar, ao usar uma sequência como valor, a ordem é significativa. Isso não importa no exemplo aqui, pois não existem valores associados, mas vale a pena estar atento.

gatinho
fonte
Ah obrigado! Isso é muito útil. É uma pena que não funcione para sequências, no entanto. Você está certo que a ordem não é importante para este exemplo; o que tenho é conceitualmente um conjunto, mas que mapeia muito mais de perto uma sequência do que um mapeamento. E a estrutura do que obtenho com isso importa (é por isso que eu não queria apenas adicionar outra camada de aninhamento para mesclar minhas estruturas), portanto, ter um mapeamento para o qual preciso ignorar os valores (todos nulos) não realmente funciona.
Ben
3
Não vejo nada sobre ele na especificação YAML oficial atual: yaml.org/spec/1.2/spec.html . Essa página não contém a palavra "mesclar", nem o texto "<<", nem a frase "tipo de chave". A sintaxe << funciona no pacote Python yaml. Você sabe onde posso encontrar mais informações sobre esses tipos de recursos extras?
Ben
1
Não está diretamente na especificação, é descrito no repositório de tags. Outros esquemas possuem uma descrição geral e um link. Além das chaves de mesclagem, também existem conjuntos e conjuntos ordenados; entretanto, o YAML considera os conjuntos como um tipo de mapeamento (por exemplo, o exemplo acima pode ser implementado como um conjunto). Seu idioma permite que você troque chaves com valores no mapeamento resultante? Mesmo que você mesmo tenha que implementar isso, acho que seria mais limpo; você teria pelo menos todos os dados já agrupados e seu YAML seria o padrão.
gatinho,
Os conjuntos não são mapeamentos; um mapeamento é um conjunto de associações de valor-chave. Quando estou yaml.load(...)em Python, recebo um dicionário como a representação de um mapeamento YAML. Sim, é fácil pós-processar isso em um conjunto, mas preciso saber o que aconteceu (e a complexidade semântica ao ler / gravar os arquivos de configuração é muito maior se a regra for "conjuntos são escritos como mapas com valores nulos" ) Dado que preciso de um pós-processamento entre yaml.load(...)e usando os dados resultantes, quer eu use <<ou MERGE, provavelmente irei continuar MERGE(que já implementei agora).
Ben
2
Sim, eu achei que !!setfunciona. Muito boilerplate obscuro embora. Esses arquivos são feitos para serem lidos / gravados por humanos, por pessoas que não são necessariamente especialistas em YAML. As pessoas vão escrever suas listas de sites como listas YAML, em seguida, querem mesclá-los e ter que converter tudo para um conjunto E lembre-se de marcá-lo explicitamente como um conjunto ... Eu tenho alguns outros post- padronizados processar coisas junto com de MERGEqualquer maneira. Obrigado pela sua ajuda!
Ben
16

Como as respostas anteriores indicaram, não há suporte integrado para estender listas em YAML. Estou oferecendo mais uma maneira de implementá-lo sozinho. Considere isto:

defaults: &defaults
  sites:
    - www.foo.com
    - www.bar.com

setup1:
  <<: *defaults
  sites+:
    - www.baz.com

Isso será processado em:

defaults:
  sites:
    - www.foo.com
    - www.bar.com

setup1:
  sites:
    - www.foo.com
    - www.bar.com
    - www.baz.com

A ideia é mesclar o conteúdo de uma tecla que termina com um '+' à tecla correspondente sem um '+'. Implementei isso em Python e publiquei aqui .

Aproveitar!

Alexander Ryzhov
fonte
2
Observação: esse problema também pode ser resolvido com o uso padrão de âncoras e aliases em YAML. Consulte também: Como mesclar arrays YAML?
dreftymac
11
Isso significa que essa abordagem só funciona com uma ferramenta separada que mescla sitese sites+. Quero dizer uma ferramenta que deve ser implementada pelo usuário, pois este não é um yamlcomportamento padrão ?
stan0
7

(Respondendo minha própria pergunta caso a solução que estou usando seja útil para alguém que pesquisar por isso no futuro)

Sem uma maneira pura de YAML de fazer isso, vou implementar isso como uma "transformação de sintaxe" situada entre o analisador YAML e o código que realmente usa o arquivo de configuração. Portanto, meu aplicativo principal não precisa se preocupar com nenhuma medida de prevenção de redundância amigável e pode apenas agir diretamente nas estruturas resultantes.

A estrutura que vou usar é assim:

foo:
  MERGE:
    - - a
      - b
      - c
    - - 1
      - 2
      - 3

Que seria transformado no equivalente a:

foo:
  - a
  - b
  - c
  - 1
  - 2
  - 3

Ou, com mapas:

foo:
  MERGE:
    - fork: a
      spoon: b
      knife: c
    - cup: 1
      mug: 2
      glass: 3

Seria transformado em:

foo:
  fork: a
  spoon: b
  knife: c
  cup: 1
  mug: 2
  glass: 3

Mais formalmente, depois de chamar o analisador YAML para obter objetos nativos de um arquivo de configuração, mas antes de passar os objetos para o resto do aplicativo, meu aplicativo percorrerá o gráfico de objetos procurando mapeamentos contendo a chave única MERGE. O valor associado MERGEdeve ser uma lista de listas ou uma lista de mapas; qualquer outra subestrutura é um erro.

No caso de lista de listas, todo o mapa que contém MERGEserá substituído pelas listas secundárias concatenadas na ordem em que apareceram.

No caso da lista de mapas, todo o mapa contendo MERGE será substituído por um único mapa contendo todos os pares chave / valor nos mapas filho. Onde houver sobreposição nas chaves, o valor do mapa filho que ocorre por último na MERGElista será usado.

Os exemplos dados acima não são tão úteis, já que você poderia apenas ter escrito a estrutura desejada diretamente. É mais provável que apareça como:

foo:
  MERGE:
    - *salt
    - *pepper

Permitindo que você crie uma lista ou mapa contendo tudo em nós saltepepper sendo usado em outro lugar.

(Eu continuo fornecendo aquele foo:mapa externo para mostrar que MERGEdeve ser a única chave em seu mapeamento, o que significa que MERGEnão pode aparecer como um nome de nível superior a menos que não haja outros nomes de nível superior)

Ben
fonte
6

Para esclarecer algo sobre as duas respostas aqui, isso não é suportado diretamente no YAML para listas (mas é compatível com dicionários, consulte a resposta do kittemon).

asmeurer
fonte
Observação: esse problema também pode ser resolvido com o uso padrão de âncoras e aliases em YAML. Consulte também: Como mesclar arrays YAML?
dreftymac
5

Para pegar carona na resposta de Kittemon, observe que você pode criar mapeamentos com valores nulos usando a sintaxe alternativa

foo:
    << : myanchor
    bar:
    baz:

em vez da sintaxe sugerida

foo:
    << : myanchor
    ? bar
    ? baz

Como a sugestão de Kittemon, isso permitirá que você use referências a âncoras no mapeamento e evite o problema de sequência. Descobri que precisava fazer isso depois de descobrir que o componente Symfony Yaml v2.4.4 não reconhece a ? barsintaxe.

beef_boolean
fonte
o que myanchorparece?
ssc