Bagaimana cara menggabungkan array YAML?


113

Saya ingin menggabungkan array di YAML, dan memuatnya melalui ruby ​​-

some_stuff: &some_stuff
 - a
 - b
 - c

combined_stuff:
  <<: *some_stuff
  - d
  - e
  - f

Saya ingin memiliki array gabungan sebagai [a,b,c,d,e,f]

Saya menerima kesalahan: tidak menemukan kunci yang diharapkan saat mengurai pemetaan blok

Bagaimana cara menggabungkan array di YAML?


6
Mengapa Anda ingin melakukan ini dalam YAML daripada bahasa yang Anda gunakan untuk mengurai?
Patrick Collins

7
untuk mengeringkan duplikasi dalam file yaml yang sangat besar
lfender6445

4
Ini praktik yang sangat buruk. Anda harus membaca yamls secara terpisah, menempatkan array di Ruby, lalu menuliskannya kembali ke yaml.
sawa

74
Bagaimana mencoba menjadi praktik buruk yang kering?
krak3n

13
@PatrickCollins Saya menemukan pertanyaan ini mencoba mengurangi duplikasi di file .gitlab-ci.yml saya dan sayangnya saya tidak memiliki kendali atas parser yang digunakan GitLab CI :(
rink.attendant.

Jawaban:


41

Jika tujuannya adalah untuk menjalankan urutan perintah shell, Anda mungkin dapat melakukannya sebagai berikut:

# note: no dash before commands
some_stuff: &some_stuff |-
    a
    b
    c

combined_stuff:
  - *some_stuff
  - d
  - e
  - f

Ini sama dengan:

some_stuff: "a\nb\nc"

combined_stuff:
  - "a\nb\nc"
  - d
  - e
  - f

Saya telah menggunakan ini di gitlab-ci.yml(untuk menjawab komentar @ rink.attendant.6 pada pertanyaan).


Contoh kerja yang kami gunakan untuk mendukung requirements.txtmemiliki repos pribadi dari gitlab:

.pip_git: &pip_git
- git config --global url."https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.com".insteadOf "ssh://git@gitlab.com"
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- echo "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts

test:
    image: python:3.7.3
    stage: test
    script:
        - *pip_git
        - pip install -q -r requirements_test.txt
        - python -m unittest discover tests

use the same `*pip_git` on e.g. build image...

dimana requirements_test.txtberisi mis

-e git+ssh://git@gitlab.com/example/example.git@v0.2.2#egg=example


3
Pintar. Saya menggunakannya di pipeline Bitbucket kami sekarang. Terima kasih
Dariop

* Garis putus-putus tidak diperlukan di sini, hanya pipa di ujung yang cukup. * Ini adalah solusi yang lebih rendah karena ketika pekerjaan gagal pada pernyataan multi-baris yang sangat panjang, tidak jelas perintah mana yang gagal.
Mina Luke

1
@ MinaLuke, lebih rendah dibandingkan dengan apa? Tidak ada jawaban saat ini yang memberikan cara untuk menggabungkan dua item hanya dengan menggunakan yaml ... Selain itu, tidak ada pertanyaan yang menyatakan bahwa OP ingin menggunakan ini di CI / CD. Terakhir, saat ini digunakan di CI / CD, logging hanya bergantung pada CI / CD tertentu yang digunakan, bukan deklarasi yaml. Jadi, jika ada, CI / CD yang Anda maksud adalah yang melakukan pekerjaan dengan buruk. Yaml dalam jawaban ini valid, dan memecahkan masalah OP.
Jorge Leitao

@JorgeLeitao Saya rasa Anda menggunakannya untuk menggabungkan Rules. Bisakah Anda memberikan contoh gitlabci yang berfungsi? Saya mencoba sesuatu berdasarkan solusi Anda, tetapi selalu mendapatkan kesalahan validasi.
niels

@niels, saya telah menambahkan contoh dengan contoh gitlabci yang berfungsi. Perhatikan bahwa beberapa IDE menandai yaml ini sebagai tidak valid, meskipun sebenarnya tidak.
Jorge Leitao

26

Perbarui: 2019-07-01 14:06:12

  • Catatan : jawaban lain untuk pertanyaan ini telah diedit secara substansial dengan pembaruan pada pendekatan alternatif .
    • Jawaban yang diperbarui tersebut menyebutkan alternatif solusi dalam jawaban ini. Itu telah ditambahkan ke bagian Lihat juga di bawah.

Konteks

Posting ini mengasumsikan konteks berikut:

  • python 2.7.0
  • parser YAML python

Masalah

lfender6445 ingin menggabungkan dua atau lebih daftar dalam file YAML, dan membuat daftar yang digabungkan tersebut muncul sebagai satu daftar tunggal ketika diurai.

Solusi (Solusi)

Ini dapat diperoleh hanya dengan menetapkan jangkar YAML ke pemetaan, di mana daftar yang diinginkan muncul sebagai elemen turunan dari pemetaan. Ada peringatan untuk ini, bagaimanapun, (lihat "Jebakan" infra).

Pada contoh di bawah ini kami memiliki tiga pemetaan ( list_one, list_two, list_three) dan tiga jangkar dan alias yang merujuk ke pemetaan ini jika sesuai.

Ketika file YAML dimuat dalam program, kita mendapatkan daftar yang kita inginkan, tetapi mungkin memerlukan sedikit modifikasi setelah memuat (lihat perangkap di bawah).

Contoh

File YAML asli

  list_one: & id001
   - Sebuah
   - b
   - c

  list_two: & id002
   - e
   - f
   - g

  list_three: & id003
   - h
   - i
   - j

  list_combined:
      - * id001
      - * id002
      - * id003

Hasil setelah YAML.safe_load

## list_combined
  [
    [
      "Sebuah",
      "b",
      "c"
    ],
    [
      "e",
      "f",
      "g"
    ],
    [
      "h",
      "saya",
      "j"
    ]
  ]

Jebakan

  • pendekatan ini menghasilkan daftar daftar bersarang, yang mungkin bukan keluaran yang diinginkan secara persis, tetapi ini dapat diproses setelahnya menggunakan metode rata
  • yang peringatan biasa untuk jangkar YAML dan alias berlaku untuk keunikan dan ketertiban deklarasi

Kesimpulan

Pendekatan ini memungkinkan pembuatan daftar gabungan dengan menggunakan alias dan fitur jangkar dari YAML.

Meskipun hasil keluaran adalah daftar daftar bertingkat, ini dapat dengan mudah diubah menggunakan flattenmetode.

Lihat juga

Pendekatan alternatif yang diperbarui oleh @Anthon

Contoh flattenmetode


21

Ini tidak akan berhasil:

  1. penggabungan hanya didukung oleh spesifikasi YAML untuk pemetaan dan bukan untuk urutan

  2. Anda benar-benar mencampur hal-hal dengan memiliki kunci gabungan << diikuti oleh pemisah kunci / nilai :dan nilai yang merupakan referensi dan kemudian melanjutkan dengan daftar di tingkat indentasi yang sama

Ini bukan YAML yang benar:

combine_stuff:
  x: 1
  - a
  - b

Jadi contoh sintaks Anda bahkan tidak akan masuk akal sebagai proposal ekstensi YAML.

Jika Anda ingin melakukan sesuatu seperti menggabungkan beberapa array, Anda mungkin ingin mempertimbangkan sintaks seperti:

combined_stuff:
  - <<: *s1, *s2
  - <<: *s3
  - d
  - e
  - f

di mana s1, s2, s3adalah jangkar pada urutan (tidak ditampilkan) bahwa Anda ingin menggabungkan menjadi urutan baru dan kemudian memiliki d, edan f ditambahkan ke itu. Namun YAML menyelesaikan jenis kedalaman struktur ini terlebih dahulu, jadi tidak ada konteks nyata yang tersedia selama pemrosesan kunci penggabungan. Tidak ada larik / daftar yang tersedia untuk Anda di mana Anda dapat melampirkan nilai yang diproses (urutan berlabuh) ke.

Anda dapat mengambil pendekatan seperti yang diusulkan oleh @dreftymac, tetapi ini memiliki kerugian besar bahwa Anda perlu mengetahui urutan bersarang mana yang harus diratakan (yaitu dengan mengetahui "jalur" dari akar struktur data yang dimuat ke urutan induk), atau bahwa Anda secara rekursif menjalankan struktur data yang dimuat untuk mencari larik / daftar bersarang dan meratakan semuanya tanpa pandang bulu.

Solusi yang lebih baik IMO adalah menggunakan tag untuk memuat struktur data yang melakukan perataan untuk Anda. Hal ini memungkinkan untuk dengan jelas menunjukkan apa yang perlu diratakan dan apa yang tidak dan memberi Anda kendali penuh atas apakah perataan ini dilakukan selama pemuatan, atau dilakukan selama akses. Yang mana yang harus dipilih adalah soal kemudahan implementasi dan efisiensi waktu dan ruang penyimpanan. Ini adalah trade-off yang sama yang perlu dilakukan untuk menerapkan fitur kunci gabungan dan tidak ada solusi tunggal yang selalu terbaik.

Misalnya ruamel.yamlperpustakaan saya menggunakan brute force merge-dicts selama pemuatan saat menggunakan safe-loader, yang menghasilkan kamus gabungan yang merupakan dicts Python normal. Penggabungan ini harus dilakukan di muka, dan data duplikat (ruang tidak efisien) tetapi cepat dalam pencarian nilai. Saat menggunakan round-trip-loader, Anda ingin dapat membuang penggabungan yang tidak digabungkan, sehingga harus tetap dipisahkan. Diktekan seperti struktur data yang dimuat sebagai hasil dari pemuatan bolak-balik, hemat ruang tetapi aksesnya lebih lambat, karena perlu mencoba dan mencari kunci yang tidak ditemukan dalam dikt itu sendiri dalam penggabungan (dan ini tidak di-cache, jadi itu perlu dilakukan setiap saat). Tentu saja pertimbangan seperti itu tidak terlalu penting untuk file konfigurasi yang relatif kecil.


Berikut ini mengimplementasikan skema seperti penggabungan untuk daftar di python menggunakan objek dengan tag flatten yang dengan cepat muncul kembali menjadi item yang daftar dan diberi tag toflatten. Dengan menggunakan dua tag ini, Anda dapat memiliki file YAML:

l1: &x1 !toflatten
  - 1 
  - 2
l2: &x2
  - 3 
  - 4
m1: !flatten
  - *x1
  - *x2
  - [5, 6]
  - !toflatten [7, 8]

(penggunaan urutan aliran vs gaya blok benar-benar sewenang-wenang dan tidak memiliki pengaruh pada hasil yang dimuat).

Saat mengulangi item yang merupakan nilai untuk kunci m1ini "berulang" ke dalam urutan yang diberi tag toflatten, tetapi menampilkan daftar lain (alias atau tidak) sebagai satu item.

Salah satu cara yang mungkin dengan kode Python untuk mencapainya adalah:

import sys
from pathlib import Path
import ruamel.yaml

yaml = ruamel.yaml.YAML()


@yaml.register_class
class Flatten(list):
   yaml_tag = u'!flatten'
   def __init__(self, *args):
      self.items = args

   @classmethod
   def from_yaml(cls, constructor, node):
       x = cls(*constructor.construct_sequence(node, deep=True))
       return x

   def __iter__(self):
       for item in self.items:
           if isinstance(item, ToFlatten):
               for nested_item in item:
                   yield nested_item
           else:
               yield item


@yaml.register_class
class ToFlatten(list):
   yaml_tag = u'!toflatten'

   @classmethod
   def from_yaml(cls, constructor, node):
       x = cls(constructor.construct_sequence(node, deep=True))
       return x



data = yaml.load(Path('input.yaml'))
for item in data['m1']:
    print(item)

keluaran yang mana:

1
2
[3, 4]
[5, 6]
7
8

Seperti yang Anda lihat, Anda dapat melihat, dalam urutan yang perlu diratakan, Anda dapat menggunakan alias ke urutan yang diberi tag atau Anda dapat menggunakan urutan yang diberi tag. YAML tidak mengizinkan Anda melakukan:

- !flatten *x2

, yaitu menandai urutan berlabuh, karena ini pada dasarnya akan membuatnya menjadi struktur data yang berbeda.

Menggunakan tag eksplisit IMO lebih baik daripada melakukan keajaiban seperti pada kunci gabungan YAML <<. Jika tidak ada lagi Anda sekarang harus melalui rintangan jika Anda kebetulan memiliki file YAML dengan pemetaan yang memiliki kunci <<yang Anda tidak ingin bertindak seperti kunci gabungan, misalnya ketika Anda membuat pemetaan operator C ke deskripsinya dalam bahasa Inggris (atau bahasa alami lainnya).


9

Jika Anda hanya perlu menggabungkan satu item ke dalam daftar, Anda dapat melakukannya

fruit:
  - &banana
    name: banana
    colour: yellow

food:
  - *banana
  - name: carrot
    colour: orange

yang menghasilkan

fruit:
  - name: banana
    colour: yellow

food:
  - name: banana
    colour: yellow
  - name: carrot
    colour: orange

-4

Anda dapat menggabungkan pemetaan lalu mengubah kuncinya menjadi daftar, dengan ketentuan berikut:

  • jika Anda menggunakan jinja2 templating dan
  • jika pesanan barang tidak penting
some_stuff: &some_stuff
 a:
 b:
 c:

combined_stuff:
  <<: *some_stuff
  d:
  e:
  f:

{{ combined_stuff | list }}

Ada apa dengan jawaban ini? Saya tidak keberatan suara negatif jika mereka diperdebatkan. Saya akan menyimpan jawabannya untuk orang-orang yang dapat memanfaatkannya.
sm4rk0

3
Kemungkinan karena jawaban ini bergantung pada template jinja2, saat pertanyaan meminta untuk melakukannya di yml. jinja2 membutuhkan lingkungan Python, yang kontra produktif jika OP mencoba MENGERING. Selain itu, banyak alat CI / CD tidak menerima langkah pembuatan template.
Jorge Leitao

Terima kasih @JorgeLeitao. Itu masuk akal. Saya belajar YAML dan Jinja2 bersama-sama sambil mengembangkan buku pedoman dan template yang mungkin dan tidak dapat memikirkan satu sama lain tanpa yang lain
sm4rk0
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.