कैसे एक YAML फ़ाइल में कहीं से एक YAML "सेटिंग" को संदर्भित करने के लिए?


145

मेरे पास निम्नलिखित YAML है:

paths:
  patha: /path/to/root/a
  pathb: /path/to/root/b
  pathc: /path/to/root/c

/path/to/root/तीन रास्तों से हटाकर मैं इसे "सामान्य" कैसे कर सकता हूं , और इसकी अपनी सेटिंग के रूप में, कुछ इस तरह है:

paths:
  root: /path/to/root/
  patha: *root* + a
  pathb: *root* + b
  pathc: *root* + c

जाहिर है कि यह अमान्य है, मैंने अभी इसे बनाया है। असली वाक्य रचना क्या है? क्या यह किया जा सकता है?


1
इसे भी देखें: stackoverflow.com/a/41620747/42223
dreftymac

जवाबों:


127

मुझे नहीं लगता कि यह संभव है। आप "नोड" का पुन: उपयोग कर सकते हैं लेकिन इसका हिस्सा नहीं हैं।

bill-to: &id001
    given  : Chris
    family : Dumars
ship-to: *id001

यह पूरी तरह से मान्य YAML और फ़ील्ड हैं givenऔर ब्लॉक familyमें पुन: उपयोग किए जाते हैं ship-to। आप एक स्केलर नोड को उसी तरह से पुन: उपयोग कर सकते हैं लेकिन ऐसा कोई तरीका नहीं है जिससे आप बदल सकते हैं कि अंदर क्या है और एक पथ के अंतिम भाग को YAML के अंदर से जोड़ दें।

यदि पुनरावृत्ति आपको परेशान करती है तो मैं आपके आवेदन को rootसंपत्ति के बारे में जागरूक करने और उसे हर उस रास्ते से जोड़ने का सुझाव देता हूं जो सापेक्ष नहीं है।


1
ठीक है धन्यवाद, हाँ बीमार को rootकोड में प्रस्तुत करना होगा। कोई बड़ी बात नहीं।
एंड्रयू बुलॉक

2
स्वीकृत उत्तर सटीक नहीं है। एक समाधान के लिए मेरा जवाब देखें।
क्रिस जॉनसन

यह कैसे करें, यदि बिल- इन एक अन्य फ़ाइल में है, जिसे हमने शिप-टू परिभाषित किया है, जहां आयात किया है?
प्रतिक जैन

@PrateekJain: यदि आप कई फ़ाइलों के साथ काम कर रहे हैं, तो आप शायद एक स्टैंडअलोन YAML- वृद्धि पुस्तकालय का मूल्यांकन करने के लिए सबसे अच्छा करेंगे, जैसे कि यहां सूचीबद्ध। github.com/dreftymac/dynamic.yaml/blob/master/…
dreftymac

1
उदाहरण देखें yaml.org/spec/1.2/spec.html में 2.9 ; एक भी स्केलर का संदर्भ दे सकता है जो भयानक है
akostadinov

72

हाँ, कस्टम टैग का उपयोग कर। पायथन में उदाहरण, !joinटैग को एक सरणी में तार मिलाते हैं:

import yaml

## define custom tag handler
def join(loader, node):
    seq = loader.construct_sequence(node)
    return ''.join([str(i) for i in seq])

## register the tag handler
yaml.add_constructor('!join', join)

## using your sample data
yaml.load("""
paths:
    root: &BASE /path/to/root/
    patha: !join [*BASE, a]
    pathb: !join [*BASE, b]
    pathc: !join [*BASE, c]
""")

जिसके परिणामस्वरूप:

{
    'paths': {
        'patha': '/path/to/root/a',
        'pathb': '/path/to/root/b',
        'pathc': '/path/to/root/c',
        'root': '/path/to/root/'
     }
}

तर्कों की सरणी में !joinकिसी भी प्रकार के किसी भी डेटा प्रकार के तत्व हो सकते हैं, जब तक कि उन्हें स्ट्रिंग में परिवर्तित किया जा सकता है, तो !join [*a, "/", *b, "/", *c]आप क्या उम्मीद करेंगे।


2
मुझे आपका समाधान पसंद है, कोडिंग में सरल तो थोड़ा कम पठनीय YAML की कीमत पर मेरा।
एंथन

7
यह उत्तर अधिक वोटों का हकदार है। यह तकनीकी रूप से YAML विनिर्देशन का सबसे सटीक उत्तर है। हालांकि, एक वास्तविक एचएएमएल कार्यान्वयन के लिए एक चेतावनी है, लेकिन कुछ ऐसे हैं जो वास्तव में पूर्ण एचसीएलएल कल्पना को लागू करते हैं। विनिर्देशन के साथ इसकी एकरूपता के संदर्भ में पायथन का पायमेल कई अन्य लोगों से ऊपर और परे है।
dreftymac

5
ऐसा लगता है कि सवाल एक यमल फ़ाइल में मान को संदर्भित करने के बारे में है। इसके चारों ओर कोड की एक और परत जोड़ना मेरा पसंदीदा समाधान नहीं होगा।
user2020056

1
@ChrisJohnson इस उत्तर के लिए धन्यवाद, मैं सोच रहा था कि क्या आपके पास एक संदर्भ दस्तावेज़ है जो इस वाक्यविन्यास को सूचीबद्ध करता है। मैंने देखा है कि YAML कल्पना वेब पर कई स्थानों पर बताई गई है, इसलिए मैं सिर्फ यह सुनिश्चित करना चाहता हूं कि मैं उसी संदर्भ को देख रहा हूं जो आप हैं। धन्यवाद!
user5359531

3
यह समाधान मेरे लिए ( python3)? विशेष रूप से:yaml.SafeLoader.add_constructor(tag='!join', constructor=join) yaml.load(open(fpth, mode='r'), Loader=yaml.SafeLoader)
बेंजामिनोग्रामस

20

इसे देखने का एक और तरीका है, बस दूसरे क्षेत्र का उपयोग करना।

paths:
  root_path: &root
     val: /path/to/root/
  patha: &a
    root_path: *root
    rel_path: a
  pathb: &b
    root_path: *root
    rel_path: b
  pathc: &c
    root_path: *root
    rel_path: c

5

YML परिभाषा:

dir:
  default: /home/data/in/
  proj1: ${dir.default}p1
  proj2: ${dir.default}p2
  proj3: ${dir.default}p3 

थाइमेलफ में कहीं

<p th:utext='${@environment.getProperty("dir.default")}' />
<p th:utext='${@environment.getProperty("dir.proj1")}' /> 

आउटपुट: / घर / डेटा / in / / घर / डेटा / in / p1


@AndrewBullock मुझे लगता है कि यह स्वीकृत उत्तर होना चाहिए, क्योंकि यह आपके मुद्दे को हल करता है।
होनज़ा ज़िदक

5
नहीं, यह YAML में चर का एक देशी उपयोग नहीं है और यह किसी भी विनिर्देश संस्करण में निर्दिष्ट नहीं है। कुछ परीक्षण के बाद, यह काम नहीं करता है।
आर्थर लैकोस्टे

2
यह संभवत: पावोल के लिए काम करता है जिसमें कुछ यमल को प्री-प्रोसेस्ड (यानी मावेन-रिसोर्स-प्लगइन फ़िल्टरिंग) किया गया था
DeezCashews

1
मानक यमल नहीं
दान नीरो

3

मैंने एक पुस्तकालय बनाया है, जो पैकगिस्ट पर उपलब्ध है, जो इस फ़ंक्शन को करता है: https://packagist.org/packages/grasmash/yaml-expander

उदाहरण YAML फ़ाइल:

type: book
book:
  title: Dune
  author: Frank Herbert
  copyright: ${book.author} 1965
  protaganist: ${characters.0.name}
  media:
    - hardcover
characters:
  - name: Paul Atreides
    occupation: Kwisatz Haderach
    aliases:
      - Usul
      - Muad'Dib
      - The Preacher
  - name: Duncan Idaho
    occupation: Swordmaster
summary: ${book.title} by ${book.author}
product-name: ${${type}.title}

उदाहरण तर्क:

// Parse a yaml string directly, expanding internal property references.
$yaml_string = file_get_contents("dune.yml");
$expanded = \Grasmash\YamlExpander\Expander::parse($yaml_string);
print_r($expanded);

परिणामी सरणी:

array (
  'type' => 'book',
  'book' => 
  array (
    'title' => 'Dune',
    'author' => 'Frank Herbert',
    'copyright' => 'Frank Herbert 1965',
    'protaganist' => 'Paul Atreides',
    'media' => 
    array (
      0 => 'hardcover',
    ),
  ),
  'characters' => 
  array (
    0 => 
    array (
      'name' => 'Paul Atreides',
      'occupation' => 'Kwisatz Haderach',
      'aliases' => 
      array (
        0 => 'Usul',
        1 => 'Muad\'Dib',
        2 => 'The Preacher',
      ),
    ),
    1 => 
    array (
      'name' => 'Duncan Idaho',
      'occupation' => 'Swordmaster',
    ),
  ),
  'summary' => 'Dune by Frank Herbert',
);

विस्तारक अवधारणा को प्यार करना!
गिलियूम रॉड्रिक

2

कुछ भाषाओं में, आप एक वैकल्पिक पुस्तकालय का उपयोग कर सकते हैं, उदाहरण के लिए, टैम्पैक्स YAML हैंडलिंग चर का एक कार्यान्वयन है:

const tampax = require('tampax');

const yamlString = `
dude:
  name: Arthur
weapon:
  favorite: Excalibur
  useless: knife
sentence: "{{dude.name}} use {{weapon.favorite}}. The goal is {{goal}}."`;

const r = tampax.yamlParseString(yamlString, { goal: 'to kill Mordred' });
console.log(r.sentence);

// output : "Arthur use Excalibur. The goal is to kill Mordred."

1

अपने उदाहरण अमान्य है यही कारण है केवल क्योंकि आप एक आरक्षित वर्ण के साथ अपने scalars शुरू करने के लिए चुना है। यदि आप *कुछ अन्य गैर-आरक्षित वर्ण के साथ प्रतिस्थापित करते हैं (मैं गैर-ASCII वर्णों का उपयोग करने के लिए हूं क्योंकि वे शायद ही कभी कुछ विनिर्देश के भाग के रूप में उपयोग किए जाते हैं), तो आप पूरी तरह से कानूनी यम के साथ समाप्त होते हैं:

paths:
  root: /path/to/root/
  patha: ♦root♦ + a
  pathb: ♦root♦ + b
  pathc: ♦root♦ + c

यह आपके पार्सर द्वारा उपयोग की जाने वाली भाषा में मैपिंग के लिए मानक प्रतिनिधित्व में लोड होगा और जादुई रूप से किसी चीज़ का विस्तार नहीं करता है।
निम्न पायथन प्रोग्राम के अनुसार स्थानीय रूप से डिफ़ॉल्ट ऑब्जेक्ट प्रकार का उपयोग करने के लिए:

# coding: utf-8

from __future__ import print_function

import ruamel.yaml as yaml

class Paths:
    def __init__(self):
        self.d = {}

    def __repr__(self):
        return repr(self.d).replace('ordereddict', 'Paths')

    @staticmethod
    def __yaml_in__(loader, data):
        result = Paths()
        loader.construct_mapping(data, result.d)
        return result

    @staticmethod
    def __yaml_out__(dumper, self):
        return dumper.represent_mapping('!Paths', self.d)

    def __getitem__(self, key):
        res = self.d[key]
        return self.expand(res)

    def expand(self, res):
        try:
            before, rest = res.split(u'♦', 1)
            kw, rest = rest.split(u'♦ +', 1)
            rest = rest.lstrip() # strip any spaces after "+"
            # the lookup will throw the correct keyerror if kw is not found
            # recursive call expand() on the tail if there are multiple
            # parts to replace
            return before + self.d[kw] + self.expand(rest)
        except ValueError:
            return res

yaml_str = """\
paths: !Paths
  root: /path/to/root/
  patha: ♦root♦ + a
  pathb: ♦root♦ + b
  pathc: ♦root♦ + c
"""

loader = yaml.RoundTripLoader
loader.add_constructor('!Paths', Paths.__yaml_in__)

paths = yaml.load(yaml_str, Loader=yaml.RoundTripLoader)['paths']

for k in ['root', 'pathc']:
    print(u'{} -> {}'.format(k, paths[k]))

जो प्रिंट करेगा:

root -> /path/to/root/
pathc -> /path/to/root/c

विस्तार मक्खी पर किया जाता है और नेस्टेड परिभाषाओं को संभालता है, लेकिन आपको अनंत पुनरावृत्ति न करने के बारे में सावधान रहना होगा।

डम्पर को निर्दिष्ट करके, आप ऑन-द-फ्लाई विस्तार के कारण लोड किए गए डेटा से मूल YAML को डंप कर सकते हैं:

dumper = yaml.RoundTripDumper
dumper.add_representer(Paths, Paths.__yaml_out__)
print(yaml.dump(paths, Dumper=dumper, allow_unicode=True))

यह मैपिंग कुंजी को बदल देगा। यदि यह एक समस्या है तो आपको self.dएक CommentedMap(से आयातित ruamel.yaml.comments.py) बनाना होगा


0

मैंने पाइथॉन पर अपनी खुद की लाइब्रेरी लिखी है, जैसे कि एक पदानुक्रम के साथ निर्देशिकाओं से लोड किए जा रहे चर का विस्तार करने के लिए:

/root
 |
 +- /proj1
     |
     +- config.yaml
     |
     +- /proj2
         |
         +- config.yaml
         |
         ... and so on ...

यहां मुख्य अंतर यह है कि सभी config.yamlफ़ाइलों के लोड होने के बाद ही विस्तार लागू किया जाना चाहिए , जहां अगली फ़ाइल से चर पिछले से चर को ओवरराइड कर सकते हैं, इसलिए छद्मकोड को इस तरह दिखना चाहिए:

env = YamlEnv()
env.load('/root/proj1/config.yaml')
env.load('/root/proj1/proj2/config.yaml')
...
env.expand()

एक अतिरिक्त विकल्प के रूप में xonshस्क्रिप्ट परिणामी चर को पर्यावरण चर ( yaml_update_global_varsफ़ंक्शन देखें ) में निर्यात कर सकती है ।

स्क्रिप्ट:

https://sourceforge.net/p/contools/contools/HEAD/tree/Sunk/Scripts/Tools/cmdoplib.yaml.py https://sourceforge.net/p/contools/contools/HEAD/tree/trunk/Scripts /Tools/cmdoplib.yaml.xsh

पेशेवरों :

  • सरल, पुनरावृत्ति और नेस्टेड चर का समर्थन नहीं करता है
  • एक प्लेसहोल्डर के लिए अपरिभाषित चर की जगह ले सकता है ( ${MYUNDEFINEDVAR}-> *$/{MYUNDEFINEDVAR})
  • पर्यावरण चर से एक संदर्भ का विस्तार कर सकते हैं ( ${env:MYVAR})
  • एक पथ चर ( ) में सभी \\को बदल सकते हैं/${env:MYVAR:path}

विपक्ष :

  • नेस्टेड चर का समर्थन नहीं करता है, इसलिए नेस्टेड शब्दकोशों में मूल्यों का विस्तार नहीं कर सकता (कुछ ऐसा ${MYSCOPE.MYVAR}लागू नहीं है)
  • विस्तार पुनरावृत्ति का पता नहीं लगाता है, एक प्लेसहोल्डर डाल के बाद पुनरावृत्ति सहित

0

यगलु के साथ , आप अपना उदाहरण इस प्रकार लिख सकते हैं:

paths:
  root: /path/to/root/
  patha: !? .paths.root + a
  pathb: !? .paths.root + b
  pathc: !? .paths.root + c

डिस्क्लेमर: मैं यगलू का लेखक हूं।


यह एक पुस्तकालय के बारे में पता होना अच्छा है जो कि इस कार्यशीलता को २२:५० पर २५-२०१४ के अनुसार यमल
धीरज
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.