2010-01-14 21 views
73

Tôi có YAML sau:làm thế nào để tham khảo một "thiết lập" YAML từ những nơi khác trong cùng một tập tin YAML?

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

Làm thế nào tôi có thể "bình thường hóa" này, bằng cách loại bỏ /path/to/root/ từ ba con đường, và có nó như là thiết lập riêng của mình, một cái gì đó như:

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

Rõ ràng đó là không hợp lệ, tôi chỉ làm cho nó lên. Cú pháp thực sự là gì? Nó có thể được thực hiện?

+0

** Xem thêm: ** https://stackoverflow.com/a/41620747/42223 – dreftymac

Trả lời

64

Tôi không nghĩ điều đó là có thể. Bạn có thể tái sử dụng "nút" nhưng không sử dụng được nút đó.

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

này được YAML hoàn toàn hợp lệ và các lĩnh vực givenfamily được tái sử dụng trong ship-to khối. Bạn có thể tái sử dụng một nút vô hướng theo cùng một cách nhưng không có cách nào bạn có thể thay đổi những gì bên trong và thêm phần cuối cùng của một đường dẫn đến nó từ bên trong YAML.

Nếu sự lặp lại làm phiền bạn nhiều đến mức tôi đề nghị làm cho ứng dụng của bạn biết về tài sản root và thêm nó vào mọi đường dẫn có vẻ tương đối không tuyệt đối.

+1

Ok cảm ơn, vâng bệnh phải thêm tiền tố 'root' vào mã. không có gì. –

+2

Câu trả lời được chấp nhận là không chính xác. Xem câu trả lời của tôi cho một giải pháp. –

+0

cách thực hiện việc này, nếu ** bill-to ** có trong tệp khác, chúng tôi đã nhập nơi ** ship-to ** được xác định? –

37

Có, sử dụng thẻ tùy chỉnh. Ví dụ trong Python, làm thẻ !join tham gia chuỗi trong một mảng:

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] 
""") 

mà kết quả trong:

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

Các mảng các đối số để !join có thể có bất kỳ số lượng các yếu tố của bất kỳ loại dữ liệu, như miễn là chúng có thể được chuyển đổi thành chuỗi, do đó, !join [*a, "/", *b, "/", *c] sẽ làm những gì bạn mong đợi.

+1

Tôi thích giải pháp của bạn, đơn giản hơn trong mã hóa sau đó tôi với chi phí của YAML hơi ít có thể đọc được. – Anthon

+0

Bất cứ ai đã bỏ phiếu này xuống - bạn có thể nói lý do tại sao không? –

+0

Điểm nổi bật trên mũi tên xuống cho biết "câu trả lời này không hữu ích" và đó chính là trường hợp cho bất kỳ ai không quan tâm đến chủ đề của YAML. Thậm chí tệ hơn không ai (ngoài tôi, bởi vì tôi là người bình luận duy nhất cho đến nay) sẽ nhận được một thông báo về bình luận/câu hỏi của bạn. Cơ hội mà một người downvoted quay trở lại đây và đọc bình luận và câu trả lời của bạn quá thấp đến mức tôi sẽ xem xét những người gần gũi với sự tồn tại. Có nói rằng, nó không phải là tôi, và nếu tôi là bạn tôi sẽ không hỏi và xóa bình luận. – Anthon

7

Một cách khác để xem xét việc này là chỉ cần sử dụng một trường khác.

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 
-1

Đó ví dụ của bạn là không hợp lệ là chỉ vì bạn đã chọn ký tự dành riêng để bắt đầu vô hướng của bạn với. Nếu bạn thay thế * với một số nhân vật không được bảo lưu khác (tôi có xu hướng sử dụng các ký tự ASCII cho rằng khi họ hiếm khi được sử dụng như một phần của một số đặc điểm kỹ thuật), bạn kết thúc với YAML hoàn toàn hợp pháp:

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

Điều này sẽ tải vào biểu diễn tiêu chuẩn cho ánh xạ theo ngôn ngữ mà trình phân tích cú pháp của bạn sử dụng và không mở rộng bất kỳ điều gì một cách kỳ diệu.
Để làm điều đó sử dụng một loại đối tượng mặc định tại địa phương như trong chương trình Python sau:

# 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])) 

mà sẽ in:

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

việc mở rộng được thực hiện một cách nhanh chóng và xử lý các định nghĩa lồng nhau, nhưng bạn phải cẩn thận về việc không gọi đệ quy vô hạn.

Bằng cách xác định dumper, bạn có thể đổ YAML gốc từ các dữ liệu được nạp vào, vì việc mở rộng on-the-fly:

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

này sẽ thay đổi thứ tự chính bản đồ. Nếu đó là một vấn đề bạn có để làm self.d một CommentedMap (nhập khẩu từ ruamel.yaml.comments.py)

1

tôi đã tạo ra một thư viện, có sẵn trên Packagist, mà thực hiện chức năng này: https://packagist.org/packages/grasmash/yaml-expander

Ví dụ YAML file:

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} 

Ví dụ logic:

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

mảng Resultant:

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', 
); 
+0

Yêu thích khái niệm giãn nở! –

2

YML định nghĩa:

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

Một nơi nào đó trong thymeleaf

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

Output: /home/data/trong/ /home/data/trong/p1

+0

@AndrewBullock Tôi nghĩ rằng đây phải là câu trả lời được chấp nhận, vì nó chính xác giải quyết vấn đề của bạn. –

+1

Không, nó không phải là một cách sử dụng bản địa của biến trong YAML và nó không được chỉ định trong bất kỳ phiên bản đặc tả nào. Sau khi thử nghiệm một số, điều này không hoạt động. –

+0

Điều này có thể làm việc cho Pavol bằng cách sử dụng thứ gì đó xử lý trước yaml (ví dụ: lọc maven-resources-plugin) – DeezCashews

0

Trong một số ngôn ngữ, bạn có thể sử dụng một thư viện thay thế, Ví dụ, tampax là một thực hiện YAML biến xử lý:

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." 
Các vấn đề liên quan