¿Existe una sintaxis YAML para compartir parte de una lista o mapa?

94

Entonces, sé que puedo hacer algo como esto:

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

anotherlist: *sites

Y tienen sitelisty anotherlistambos contienen www.foo.comy www.bar.com. Sin embargo, lo que realmente quiero es anotherlistque contenga tambiénwww.baz.com , sin tener que repetir www.foo.comy www.baz.com.

Hacer esto me da un error de sintaxis en el analizador YAML:

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

anotherlist: *sites
  - www.baz.com

Simplemente usando anclas y alias, no parece posible hacer lo que quiero sin agregar otro nivel de subestructura, como:

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

anotherlist:
  - *sites
  - www.baz.com

Lo que significa que el consumidor de este archivo YAML debe conocerlo.

¿Existe una forma pura YAML de hacer algo como esto? ¿O tendré que usar algún procesamiento posterior a YAML, como implementar la sustitución de variables o la elevación automática de ciertos tipos de subestructura? Ya estoy haciendo ese tipo de posprocesamiento para manejar un par de otros casos de uso, por lo que no soy totalmente contrario a él. Pero mis archivos YAML serán escritos por humanos, no generados por máquinas, por lo que me gustaría minimizar la cantidad de reglas que mis usuarios deben memorizar además de la sintaxis YAML estándar.

También me gustaría poder hacer lo análogo con los mapas:

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

moresites: *sites
  Baz: www.baz.com

Hice una búsqueda a través de la especificación YAML y no pude encontrar nada, por lo que sospecho que la respuesta es simplemente "no, no puedes hacer esto". Pero si alguien tiene alguna idea, sería genial.


EDITAR: Dado que no ha habido respuestas, supongo que nadie ha detectado nada que yo no haya visto en la especificación YAML y que esto no se puede hacer en la capa YAML. Así que estoy abriendo la pregunta a la idea de posprocesar el YAML para ayudar con esto, en caso de que alguien encuentre esta pregunta en el futuro.

Ben
fuente
Nota: Este problema también se puede solucionar con el uso estándar de anclajes y alias en YAML. Consulte también: ¿Cómo fusionar matrices YAML?
dreftymac

Respuestas:

53

El tipo de clave de combinación es probablemente lo que desea. Utiliza una <<clave de mapeo especial para indicar fusiones, lo que permite utilizar un alias para un mapeo (o una secuencia de dichos alias) como inicializador para fusionar en un solo mapeo. Además, aún puede anular valores explícitamente o agregar más que no estaban presentes en la lista de combinación.

Es importante tener en cuenta que funciona con asignaciones, no con secuencias como su primer ejemplo. Esto tiene sentido cuando lo piensa, y su ejemplo parece que probablemente no necesita ser secuencial de todos modos. Simplemente cambiar los valores de la secuencia a las claves de asignación debería funcionar, como en el siguiente ejemplo (no probado):

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

Algunas cosas para notar. En primer lugar, dado que <<es una clave, solo se puede especificar una vez por nodo. En segundo lugar, cuando se utiliza una secuencia como valor, el orden es significativo. Esto no importa en el ejemplo aquí, ya que no hay valores asociados, pero vale la pena tenerlo en cuenta.

Kittemon
fuente
¡Ah gracias! Eso es muy útil. Sin embargo, es una pena que no funcione para secuencias. Tiene razón en que el orden no es importante para este ejemplo; lo que tengo es conceptualmente un conjunto, pero se asigna mucho más a una secuencia que a un mapeo. Y la estructura de lo que obtengo de esto es importante (por eso no quería simplemente agregar otra capa de anidación para fusionar mis estructuras), por lo que tener un mapeo para el cual necesito ignorar los valores (todos nulos) no realmente funciona.
Ben
3
No veo nada en él en la especificación oficial actual de YAML: yaml.org/spec/1.2/spec.html . Esa página no contiene la palabra "fusionar", ni el texto "<<", ni la frase "tipo de clave". Sin embargo, la sintaxis << funciona en el paquete yaml de Python. ¿Sabe dónde puedo encontrar más información sobre este tipo de funciones adicionales?
Ben
1
No está directamente en la especificación, se describe en el repositorio de etiquetas. Otros esquemas tiene una descripción general y un enlace. Además de las claves de combinación, también hay conjuntos y conjuntos ordenados; sin embargo, YAML considera conjuntos como un tipo de mapeo (por ejemplo, el ejemplo anterior podría implementarse como un conjunto). ¿Su idioma le permite intercambiar claves con valores en el mapeo resultante? Incluso si tiene que implementarlo usted mismo, creo que sería más limpio; al menos ya tendría todos los datos agrupados y su YAML sería estándar.
kittemon
Sin embargo, los conjuntos no son mapeos; una asignación es un conjunto de asociaciones clave-valor. Cuando estoy yaml.load(...)en Python, obtengo un diccionario como representación de un mapeo YAML. Sí, es fácil posprocesar eso en un conjunto, pero tengo que saber que eso sucedió (y la complejidad semántica al leer / escribir los archivos de configuración es mucho mayor si la regla es "los conjuntos se escriben como mapas con valores nulos" ). Dado que necesito un posprocesamiento entre yaml.load(...)los datos resultantes y su uso, ya sea que use <<o MERGE, probablemente me quedo con MERGE(que ya he implementado ahora).
Ben
2
Sí, encontré que !!setfunciona. Sin embargo, demasiada caligrafía oscura. Estos archivos están hechos para que los humanos puedan leer / escribir, por personas que no son necesariamente expertos en YAML. Las personas escribirán sus listas de sitios como listas YAML, luego querrán fusionarlas y tendrán que convertir todo en un conjunto Y recordar etiquetarlo explícitamente como un conjunto ... Tengo un par de otras publicaciones estandarizadas procesando cosas junto con de MERGEtodos modos. ¡Gracias por tu ayuda!
Ben
16

Como han señalado las respuestas anteriores, no hay soporte integrado para extender listas en YAML. Ofrezco otra forma de implementarlo usted mismo. Considera esto:

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

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

Esto se procesará en:

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

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

La idea es fusionar el contenido de una clave que termina con un '+' con la clave correspondiente sin un '+'. Implementé esto en Python y publiqué aquí .

¡Disfrutar!

Alexander Ryzhov
fuente
2
Nota: Este problema también se puede solucionar con el uso estándar de Anchors y Aliases en YAML. Consulte también: ¿Cómo fusionar matrices YAML?
dreftymac
11
¿Significa esto que este enfoque solo funciona con una herramienta separada que fusiona sitesy sites+. Me refiero a una herramienta que debe ser implementada por el usuario ya que este no es un yamlcomportamiento predeterminado .
stan0
7

(Responder a mi propia pregunta en caso de que la solución que estoy usando sea útil para cualquiera que busque esto en el futuro)

Sin una forma puramente YAML de hacer esto, voy a implementar esto como una "transformación de sintaxis" entre el analizador YAML y el código que realmente usa el archivo de configuración. Por lo tanto, mi aplicación principal no tiene que preocuparse en absoluto por ninguna medida de evitación de redundancia amigable para los humanos, y puede actuar directamente sobre las estructuras resultantes.

La estructura que voy a usar se ve así:

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

Que se transformaría en el equivalente de:

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

O, con mapas:

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

Se transformaría en:

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

Más formalmente, después de llamar al analizador YAML para obtener objetos nativos de un archivo de configuración, pero antes de pasar los objetos al resto de la aplicación, mi aplicación recorrerá el gráfico de objetos en busca de asignaciones que contengan la clave única MERGE. El valor asociado con MERGEdebe ser una lista de listas o una lista de mapas; cualquier otra subestructura es un error.

En el caso de la lista de listas, todo el mapa que contiene MERGEserá reemplazado por las listas secundarias concatenadas en el orden en que aparecieron.

En el caso de la lista de mapas, el mapa completo que contiene MERGEserá reemplazado por un solo mapa que contiene todos los pares clave / valor en los mapas secundarios. Cuando haya superposición en las claves, MERGEse utilizará el valor del mapa secundario que aparece en último lugar en la lista.

Los ejemplos dados anteriormente no son tan útiles, ya que podría haber escrito directamente la estructura que deseaba. Es más probable que aparezca como:

foo:
  MERGE:
    - *salt
    - *pepper

Permitiéndole crear una lista o mapa que contenga todo en los nodos salty que pepperse use en otros lugares.

(Sigo dando ese foo:mapa externo para mostrar que MERGEdebe ser la única clave en su mapeo, lo que significa que MERGEno puede aparecer como un nombre de nivel superior a menos que no haya otros nombres de nivel superior)

Ben
fuente
6

Para aclarar algo de las dos respuestas aquí, esto no se admite directamente en YAML para listas (pero es compatible con diccionarios, consulte la respuesta de kittemon).

como medidor
fuente
Nota: Este problema también se puede solucionar con el uso estándar de Anchors y Aliases en YAML. Consulte también: ¿Cómo fusionar matrices YAML?
dreftymac
5

Para aprovechar la respuesta de Kittemon, tenga en cuenta que puede crear asignaciones con valores nulos utilizando la sintaxis alternativa

foo:
    << : myanchor
    bar:
    baz:

en lugar de la sintaxis sugerida

foo:
    << : myanchor
    ? bar
    ? baz

Como la sugerencia de Kittemon, esto le permitirá usar referencias a los anclajes dentro del mapeo y evitar el problema de la secuencia. Me encontré con la necesidad de hacer esto después de descubrir que el componente Symfony Yaml v2.4.4 no registra la ? barsintaxis.

beef_boolean
fuente
lo que hace myanchorparece?
ssc