68

有没有办法像这样在 yaml 中使用占位符:

foo: &FOO
    <<propname>>: 
        type: number 
        default: <<default>>

bar:
    - *FOO 
       propname: "some_prop"
       default: "some default" 
4

4 回答 4

145

语境

  • YAML 版本 1.2
  • 用户希望
    • 在 YAML 中包含变量占位符
    • 将占位符替换为计算值yaml.load
    • 能够为 YAML 映射键和值使用占位符

问题

  • YAML 本身不支持变量占位符。
  • Anchors 和 Aliases 几乎提供了所需的功能,但它们不能用作可以插入到整个 YAML 文本中的任意区域的变量占位符。它们必须作为单独的 YAML 节点放置。
  • 有一些附加库支持任意变量占位符,但它们不是本机 YAML 规范的一部分。

例子

考虑以下示例 YAML。它是格式良好的 YAML 语法,但是它使用(非标准)花括号占位符和嵌入式表达式。

嵌入的表达式不会在 YAML 中产生所需的结果,因为它们不是本机 YAML 规范的一部分。尽管如此,它们在此示例中仅用于帮助说明标准 YAML 可用的内容和不可用的内容。

part01_customer_info:
  cust_fname:   "Homer"
  cust_lname:   "Himpson"
  cust_motto:   "I love donuts!"
  cust_email:   homer@himpson.org

part01_government_info:
  govt_sales_taxrate: 1.15

part01_purchase_info:
  prch_unit_label:    "Bacon-Wrapped Fancy Glazed Donut"
  prch_unit_price:    3.00
  prch_unit_quant:    7
  prch_product_cost:  "{{prch_unit_price * prch_unit_quant}}"
  prch_total_cost:    "{{prch_product_cost * govt_sales_taxrate}}"   

part02_shipping_info:
  cust_fname:   "{{cust_fname}}"
  cust_lname:   "{{cust_lname}}"
  ship_city:    Houston
  ship_state:   Hexas    

part03_email_info:
  cust_email:     "{{cust_email}}"
  mail_subject:   Thanks for your DoughNutz order!
  mail_notes: |
    We want the mail_greeting to have all the expected values
    with filled-in placeholders (and not curly-braces).
  mail_greeting: |
    Greetings {{cust_fname}} {{cust_lname}}!
    
    We love your motto "{{cust_motto}}" and we agree with you!
    
    Your total purchase price is {{prch_total_cost}}
    

解释

  • 下面是一个内联图像,说明了带有绿色、黄色和红色彩色区域的示例。

  • 使用锚点、别名和合并键,在标准 YAML 中可以很容易地使用绿色标记的替换。

  • 黄色标记的替换在标准 YAML 中技术上可用,但并非没有自定义类型声明或其他一些绑定机制。

  • 红色标记的替换在标准 YAML 中不可用。然而,有一些变通方法和替代方案;例如通过字符串格式化或字符串模板引擎(例如 python 的str.format)。

解释 YAML 中不同类型的变量替换的图像

细节

YAML 的一个经常被请求的功能是能够插入任意变量占位符,这些占位符支持任意交叉引用和与同一(或嵌入的)YAML 文件中的其他内容相关的表达式。

YAML 支持锚点和别名,但此功能不支持在 YAML 文本中任意位置放置占位符和表达式。它们仅适用于 YAML 节点。

YAML 还支持自定义类型声明,但是这些不太常见,并且如果您接受来自可能不受信任的来源的 YAML 内容,则会存在安全隐患。

YAML 插件库

有 YAML 扩展库,但这些不是本机 YAML 规范的一部分。

解决方法

  • 将 YAML 与模板系统结合使用,例如 Jinja2 或 Twig
  • 使用 YAML 扩展库
  • 使用托管语言的功能sprintfstr.format设置其样式

备择方案

  • YTT YAML 模板本质上是 YAML 的一个分支,具有可能更接近 OP 中指定的目标的附加功能。
  • Jsonnet与 YAML 有一些相似之处,但具有可能更接近 OP 中指定的目标的附加功能。

也可以看看

在这里

外部 SO

于 2017-01-12T18:48:32.460 回答
8

使用Yglu结构模板,您的示例可以编写为:

foo: !()
  !? $.propname: 
     type: number 
     default: !? $.default

bar:
  !apply .foo: 
    propname: "some_prop"
    default: "some default"

免责声明:我是作者或Yglu。

于 2020-02-17T20:52:47.230 回答
4

我想https://get-ytt.io/将是您问题的可接受解决方案

于 2019-11-04T22:53:27.090 回答
1

我也想在yaml文件中实现模板化,我发现dreftymac 的答案作为一个起点真的很有帮助。经过几个小时的研究和编码,这是我的答案,请告诉我是否/如何改进它。

我没有做任何太特别的事情,我尝试利用 python 的字符串模板语法并稍微滥用字符串格式方法。因此,在这里发挥魔力的是所有 python 的字符串模板和替换。我已经修改了dreftymac 的答案模板化他的yaml文件以用作示例的方式。

YAML:

part01_customer_info:
  cust_fname: "Homer"
  cust_lname: "Himpson"
  cust_motto: "I love donuts!"
  cust_email: homer@himpson.org

part01_government_info:
  govt_sales_taxrate: 1.15

part01_purchase_info:
  prch_unit_label: "Bacon-Wrapped Fancy Glazed Donut"
  prch_unit_price: 3.00
  prch_unit_quant: 7
  prch_product_cost: "eval!#{part01_purchase_info[prch_unit_price]} * {part01_purchase_info[prch_unit_quant]}"
  prch_total_cost: "eval!#{part01_purchase_info[prch_product_cost]} * {part01_government_info[govt_sales_taxrate]}"

part02_shipping_info:
  cust_fname: "{part01_customer_info[cust_fname]}"
  cust_lname: "{part01_customer_info[cust_lname]}"
  ship_city: Houston
  ship_state: Hexas

part03_email_info:
  cust_email: "{part01_customer_info[cust_email]}"
  mail_subject: Thanks for your DoughNutz order!
  mail_notes: |
    We want the mail_greeting to have all the expected values
    with filled-in placeholders (and not curly-braces).
  mail_greeting: |
    Greetings {part01_customer_info[cust_fname]} {part01_customer_info[cust_lname]}!

    We love your motto "{part01_customer_info[cust_motto]}" and we agree with you!

    Your total purchase price is {part01_purchase_info[prch_total_cost]}

我已更改{{}}{}添加了eval!#哪个是标识符

Python:

from pprint import pprint
import yaml

EVAL_IDENTIFIER = "eval!#"


def eval_math_expr(val):
    if val.startswith(EVAL_IDENTIFIER):
        val = val.replace(EVAL_IDENTIFIER, "")
        val = eval(val)
    return val


def str_template_substitute(full, val=None, initial=True):
    val = val or full if initial else val
    if isinstance(val, dict):
        for k, v in val.items():
            val[k] = str_template_substitute(full, v, False)
    elif isinstance(val, list):
        for idx, i in enumerate(val):
            val[idx] = str_template_substitute(full, i, False)
    elif isinstance(val, str):
        # NOTE:
        # Templating shouldn't be confused or tasked with extra work.
        # I am attaching evaluation to string substitution here,
        # just to prove this can be done.
        val = eval_math_expr(val.format(**full))
    return val


data = yaml.load(open('./data.yml'))
str_template_substitute(data)

pprint(data)

注意:这个函数非常强大,因为它可以在字典上工作,字典是 JSON/YAML 和许多其他格式在 python 中转换的。

于 2022-01-26T00:18:40.277 回答