سؤال

إذن لدي ملفان YAML، "A" و"B" وأريد أن يتم إدراج محتويات A داخل B، إما مقسمة إلى بنية البيانات الموجودة، مثل المصفوفة، أو كطفل لعنصر، مثل القيمة لمفتاح تجزئة معين.

هل هذا ممكن على الإطلاق؟كيف؟إذا لم يكن الأمر كذلك، أي مؤشرات إلى مرجعية معيارية؟

هل كانت مفيدة؟

المحلول

لا، YAML لا يتضمن أي نوع من "استيراد" أو "تشمل" بيان.

نصائح أخرى

سؤالك لا يطالب بحل بايثون، ولكن إليك حلًا يستخدمه بييامل.

يسمح لك PyYAML بإرفاق مُنشئات مخصصة (مثل !include) إلى محمل YAML.لقد قمت بتضمين دليل جذر يمكن تعيينه بحيث يدعم هذا الحل مراجع الملفات النسبية والمطلقة.

الحل القائم على الفصل

إليك حلًا قائمًا على الفصل يتجنب المتغير الجذري العام لاستجابتي الأصلية.

انظر الى هذا جوهر للحصول على حل مماثل وأكثر قوة لـ Python 3 يستخدم فئة تعريفية لتسجيل المُنشئ المخصص.

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]]}

إذا كنت تستخدم نسخة أن symfony من YAML ، وهذا ممكن، مثل هذا:

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

ويشمل غير معتمدة مباشرة في 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.yml

.

في وجهة نظر مختلفة قليلا، YAML لا يدعم يتضمن كما ليست مصممة حقا كما حصريا كملف أساس المبرمجة. ما يمكن أن تشمل يعني إذا كنت حصلت عليه في استجابة لطلب AJAX؟

والتوسع في @ الجواب 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                                                      

بالنسبة لمستخدمي بايثون، يمكنك المحاولة pyyaml-include.

ثَبَّتَ

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)

النظر في أن لدينا مثل هذا يامل الملفات:

├── 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 يامل العلامة هي true, ، النمط “**” سوف يطابق أي ملفات وصفر أو أكثر من الدلائل والأدلة الفرعية.
  • باستخدام “**” قد يستهلك النمط الموجود في أشجار الدليل الكبيرة قدرًا هائلاً من الوقت بسبب البحث العودي.

لكي تمكن recursive حجة، يجب علينا أن نكتب !include العلامة في Mapping أو Sequence وضع:

  • الحجج في Sequence وضع:
!include [tests/data/include.d/**/*.yaml, true]
  • الحجج في Mapping وضع:
!include {pathname: tests/data/include.d/**/*.yaml, recursive: true}

ومما يؤسف له YAML لا يوفر هذا في مستواه.

ولكن إذا كنت تستخدم روبي، هناك جوهرة توفير وظائف تسألون عن طريق تمديد مكتبة روبي YAML: https://github.com/entwanderer/yaml_extend

وأعتقد أن الحل يستخدمه @ 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 بشكل عام كما دفق الإدخال قد لا يكون ملف، ولكن من المفيد جدا!

وربما هذا يمكن أن تلهمك، في محاولة لمحاذاة إلى الاتفاقيات JBB:

https://docs.openstack.org /infra/jenkins-job-builder/definition.html#inclusion-tags

و - job: name: test-job-include-raw-1 builders: - shell: !include-raw: include-raw001-hello-world.sh

وستاندرد YAML 1.2 لا يتضمن أصلا هذه الميزة. مع ذلك العديد من تطبيقات توفر بعض تمديد للقيام بذلك.

وأقدم وسيلة لتحقيق ذلك مع جافا وsnakeyaml:1.24 (مكتبة جافا لتحليل / تنبعث منها ملفات 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

وهنا هو جافا فئة واحدة أن يسمح تجهيز العلامة !include. يتم تحميل الملفات من CLASSPATH (دليل الموارد مخضرم):

/**
 * 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));
                }
            }
        }

    }

}

مع سيمفوني, ، فإن تعامله مع 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"

وربما كان غير معتمد عندما سئل السؤال ولكن يمكنك استيراد ملف YAML أخرى إلى واحد:

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

وعلى الرغم من أنني لم يكن لديك أي إشارة على الانترنت ولكن هذا يعمل بالنسبة لي.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top