yq 编码/解码操作符完全指南:from_yaml、to_json、@base64 等 15 种格式的实战与源码解析
2026/9/14 1:22:56 网站建设 项目流程

yq 编码/解码操作符完全指南:from_yaml、to_json、@base64 等 15 种格式的实战与源码解析

【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yq

导读

yq 是一个可移植的命令行 YAML、JSON、XML、CSV、TOML、HCL 与 properties 处理器。其表达式语言内置了一整套编码(Encode)与解码(Decode)操作符,可以将管道传入的对象结构编码为指定格式的字符串,也可以反向把格式化字符串解码回对象结构。本指南以 pkg/yqlib/doc/operators/headers/encode-decode.md 为骨架,逐一讲解每种格式的完整操作符用法、缩进控制、简写形式,并结合 operator_encoder_decoder.go 与 lexer_participle.go 的源码揭示其底层实现。读完本文,你将掌握在 YAML 文档中内嵌/提取 JSON、YAML、props、CSV/TSV、XML、Base64、URI 与 Shell 字符串的完整实战方案。

一、操作符总览:一张表看清全部编码/解码能力

编码操作符接收管道传入的对象结构,将其编码为目标格式的字符串;解码操作符则执行相反操作,把格式化字符串还原为对象结构。编码函数还可选地接收一个缩进(indent)参数。这些操作符最典型的应用场景是:处理 YAML 文档中那些被字符串化的内嵌 YAML/JSON/props 内容(例如配置项里以字符串保存的片段、CI 日志中的编码数据等)。

格式解码(字符串 → 对象)编码(对象 → 字符串)
Yamlfrom_yaml/@yamldto_yaml(i)/@yaml
JSONfrom_json/@jsondto_json(i)/@json
Propertiesfrom_props/@propsdto_props/@props
CSVfrom_csv/@csvdto_csv/@csv
TSVfrom_tsv/@tsvdto_tsv/@tsv
XMLfrom_xml/@xmldto_xml(i)/@xml
Base64@base64d@base64
URI@urid@uri
Shell@sh

几点值得注意的细节:

  • to_yaml/to_json/to_xml均可传入缩进参数(如to_yaml(8)),而@yaml@json@xml等简写形式固定使用各自的默认缩进;
  • 缩进默认值因格式而异:YAML 默认 2,JSON 默认 2,XML 默认 2,而CSV/TSV/Base64/URI 固定为 0(见 lexer_participle.go 中各 token 的默认 indent);
  • CSV 与 TSV 的格式约定:解析的是"首行作为字段名"的对象数组结构,详细约定可参考仓库内的 csv-tsv 使用文档;
  • XML 使用--xml-attribute-prefix--xml-content-name两个全局 flag来标识属性字段与内容字段(见下文源码解析);
  • Base64 采用 RFC 4648 标准编码,且编码与解码都假定内容是 UTF-8 字符串而非二进制数据。

从词法层看,这些操作符在 lexer_participle.go 中被定义为 token:from_?yaml|@yamld|from_?json|@jsond统一映射到decodeOp(YamlFormat)to_?yaml|@yaml映射到encodeWithIndent(YamlFormat, 2)@json则被特殊定义为encodeWithIndent(JSONFormat, 0)——这正是"@json输出单行 JSON"这一行为的语法层来源。

二、JSON:编码与解码实战

将值编码为 JSON 字符串

假设存在 sample.yml:

a: cool: thing

执行:

yq '.b = (.a | to_json)' sample.yml

输出:

a: cool: thing b: | { "cool": "thing" }

单行 JSON 编码(指定 0 缩进)

传入 0 缩进即可将 JSON 打印为单行:

yq '.b = (.a | to_json(0))' sample.yml

输出:

a: cool: thing b: '{"cool":"thing"}'

单行 JSON 简写形式

@jsonto_json(0)的简写,行为完全一致:

yq '.b = (.a | @json)' sample.yml

输出:

a: cool: thing b: '{"cool":"thing"}'

注意词法层的巧妙设计:to_?json映射为JSONEncode(缩进 2),而@json单独映射为JSONEncodeNoIndent(缩进 0),因此在 yq 中@json天然就是单行输出,无需再写参数。

解码 JSON 字符串

请记住:JSON 是 YAML 的子集。如果你希望得到地道的 YAML 输出(而非带 JSON 风格引号的节点),可以在解码后通过 style 操作符清除 JSON 样式:

yq '.a | from_json | ... style=""' sample.yml

输入:

a: '{"cool":"thing"}'

输出:

cool: thing

源码佐证:decodeOperator(operator_encoder_decoder.go)对每个匹配节点调用format.DecoderFactory()构造解码器、以strings.NewReader(candidate.Value)初始化后逐个Decode(),解码出的节点继承原候选的 Key 与 Parent,从而保持在原文档中的位置。

三、Properties:把 map 编码为键值对字符串

编码为 props 字符串

yq '.b = (.a | @props)' sample.yml

输入:

a: cool: thing

输出:

a: cool: thing b: | cool = thing

解码 props 字符串

yq '.a |= @propsd' sample.yml

输入:

a: |- cats=great dogs=cool as well

输出:

a: cats: great dogs: cool as well

这里使用了|=(更新赋值)语法:将.a的值就地替换为 props 解码结果。与.a = (.a | @propsd)等价,但表达更简洁。

四、CSV / TSV:对象数组与字符串之间的双向转换

解码 CSV 字符串

yq '.a |= @csvd' sample.yml

输入:

a: |- cats,dogs great,cool as well

输出:

a: - cats: great dogs: cool as well

解码 TSV 字符串

yq '.a |= @tsvd' sample.yml

输入(注意字段间是 Tab 分隔):

a: |- cats dogs great cool as well

输出:

a: - cats: great dogs: cool as well

将标量数组编码为 CSV 字符串

这里的"标量"指字符串、数字和布尔值:

- cat - thing1,thing2 - true - 3.40
yq '@csv' sample.yml

输出(含逗号的字段被自动加引号):

cat,"thing1,thing2",true,3.40

将二维数组编码为 CSV 字符串

- - cat - thing1,thing2 - true - 3.40 - - dog - thing3 - false - 12
yq '@csv' sample.yml

输出:

cat,"thing1,thing2",true,3.40 dog,thing3,false,12

将二维数组编码为 TSV 字符串

- - cat - thing1,thing2 - true - 3.40 - - dog - thing3 - false - 12
yq '@tsv' sample.yml

输出:

cat thing1,thing2 true 3.40 dog thing3 false 12

实现细节:CSV 与 TSV 共用同一个底层编码器,区别仅在于使用的偏好配置不同——见 format.go 中CSVFormatTSVFormat均注册了NewCsvEncoder,只是分别绑定ConfiguredCsvPreferencesConfiguredTsvPreferences;解码侧则统一使用NewCSVObjectDecoder。同时 operator_encoder_decoder.go 显示,CSV/TSV 编码结果末尾的换行会被主动去除。

五、YAML:字符串化 YAML 的编码、解码与就地更新

编码为 YAML 字符串

缩进默认值为 2:

yq '.b = (.a | to_yaml)' sample.yml

输入:

a: cool: bob: dylan

输出:

a: cool: bob: dylan b: | cool: bob: dylan

自定义缩进的 YAML 编码

缩进作为第一个参数传入:

yq '.b = (.a | to_yaml(8))' sample.yml

输出:

a: cool: bob: dylan b: | cool: bob: dylan

解码 YAML 字符串

yq '.b = (.a | from_yaml)' sample.yml

输入:

a: 'foo: bar'

输出:

a: 'foo: bar' b: foo: bar

就地更新多行 YAML 字符串

这是最实用的场景之一:把字符串化的 YAML 解码 → 修改字段 → 再编码回字符串,全程使用|=链式管道:

yq '.a |= (from_yaml | .foo = "cat" | to_yaml)' sample.yml

输入:

a: | foo: bar baz: dog

输出:

a: | foo: cat baz: dog

就地更新单行 YAML 字符串

同样的表达式作用于单行字符串时,输出也保持单行:

yq '.a |= (from_yaml | .foo = "cat" | to_yaml)' sample.yml

输入:

a: 'foo: bar'

输出:

a: 'foo: cat'

这里体现了encodeOperator中的一处精细逻辑(operator_encoder_decoder.go):解码时会把原始节点存入名为decoded: <key>的上下文变量,编码完成后若发现原始字符串末尾没有换行,则会用正则chomper\n+$)把编码结果的多余尾部换行去掉——这正是"单行进、单行出"的底层原因。

六、XML:属性前缀、内容字段与缩进

编码为 XML 字符串

yq '.a | to_xml' sample.yml

输入:

a: cool: foo: bar +@id: hi

输出:

<cool id="hi"> <foo>bar</foo> </cool>

注意输入中的键+@id:在默认配置下,+@是 XML 属性前缀,因此+@id: hi被编码为元素<cool>上的属性id="hi"

单行 XML 编码

yq '.a | @xml' sample.yml

输出:

<cool id="hi"><foo>bar</foo></cool>

自定义缩进的 XML 编码

yq '{"cat": .a | to_xml(1)}' sample.yml

输出:

cat: | <cool id="hi"> <foo>bar</foo> </cool>

解码 XML 字符串

yq '.b = (.a | from_xml)' sample.yml

输入:

a: <foo>bar</foo>

输出:

a: <foo>bar</foo> b: foo: bar

XML 属性/内容标识的源码依据

默认的 XML 偏好定义在 xml.go:AttributePrefix默认为+@ContentName默认为+content(用于表示元素内没有属性名时的文本内容)。这两个值可以通过命令行全局 flag 覆盖,注册于 cmd/root.go:

yq --xml-attribute-prefix 'attr_' --xml-content-name '+text' ...

七、Base64:字符串与文档的编解码

字符串编码为 Base64

yq '.coolData | @base64' sample.yml

输入:

coolData: a special string

输出:

YSBzcGVjaWFsIHN0cmluZw==

将整个 YAML 文档编码为 Base64

先通过@yaml把文档转成字符串,再交给@base64

yq '@yaml | @base64' sample.yml

输入:

a: apple

输出:

YTogYXBwbGUK

解码 Base64 字符串

解码后的数据被假定为字符串:

yq '.coolData | @base64d' sample.yml

输入:

coolData: V29ya3Mgd2l0aCBVVEYtMTYg8J+Yig==

输出(UTF-16 内容也能正确还原):

Works with UTF-16 😊

解码 Base64 包裹的 YAML 文档

解码后再用from_yaml解析:

yq '.coolData |= (@base64d | from_yaml)' sample.yml

输入:

coolData: YTogYXBwbGUK

输出:

coolData: a: apple

实现说明:Base64 编码器位于 encoder_base64.go,使用 Go 标准库base64.StdEncoding(即 RFC 4648 标准字符集与填充),且强制要求被编码节点必须是字符串(!!str),否则报错"cannot encode ... as base64"——这解释了为什么编码文档前必须先@yaml转成字符串。

八、URI:URL 编码与解码

字符串编码为 URI

yq '.coolData | @uri' sample.yml

输入:

coolData: this has & special () characters *

输出(空格编码为+,特殊字符百分号编码):

this+has+%26+special+%28%29+characters+%2A

解码 URI 字符串

yq '@urid' sample.yml

输入:

this+has+%26+special+%28%29+characters+%2A

输出:

this has & special () characters *

九、Shell:生成 Shell/Bash 友好字符串

@sh将字符串编码为可直接安全用于 Shell 的转义形式(仅编码、无解码方向,与 format.go 中ShFormat只注册EncoderFactory、Decoder 为 nil 的实现一致):

yq '.coolData | @sh' sample.yml

输入:

coolData: strings with spaces and a 'quote'

输出:

strings' with spaces and a '\'quote\'

十、底层原理:encodeOperator 与 decodeOperator 的实现要点

理解底层实现有助于在复杂管道中预判输出行为,核心逻辑集中在 operator_encoder_decoder.go:

  1. 编码路径encodeOperator从表达式节点取出encoderPreferences{format, indent},经configureEncoder(第 12-32 行)为 JSON/YAML/XML 格式复制一份全局偏好配置并覆写缩进(同时关闭颜色、强制不拆标量),其余格式走各自的format.EncoderFactory()。随后encodeToString通过 Printer 把节点渲染成字符串,最终results.PushBack(candidate.CreateReplacement(ScalarNode, "!!str", stringValue))——编码结果总是以字符串节点形式放回管道,这正是b: '{"cool":"thing"}'这类输出的来源。

  2. 尾部换行处理:编码完成后有三类换行清理规则——原始字符串为单行时去掉尾部换行(保持"单行进单行出");JSON 0 缩进、CSV、TSV 一律去掉尾部换行。

  3. 解码路径decodeOperatorpreferences.format.DecoderFactory()构造解码器,把每个候选节点的字符串值作为输入流初始化解码器并Decode(),同时把原始候选以decoded: <key>变量存入上下文供编码侧引用;解码节点继承原 Key 与 Parent,因此.a |= @propsd等就地更新能精准落位。

  4. 格式注册表:format.go 中的Formats列表统一注册了 yaml、kyaml、json、props、csv、tsv、xml、base64、uri、sh、toml、hcl、shell、lua、ini 共 15 种格式的编码/解码工厂。本文讨论的编码/解码操作符与--output-format/--input-format共享同一套工厂机制,因此行为天然一致——to_yaml的产物与yq -o=yaml的输出规范相同。

十一、配套测试与延伸阅读

本主题的所有示例均有对应的自动化测试支撑,位于 operator_encoder_decoder_test.go,测试函数TestEncoderDecoderOperatorScenarios逐一断言了 JSON/YAML/props/CSV/TSV/XML/Base64/URI/Shell 共 30 余个场景(含"空字符串 base64 解码""缺失 padding 的 base64""@base64 与 @base64d 往返"等边界用例),部分场景还通过skipDoc标记为文档外补充测试,例如@sh对连续空引号''的特殊转义行为。

若要继续深入,可结合以下仓库资源:

  • encode-decode 文档:本文对应的正式文档页;
  • csv-tsv 使用文档:CSV/TSV 可接受格式的完整约定;
  • convert 使用文档:跨格式整体转换场景;
  • xml 使用文档:XML 属性、命名空间与内容字段的完整说明;
  • base64 使用文档:Base64 与文档处理的更多组合技巧。

将这些操作符与 yq 的管道、|=赋值和 style 操作符组合使用,即可在单个命令行表达式中完成"解码→修改→再编码"的完整数据变换闭环,是处理配置模板、内嵌片段与编码载荷的利器。

【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yq

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询