有没有办法在YAML中像这样使用占位符:
foo: &FOO
<<propname>>:
type: number
default: <<default>>
bar:
- *FOO
propname: "some_prop"
default: "some default"
有没有办法在YAML中像这样使用占位符:
foo: &FOO
<<propname>>:
type: number
default: <<default>>
bar:
- *FOO
propname: "some_prop"
default: "some default"
yaml.load
时将占位符替换为计算出的值考虑以下示例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规范的一部分。
sprintf
或str.format
样式功能&hello
(别名)和 *hello
(锚点),如此解释:https://idratherbewriting.com/documentation-theme-jekyll/mydoc_yaml_tutorial#example-6-variables - Antônio Medeiros通过 Yglu 结构化模板,您的示例可以这样编写:
foo: !()
!? $.propname:
type: number
default: !? $.default
bar:
!apply .foo:
propname: "some_prop"
default: "some default"
免责声明:我是Yglu的作者。
我想在yaml
文件中实现模板化,我发现dreftymac的答案是一个很好的起点。经过几个小时的研究和编码,这是我的答案,请告诉我如何改进。
我没有做什么特别的事情,我尝试利用Python的字符串模板语法,并稍微滥用字符串格式方法。所以这里的魔法都是由Python的字符串模板和替换完成的。我修改了dreftymac的答案中模板化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!#
的标识符。
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)