在 yaml 中使用占位符
Use placeholders in yaml
有没有办法像这样在 yaml 中使用占位符:
foo: &FOO
<<propname>>:
type: number
default: <<default>>
bar:
- *FOO
propname: "some_prop"
default: "some default"
上下文
- YAML 版本 1.2
- 用户希望
- 在 YAML 中包含变量占位符
- 根据
yaml.load
,将占位符替换为计算值
- 能够对 YAML 映射键和值使用占位符
问题
- YAML 本身不支持变量占位符。
- 锚点和别名几乎可以提供所需的功能,但它们不能用作可以插入整个 YAML 文本的任意区域的可变占位符。它们必须作为单独的 YAML 节点放置。
- 有些 add-on 库支持任意变量占位符,但它们不是本机 YAML 规范的一部分。
例子
考虑以下示例 YAML。它是 well-formed YAML 语法,但是它使用 (non-standard) curly-brace 占位符和嵌入式表达式。
嵌入的表达式不会在 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}}
说明
下面是一个内联图像,用绿色、黄色和红色的彩色区域说明了示例。
GREEN 中标记的替换在标准 YAML 中很容易获得,使用锚点、别名和 merge keys。
在 YELLOW 中标记的替换在技术上可以在标准 YAML 中使用,但并非没有 custom type declaration 或其他一些绑定机制。
RED 中标记的替换在标准 YAML 中不可用。然而,有变通办法和替代方案;例如通过 string formatting 或字符串模板引擎(例如 python 的 str.format
)。
详情
YAML 的一个 frequently-requested 功能是能够插入支持任意 cross-references 的任意变量占位符以及与同一(或 transcluded)YAML 中的其他内容相关的表达式文件。
YAML 支持锚点和别名,但此功能不支持在 YAML 文本中任意放置占位符和表达式。它们仅适用于 YAML 节点。
YAML 也支持 custom type declarations,但是这些不太常见,如果您接受来自可能不受信任的来源的 YAML 内容,则存在安全隐患。
YAML 插件库
有 YAML 扩展库,但它们不是本机 YAML 规范的一部分。
- Ansible
- https://docs.ansible.com/ansible-container/container_yml/template.html
- (支持 YAML 的许多扩展,但它是一个编排工具,如果您只想要 YAML,这就太过分了)
- https://github.com/kblomqvist/yasha
- https://bitbucket.org/djarvis/yamlp
解决方法
- 将 YAML 与模板系统结合使用,例如 Jinja2 或 Twig
- 使用 YAML 扩展库
- 使用来自托管语言的
sprintf
或 str.format
样式功能
备选方案
- YTT YAML Templating 本质上是 YAML 的一个分支,具有可能更接近 OP 中指定目标的附加功能。
- Jsonnet 与 YAML 有一些相似之处,但具有可能更接近 OP 中指定目标的附加功能。
另见
这里是SO
- YAML variables in config files
- Load YAML nested with Jinja2 in Python
- String interpolation in YAML
- how to reference a YAML "setting" from elsewhere in the same YAML file?
- Use YAML with variables
- How can I include a YAML file inside another?
- Passing variables inside rails internationalization yml file
- Can one YAML object refer to another?
- is there a way to reference a constant in a yaml with rails?
- YAML with nested Jinja
SO 外
我想 https://get-ytt.io/ 是您问题的可接受解决方案
使用Yglu结构模板,你的例子可以写成:
foo: !()
!? $.propname:
type: number
default: !? $.default
bar:
!apply .foo:
propname: "some_prop"
default: "some default"
免责声明:我是作者或Yglu。
我也想在 yaml
文件中实现模板化,我发现 作为起点非常有用。经过几个小时的研究和编码,这是我的答案,请告诉我 if/how 我可以改进它。
我没有做任何特别的事情,我尝试利用 python 的字符串模板语法并稍微滥用字符串格式方法。因此,正是 python 的字符串模板和替换在这里发挥了魔力。我修改了 模板化他的 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]}
I have changed {{}}
to {}
and added eval!#
which is an identifier
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)
Note: This function is pretty powerful as this can work on dictionaries which is what JSON/YAML and many other formats convert to in python.
有没有办法像这样在 yaml 中使用占位符:
foo: &FOO
<<propname>>:
type: number
default: <<default>>
bar:
- *FOO
propname: "some_prop"
default: "some default"
上下文
- YAML 版本 1.2
- 用户希望
- 在 YAML 中包含变量占位符
- 根据
yaml.load
,将占位符替换为计算值
- 能够对 YAML 映射键和值使用占位符
问题
- YAML 本身不支持变量占位符。
- 锚点和别名几乎可以提供所需的功能,但它们不能用作可以插入整个 YAML 文本的任意区域的可变占位符。它们必须作为单独的 YAML 节点放置。
- 有些 add-on 库支持任意变量占位符,但它们不是本机 YAML 规范的一部分。
例子
考虑以下示例 YAML。它是 well-formed YAML 语法,但是它使用 (non-standard) curly-brace 占位符和嵌入式表达式。
嵌入的表达式不会在 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}}
说明
下面是一个内联图像,用绿色、黄色和红色的彩色区域说明了示例。
GREEN 中标记的替换在标准 YAML 中很容易获得,使用锚点、别名和 merge keys。
在 YELLOW 中标记的替换在技术上可以在标准 YAML 中使用,但并非没有 custom type declaration 或其他一些绑定机制。
RED 中标记的替换在标准 YAML 中不可用。然而,有变通办法和替代方案;例如通过 string formatting 或字符串模板引擎(例如 python 的
str.format
)。
详情
YAML 的一个 frequently-requested 功能是能够插入支持任意 cross-references 的任意变量占位符以及与同一(或 transcluded)YAML 中的其他内容相关的表达式文件。
YAML 支持锚点和别名,但此功能不支持在 YAML 文本中任意放置占位符和表达式。它们仅适用于 YAML 节点。
YAML 也支持 custom type declarations,但是这些不太常见,如果您接受来自可能不受信任的来源的 YAML 内容,则存在安全隐患。
YAML 插件库
有 YAML 扩展库,但它们不是本机 YAML 规范的一部分。
- Ansible
- https://docs.ansible.com/ansible-container/container_yml/template.html
- (支持 YAML 的许多扩展,但它是一个编排工具,如果您只想要 YAML,这就太过分了)
- https://github.com/kblomqvist/yasha
- https://bitbucket.org/djarvis/yamlp
解决方法
- 将 YAML 与模板系统结合使用,例如 Jinja2 或 Twig
- 使用 YAML 扩展库
- 使用来自托管语言的
sprintf
或str.format
样式功能
备选方案
- YTT YAML Templating 本质上是 YAML 的一个分支,具有可能更接近 OP 中指定目标的附加功能。
- Jsonnet 与 YAML 有一些相似之处,但具有可能更接近 OP 中指定目标的附加功能。
另见
这里是SO
- YAML variables in config files
- Load YAML nested with Jinja2 in Python
- String interpolation in YAML
- how to reference a YAML "setting" from elsewhere in the same YAML file?
- Use YAML with variables
- How can I include a YAML file inside another?
- Passing variables inside rails internationalization yml file
- Can one YAML object refer to another?
- is there a way to reference a constant in a yaml with rails?
- YAML with nested Jinja
SO 外
我想 https://get-ytt.io/ 是您问题的可接受解决方案
使用Yglu结构模板,你的例子可以写成:
foo: !()
!? $.propname:
type: number
default: !? $.default
bar:
!apply .foo:
propname: "some_prop"
default: "some default"
免责声明:我是作者或Yglu。
我也想在 yaml
文件中实现模板化,我发现
我没有做任何特别的事情,我尝试利用 python 的字符串模板语法并稍微滥用字符串格式方法。因此,正是 python 的字符串模板和替换在这里发挥了魔力。我修改了 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]}
I have changed
{{}}
to{}
and addedeval!#
which is an identifier
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)
Note: This function is pretty powerful as this can work on dictionaries which is what JSON/YAML and many other formats convert to in python.