ฉันจะรวมไฟล์ YAML ไว้ในไฟล์อื่นได้อย่างไร


288

ดังนั้นฉันจึงมีไฟล์ YAML สองไฟล์ "A" และ "B" และฉันต้องการให้เนื้อหาของ A ถูกแทรกภายใน B ทั้งคู่แต่งงานลงในโครงสร้างข้อมูลที่มีอยู่เช่นอาร์เรย์หรือเป็นลูกขององค์ประกอบเช่นค่า สำหรับรหัสแฮชที่แน่นอน

เป็นไปได้ทั้งหมดหรือไม่ อย่างไร? ถ้าไม่มีพอยน์เตอร์ใด ๆ สำหรับการอ้างอิงเชิงบรรทัด?



1
ฉันเพิ่งชนกับHiYaPyCoสำหรับ Python ที่ทำสิ่งนี้ คุณสามารถรวมไฟล์ YAML ต่างๆเข้าด้วยกัน เป็นโมดูล Python ที่ดีมากที่ควรค่าแก่การรู้
nowox

คำตอบ:


326

ไม่ YAML ไม่รวมคำสั่ง "นำเข้า" หรือ "รวม" ใด ๆ


8
คุณสามารถสร้างตัวจัดการ!! <filename>
คลา

5
@ คลาร์กแวนแน่ใจ แต่การสร้างนั้นจะเป็น "นอก" ภาษา YAML
jameshfisher

2
ตอนนี้เป็นไปได้ ฉันได้เพิ่มคำตอบด้านล่าง ... หวังว่าจะช่วยได้
daveaspinall

1
หากคุณกำลังใช้ Rails คุณสามารถแทรก <% = 'fdsa fdsa'%> ไวยากรณ์ของ ERB และมันจะใช้ได้
gleenn

9
ฉันคิดว่าคำตอบนี้ควรใช้ถ้อยคำใหม่เป็น "ไม่ YAML มาตรฐานไม่รวมฟังก์ชั่นนี้อย่างไรก็ตามการใช้งานจำนวนมากให้ส่วนขยายที่จะทำเช่นนั้น"
Franklin Yu

113

คำถามของคุณไม่ได้ถามสำหรับการแก้ปัญหาหลาม แต่ที่นี่เป็นหนึ่งในการใช้PyYAML

PyYAML อนุญาตให้คุณแนบตัวสร้างแบบกำหนดเอง (เช่น!include) เข้ากับตัวโหลด YAML ฉันได้รวมไดเรกทอรีรูทที่สามารถตั้งค่าได้เพื่อให้โซลูชันนี้รองรับการอ้างอิงไฟล์แบบสัมพัทธ์และแบบสัมบูรณ์

โซลูชันระดับคลาส

นี่คือโซลูชันแบบคลาสซึ่งหลีกเลี่ยงตัวแปรรูทโกลบอลของการตอบกลับดั้งเดิมของฉัน

ดูส่วนสำคัญนี้สำหรับโซลูชัน Python 3 ที่คล้ายกันและมีประสิทธิภาพยิ่งขึ้นซึ่งใช้ metaclass เพื่อลงทะเบียนตัวสร้างแบบกำหนดเอง

import yaml
import os

class Loader(yaml.SafeLoader):

    def __init__(self, stream):

        self._root = os.path.split(stream.name)[0]

        super(Loader, self).__init__(stream)

    def include(self, node):

        filename = os.path.join(self._root, self.construct_scalar(node))

        with open(filename, 'r') as f:
            return yaml.load(f, Loader)

Loader.add_constructor('!include', Loader.include)

ตัวอย่าง:

foo.yaml

a: 1
b:
    - 1.43
    - 543.55
c: !include bar.yaml

bar.yaml

- 3.6
- [1, 2, 3]

ตอนนี้สามารถโหลดไฟล์โดยใช้:

>>> with open('foo.yaml', 'r') as f:
>>>    data = yaml.load(f, Loader)
>>> data
{'a': 1, 'b': [1.43, 543.55], 'c': [3.6, [1, 2, 3]]}

นี่คือคุณสมบัติที่น่าดึงดูดใจ thanx แต่อะไรคือจุดประสงค์ของการเปลี่ยนแปลงเหล่านี้ด้วย root / old_root? ฉันคิดว่ารหัสของincludeฟังก์ชั่นสามารถลดความซับซ้อน: `def include (ตัวโหลดโหนด):" "" รวมไฟล์ YAML อื่น "" "filename = loader.construct_scalar (โหนด) data = yaml.load (open (ชื่อไฟล์))`
Aliaksei Ramanau

รูทโกลบอลอยู่ที่นั่นเพื่อให้ญาติรวมถึงการทำงานที่ระดับความลึกใด ๆ เช่นเมื่อไฟล์ที่รวมอยู่ในไดเรกทอรีอื่นรวมถึงไฟล์ที่เกี่ยวข้องกับไดเรกทอรีนั้น การรวมแอบโซลูทควรใช้งานได้เช่นกัน อาจเป็นวิธีที่สะอาดกว่านี้หากไม่มีตัวแปรส่วนกลางบางทีอาจใช้คลาส yaml.Loader ที่กำหนดเอง
Josh Bode

2
เป็นไปได้ไหมที่จะมีบางสิ่งเช่นนี้: foo.yaml: a: bla bar.yaml: `! รวม foo.yaml b: blubb` ดังนั้นผลลัพธ์จะเป็น:` {'a': bla, 'b': blubb}
มาร์ติน

3
นี่ควรเป็นคำตอบที่ยอมรับได้ นอกจากนี้ nitpick ความปลอดภัยคุณควรใช้ yaml.safeload แทน yaml.load เพื่อหลีกเลี่ยง yaml ที่สร้างขึ้นเป็นพิเศษจากการเป็นเจ้าของบริการของคุณ
danielpops

1
@JoshBode นี้ควรใช้กับคุณ: gist.github.com/danielpops/5a0726f2fb6288da749c4cd604276be8
danielpops

32

หากคุณกำลังใช้YAML เวอร์ชันของ Symfonyนี่เป็นไปได้เช่นนี้:

imports:
    - { resource: sub-directory/file.yml }
    - { resource: sub-directory/another-file.yml }

34
นี่เป็นลักษณะเฉพาะที่ Symfony ตีความ YAML แทนที่จะเป็นส่วนหนึ่งของ YAML
jameshfisher

9
ใช่นั่นเป็นเหตุผลที่ฉันโพสต์ลิงค์ไปที่ Symfony docs คำถามถามว่า "เป็นไปได้หรือไม่อย่างไร?" ... นี่คือวิธี ดูไม่มีเหตุผลสำหรับการลงคะแนน
daveaspinall

4
ฉันไม่ได้ลงคะแนนคุณ ฉันแค่ชี้ให้เห็นว่านี่เป็นเรื่องเฉพาะของ Symfony YAML
jameshfisher

9
ไม่มี "เวอร์ชัน Symfony ของ YAML" ... นี่เป็นเพียงไลบรารีที่เข้ากันได้กับ YAML เฉพาะของผู้ขายที่มีเนื้อหาพิเศษที่ไม่ได้เป็นส่วนหนึ่งของ YAML
dreftymac

3
ไม่มีเหตุผลในการ downvote คำตอบนี้ถ้าคำตอบ "ตามระดับ" เป็น upvoted
มิคาอิล

13

การรวมไม่ได้รับการสนับสนุนโดยตรงใน YAML เท่าที่ฉันรู้คุณจะต้องจัดเตรียมกลไกด้วยตัวเองอย่างไรก็ตามโดยทั่วไปจะทำได้ง่าย

ฉันใช้ YAML เป็นภาษาการกำหนดค่าในแอพไพ ธ อนของฉันและในกรณีนี้มักจะกำหนดแบบแผนเช่นนี้:

>>> main.yml <<<
includes: [ wibble.yml, wobble.yml]

จากนั้นในรหัส (หลาม) ของฉันฉัน:

import yaml
cfg = yaml.load(open("main.yml"))
for inc in cfg.get("includes", []):
   cfg.update(yaml.load(open(inc)))

ข้อเสียเพียงอย่างเดียวคือตัวแปรในการรวมจะแทนที่ตัวแปรใน main เสมอและไม่มีวิธีใดที่จะเปลี่ยนลำดับความสำคัญนั้นโดยการเปลี่ยนตำแหน่งที่คำสั่ง "include: ปรากฏในไฟล์ main.yml

ในจุดที่แตกต่างกันเล็กน้อย YAML ไม่รองรับการใช้งานเนื่องจากไม่ได้ออกแบบมาเป็นพิเศษเช่นเดียวกับการทำเครื่องหมายตามไฟล์ สิ่งที่รวมถึงหมายถึงถ้าคุณได้รับมันในการตอบสนองต่อการร้องขอ AJAX?


3
ใช้ได้เฉพาะเมื่อไฟล์ yaml ไม่มีการกำหนดค่าแบบซ้อน
Freedom

10

สำหรับผู้ใช้งูหลามคุณสามารถลองpyyaml- ได้แก่

ติดตั้ง

pip install pyyaml-include

การใช้

import yaml
from yamlinclude import YamlIncludeConstructor

YamlIncludeConstructor.add_to_loader_class(loader_class=yaml.FullLoader, base_dir='/your/conf/dir')

with open('0.yaml') as f:
    data = yaml.load(f, Loader=yaml.FullLoader)

print(data)

พิจารณาว่าเรามีไฟล์YAMLดังต่อไปนี้:

├── 0.yaml
└── include.d
    ├── 1.yaml
    └── 2.yaml
  • 1.yaml เนื้อหาของ:
name: "1"
  • 2.yaml เนื้อหาของ:
name: "2"

รวมไฟล์ตามชื่อ

  • ในระดับบนสุด:

    ถ้า0.yamlเป็น:

!include include.d/1.yaml

เราจะได้รับ:

{"name": "1"}
  • ในการทำแผนที่:

    ถ้า0.yamlเป็น:

file1: !include include.d/1.yaml
file2: !include include.d/2.yaml

เราจะได้รับ:

  file1:
    name: "1"
  file2:
    name: "2"
  • ในลำดับ:

    ถ้า0.yamlเป็น:

files:
  - !include include.d/1.yaml
  - !include include.d/2.yaml

เราจะได้รับ:

files:
  - name: "1"
  - name: "2"

หมายเหตุ :

ชื่อไฟล์อาจเป็นแบบสัมบูรณ์ (เหมือน/usr/conf/1.5/Make.yml) หรือแบบสัมพัทธ์ (เหมือน../../cfg/img.yml)

รวมไฟล์ด้วยไวด์การ์ด

ชื่อไฟล์สามารถมีอักขระตัวแทนแบบเชลล์ ข้อมูลที่โหลดจากไฟล์ที่พบโดยไวด์การ์ดจะถูกตั้งค่าตามลำดับ

ถ้า0.yamlเป็น:

files: !include include.d/*.yaml

เราจะได้รับ:

files:
  - name: "1"
  - name: "2"

หมายเหตุ :

  • สำหรับPython>=3.5ถ้าrecursiveอาร์กิวเมนต์ของแท็ก!include YAMLคือtrueรูปแบบ“**”จะจับคู่กับไฟล์ใด ๆ และไดเรกทอรีหรือไดเรกทอรีย่อยหรือไดเรกทอรีศูนย์หรือมากกว่า
  • การใช้“**”รูปแบบในแผนผังไดเรกทอรีขนาดใหญ่อาจใช้เวลามากเกินไปเนื่องจากการค้นหาแบบเรียกซ้ำ

เพื่อเปิดใช้งานการrecursiveโต้แย้งเราจะเขียน!includeแท็กในMappingหรือSequenceโหมด:

  • อาร์กิวเมนต์ในSequenceโหมด:
!include [tests/data/include.d/**/*.yaml, true]
  • อาร์กิวเมนต์ในMappingโหมด:
!include {pathname: tests/data/include.d/**/*.yaml, recursive: true}

นี่ไม่ได้ตอบคำถามจริงๆ มันเกี่ยวข้องกับโซลูชัน Python ไม่ใช่เพียงแค่ใช้รูปแบบ YAML มาตรฐาน
oligofren

@oligofren ตัวจัดการแท็กที่กำหนดเองเป็นคุณลักษณะของ YAML ช่วยให้ตัวแยกวิเคราะห์ขยาย YAML เพื่อระบุประเภทและใช้พฤติกรรมที่กำหนดเองเช่นนี้ มันจะยืดยาวสำหรับสเปค YAML ตัวเองที่จะไปให้ไกลที่สุดเท่าที่จะกำหนดวิธีการรวมไฟล์ควรทำงานกับทุกรายละเอียดที่แตกต่างกันเส้นทาง OS, ระบบไฟล์ ฯลฯ
แอนตัน Strogonoff

@AntonStrogonoff ขอบคุณที่ทำให้ฉันได้รับความสนใจ คุณช่วยชี้ให้ฉันไปที่สถานที่ดังกล่าวใน RFC ได้หรือไม่? ไม่มีการกล่าวถึงคำว่า "กำหนดเอง" Ref yaml.org/spec/1.2/spec.html
oligofren

1
@oligofren ไม่เป็นไร มองหา“แอพลิเคชันเฉพาะ” แท็ก
Anton Strogonoff

8

ขยายคำตอบของ @ Josh_Bode ต่อไปนี้เป็นโซลูชัน PyYAML ของฉันซึ่งมีความได้เปรียบในการเป็นคลาสย่อยที่มีในyaml.Loaderตัว ไม่ได้ขึ้นอยู่กับโมดูลระดับกลมหรือการปรับเปลี่ยนสถานะโกลบอลของyamlโมดูล

import yaml, os

class IncludeLoader(yaml.Loader):                                                 
    """                                                                           
    yaml.Loader subclass handles "!include path/to/foo.yml" directives in config  
    files.  When constructed with a file object, the root path for includes       
    defaults to the directory containing the file, otherwise to the current       
    working directory. In either case, the root path can be overridden by the     
    `root` keyword argument.                                                      

    When an included file F contain its own !include directive, the path is       
    relative to F's location.                                                     

    Example:                                                                      
        YAML file /home/frodo/one-ring.yml:                                       
            ---                                                                   
            Name: The One Ring                                                    
            Specials:                                                             
                - resize-to-wearer                                                
            Effects: 
                - !include path/to/invisibility.yml                            

        YAML file /home/frodo/path/to/invisibility.yml:                           
            ---                                                                   
            Name: invisibility                                                    
            Message: Suddenly you disappear!                                      

        Loading:                                                                  
            data = IncludeLoader(open('/home/frodo/one-ring.yml', 'r')).get_data()

        Result:                                                                   
            {'Effects': [{'Message': 'Suddenly you disappear!', 'Name':            
                'invisibility'}], 'Name': 'The One Ring', 'Specials':              
                ['resize-to-wearer']}                                             
    """                                                                           
    def __init__(self, *args, **kwargs):                                          
        super(IncludeLoader, self).__init__(*args, **kwargs)                      
        self.add_constructor('!include', self._include)                           
        if 'root' in kwargs:                                                      
            self.root = kwargs['root']                                            
        elif isinstance(self.stream, file):                                       
            self.root = os.path.dirname(self.stream.name)                         
        else:                                                                     
            self.root = os.path.curdir                                            

    def _include(self, loader, node):                                    
        oldRoot = self.root                                              
        filename = os.path.join(self.root, loader.construct_scalar(node))
        self.root = os.path.dirname(filename)                           
        data = yaml.load(open(filename, 'r'))                            
        self.root = oldRoot                                              
        return data                                                      

2
ในที่สุดก็มาถึงเพื่อเพิ่มวิธีการเรียนตามคำตอบของฉัน แต่คุณเอาชนะฉันไปชก :) หมายเหตุ: หากคุณใช้yaml.load(f, IncludeLoader)ภายใน_includeคุณสามารถหลีกเลี่ยงการแทนที่ราก นอกจากนี้หากคุณทำเช่นนี้โซลูชันจะไม่ทำงานมากกว่าหนึ่งระดับเนื่องจากข้อมูลที่รวมใช้yaml.Loaderคลาสปกติ
Josh Bode

ฉันต้องลบคำสำคัญrootของkwargsหลังจากการตั้งค่าself.rootเพื่อให้มันทำงานกับสตริง ฉันย้ายบล็อก if-else ด้านบนการsuperโทร บางทีคนอื่นสามารถยืนยันการค้นพบของฉันหรือแสดงวิธีใช้คลาสกับสตริงและrootพารามิเตอร์
Woltan

1
น่าเสียดายที่สิ่งนี้ไม่ทำงานกับการอ้างอิงเช่น `` 'รวม: & รวม! รวม Inner.yaml ผสาน: <<: * รวม ``'
antony

2

ฉันทำตัวอย่างสำหรับการอ้างอิงของคุณ

import yaml

main_yaml = """
Package:
 - !include _shape_yaml    
 - !include _path_yaml
"""

_shape_yaml = """
# Define
Rectangle: &id_Rectangle
    name: Rectangle
    width: &Rectangle_width 20
    height: &Rectangle_height 10
    area: !product [*Rectangle_width, *Rectangle_height]

Circle: &id_Circle
    name: Circle
    radius: &Circle_radius 5
    area: !product [*Circle_radius, *Circle_radius, pi]

# Setting
Shape:
    property: *id_Rectangle
    color: red
"""

_path_yaml = """
# Define
Root: &BASE /path/src/

Paths: 
    a: &id_path_a !join [*BASE, a]
    b: &id_path_b !join [*BASE, b]

# Setting
Path:
    input_file: *id_path_a
"""


# define custom tag handler
def yaml_import(loader, node):
    other_yaml_file = loader.construct_scalar(node)
    return yaml.load(eval(other_yaml_file), Loader=yaml.SafeLoader)


def yaml_product(loader, node):
    import math
    list_data = loader.construct_sequence(node)
    result = 1
    pi = math.pi
    for val in list_data:
        result *= eval(val) if isinstance(val, str) else val
    return result


def yaml_join(loader, node):
    seq = loader.construct_sequence(node)
    return ''.join([str(i) for i in seq])


def yaml_ref(loader, node):
    ref = loader.construct_sequence(node)
    return ref[0]


def yaml_dict_ref(loader: yaml.loader.SafeLoader, node):
    dict_data, key, const_value = loader.construct_sequence(node)
    return dict_data[key] + str(const_value)


def main():
    # register the tag handler
    yaml.SafeLoader.add_constructor(tag='!include', constructor=yaml_import)
    yaml.SafeLoader.add_constructor(tag='!product', constructor=yaml_product)
    yaml.SafeLoader.add_constructor(tag='!join', constructor=yaml_join)
    yaml.SafeLoader.add_constructor(tag='!ref', constructor=yaml_ref)
    yaml.SafeLoader.add_constructor(tag='!dict_ref', constructor=yaml_dict_ref)

    config = yaml.load(main_yaml, Loader=yaml.SafeLoader)

    pk_shape, pk_path = config['Package']
    pk_shape, pk_path = pk_shape['Shape'], pk_path['Path']
    print(f"shape name: {pk_shape['property']['name']}")
    print(f"shape area: {pk_shape['property']['area']}")
    print(f"shape color: {pk_shape['color']}")

    print(f"input file: {pk_path['input_file']}")


if __name__ == '__main__':
    main()

เอาท์พุต

shape name: Rectangle
shape area: 200
shape color: red
input file: /path/src/a

อัปเดต 2

และคุณสามารถรวมมันได้เช่นนี้

# xxx.yaml
CREATE_FONT_PICTURE:
  PROJECTS:
    SUNG: &id_SUNG
      name: SUNG
      work_dir: SUNG
      output_dir: temp
      font_pixel: 24


  DEFINE: &id_define !ref [*id_SUNG]  # you can use config['CREATE_FONT_PICTURE']['DEFINE'][name, work_dir, ... font_pixel]
  AUTO_INIT:
    basename_suffix: !dict_ref [*id_define, name, !product [5, 3, 2]]  # SUNG30

# ↓ This is not correct.
# basename_suffix: !dict_ref [*id_define, name, !product [5, 3, 2]]  # It will build by Deep-level. id_define is Deep-level: 2. So you must put it after 2. otherwise, it can't refer to the correct value.

1

น่าเสียดายที่ YAML ไม่ได้ให้สิ่งนี้ตามมาตรฐาน

แต่ถ้าคุณใช้ Ruby ก็มีอัญมณีที่ให้ฟังก์ชั่นที่คุณต้องการโดยการขยายไลบรารี่ YAML ไปที่ https://github.com/entwanderer/yaml_extend


1

ฉันคิดว่าวิธีที่ใช้โดย @ maxy-B ดูดีมาก อย่างไรก็ตามมันไม่ประสบความสำเร็จสำหรับฉันด้วยการรวมซ้อนกัน ตัวอย่างเช่นหาก config_1.yaml มี config_2.yaml ซึ่งรวมถึง config_3.yaml มีปัญหากับตัวโหลด อย่างไรก็ตามถ้าคุณเพียงแค่ชี้คลาสโหลดเดอร์ใหม่ให้กับตัวเองมันก็ใช้งานได้! หากเราแทนที่ฟังก์ชัน _include แบบเก่าด้วยรุ่นที่มีการปรับเปลี่ยนเล็กน้อย:

def _include(self, loader, node):                                    
     oldRoot = self.root                                              
     filename = os.path.join(self.root, loader.construct_scalar(node))
     self.root = os.path.dirname(filename)                           
     data = yaml.load(open(filename, 'r'), loader = IncludeLoader)                            
     self.root = oldRoot                                              
     return data

จากการไตร่ตรองฉันเห็นด้วยกับความคิดเห็นอื่น ๆ การโหลดซ้อนกันไม่เหมาะสำหรับ yaml โดยทั่วไปเนื่องจากอินพุตสตรีมอาจไม่ใช่ไฟล์ แต่มีประโยชน์มาก!


1

มาตรฐาน YML ไม่ได้ระบุวิธีการทำเช่นนี้ และปัญหานี้ไม่ได้ จำกัด อยู่ที่ YML เท่านั้น JSON มีข้อ จำกัด เหมือนกัน

แอปพลิเคชันจำนวนมากที่ใช้การกำหนดค่าที่ใช้ YML หรือ JSON จะพบปัญหานี้ในที่สุด และเมื่อสิ่งนั้นเกิดขึ้นพวกเขาก็ทำแบบแผนของตัวเองพวกเขาทำขึ้นการประชุมของตัวเอง

เช่นสำหรับคำจำกัดความของ swagger API:

$ref: 'file.yml'

เช่นสำหรับนักเทียบท่าเขียนการกำหนดค่า:

services:
  app:
    extends:
      file: docker-compose.base.yml

อีกทางเลือกหนึ่งถ้าคุณต้องการแยกเนื้อหาของไฟล์ yml ในหลายไฟล์เช่นโครงสร้างของเนื้อหาคุณสามารถกำหนดระเบียบโครงสร้างโฟลเดอร์ของคุณเองและใช้สคริปต์ผสาน (ปัจจุบัน)



0

มาตรฐาน YAML 1.2 ไม่รวมคุณสมบัตินี้โดยกำเนิด อย่างไรก็ตามการใช้งานจำนวนมากให้ส่วนขยายที่จะทำ

ฉันนำเสนอวิธีการบรรลุผลด้วย Java และsnakeyaml:1.24(ไลบรารี Java เพื่อแยก / ปล่อยไฟล์ YAML) ที่อนุญาตให้สร้างแท็ก YAML ที่กำหนดเองเพื่อให้บรรลุเป้าหมายต่อไปนี้ (คุณจะเห็นว่าฉันใช้มันเพื่อโหลดชุดทดสอบที่กำหนดในหลายไฟล์ YAML และฉันทำให้มันทำงานเป็นรายการรวมสำหรับtest:โหนดเป้าหมาย):

# ... yaml prev stuff

tests: !include
  - '1.hello-test-suite.yaml'
  - '3.foo-test-suite.yaml'
  - '2.bar-test-suite.yaml'

# ... more yaml document

นี่คือ Java แบบชั้นเดียวที่อนุญาตให้ประมวลผล!includeแท็ก ไฟล์ถูกโหลดจาก classpath (ไดเรกทอรีทรัพยากร Maven):

/**
 * Custom YAML loader. It adds support to the custom !include tag which allows splitting a YAML file across several
 * files for a better organization of YAML tests.
 */
@Slf4j   // <-- This is a Lombok annotation to auto-generate logger
public class MyYamlLoader {

    private static final Constructor CUSTOM_CONSTRUCTOR = new MyYamlConstructor();

    private MyYamlLoader() {
    }

    /**
     * Parse the only YAML document in a stream and produce the Java Map. It provides support for the custom !include
     * YAML tag to split YAML contents across several files.
     */
    public static Map<String, Object> load(InputStream inputStream) {
        return new Yaml(CUSTOM_CONSTRUCTOR)
                .load(inputStream);
    }


    /**
     * Custom SnakeYAML constructor that registers custom tags.
     */
    private static class MyYamlConstructor extends Constructor {

        private static final String TAG_INCLUDE = "!include";

        MyYamlConstructor() {
            // Register custom tags
            yamlConstructors.put(new Tag(TAG_INCLUDE), new IncludeConstruct());
        }

        /**
         * The actual include tag construct.
         */
        private static class IncludeConstruct implements Construct {

            @Override
            public Object construct(Node node) {
                List<Node> inclusions = castToSequenceNode(node);
                return parseInclusions(inclusions);
            }

            @Override
            public void construct2ndStep(Node node, Object object) {
                // do nothing
            }

            private List<Node> castToSequenceNode(Node node) {
                try {
                    return ((SequenceNode) node).getValue();

                } catch (ClassCastException e) {
                    throw new IllegalArgumentException(String.format("The !import value must be a sequence node, but " +
                            "'%s' found.", node));
                }
            }

            private Object parseInclusions(List<Node> inclusions) {

                List<InputStream> inputStreams = inputStreams(inclusions);

                try (final SequenceInputStream sequencedInputStream =
                             new SequenceInputStream(Collections.enumeration(inputStreams))) {

                    return new Yaml(CUSTOM_CONSTRUCTOR)
                            .load(sequencedInputStream);

                } catch (IOException e) {
                    log.error("Error closing the stream.", e);
                    return null;
                }
            }

            private List<InputStream> inputStreams(List<Node> scalarNodes) {
                return scalarNodes.stream()
                        .map(this::inputStream)
                        .collect(toList());
            }

            private InputStream inputStream(Node scalarNode) {
                String filePath = castToScalarNode(scalarNode).getValue();
                final InputStream is = getClass().getClassLoader().getResourceAsStream(filePath);
                Assert.notNull(is, String.format("Resource file %s not found.", filePath));
                return is;
            }

            private ScalarNode castToScalarNode(Node scalarNode) {
                try {
                    return ((ScalarNode) scalarNode);

                } catch (ClassCastException e) {
                    throw new IllegalArgumentException(String.format("The value must be a scalar node, but '%s' found" +
                            ".", scalarNode));
                }
            }
        }

    }

}

0

ด้วยYgluคุณสามารถนำเข้าไฟล์อื่น ๆ เช่นนี้:

A.yaml

foo: !? $import('B.yaml')

B.yaml

bar: Hello
$ yglu A.yaml
foo:
  bar: Hello

ในฐานะที่$importเป็นฟังก์ชั่นคุณสามารถส่งผ่านการแสดงออกเป็นอาร์กิวเมนต์:

  dep: !- b
  foo: !? $import($_.dep.toUpper() + '.yaml')

สิ่งนี้จะให้ผลลัพธ์เช่นเดียวกับข้างต้น

ข้อจำกัดความรับผิดชอบ: ฉันเป็นผู้เขียนของ Yglu


-1

ด้วยSymfonyการจัดการ yaml ในทางอ้อมจะช่วยให้คุณสามารถซ้อนไฟล์ yaml ได้ เคล็ดลับคือการใช้ประโยชน์จากparametersตัวเลือก เช่น:

common.yml

parameters:
    yaml_to_repeat:
        option: "value"
        foo:
            - "bar"
            - "baz"

config.yml

imports:
    - { resource: common.yml }
whatever:
    thing: "%yaml_to_repeat%"
    other_thing: "%yaml_to_repeat%"

ผลลัพธ์จะเหมือนกับ:

whatever:
    thing:
        option: "value"
        foo:
            - "bar"
            - "baz"
    other_thing:
        option: "value"
        foo:
            - "bar"
            - "baz"

-6

อาจไม่ได้รับการสนับสนุนเมื่อถามคำถาม แต่คุณสามารถนำเข้าไฟล์ YAML อื่น ๆ เป็นไฟล์เดียว:

imports: [/your_location_to_yaml_file/Util.area.yaml]

แม้ว่าฉันจะไม่มีการอ้างอิงออนไลน์ แต่สิ่งนี้ใช้ได้สำหรับฉัน


4
สิ่งนี้ไม่ได้ทำสิ่งใดรวมถึง มันจะสร้างการทำแผนที่ด้วยลำดับที่ประกอบด้วยสายเดียว "/your_location_to_yaml_file/Util.area.yaml" importsเป็นค่าสำหรับคีย์
Anthon
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.