【HASS初级教程】3-YAML语法和JSON(上)
-------施工中------
未完成:解释性图片、简化和优化表述。
上一篇教程中,我们了解了HASS运行的基本原理,特别是核心、组件的概念,并且,已经能够从这个角度来理解配置文件的整个配置过程。
但是,若仅依靠对核心和组件的认知,以及参考官网组件文档,还是不可能彻底掌握配置文件的编写技巧。我们必须通过比较系统地学习YAML格式来准确掌握配置文件的编写规范。本篇教程将重点介绍字典、列表和缩进。
一、YAML和JSON概述
YAML简介
一般的标签语言通过标签对数据进行格式化,以便机器能够理解其中的含义。
YAML语言几乎不使用标签,而是利用文件内容本身的语法结构,对数据进行格式化。
YAML文件的语法结构非常严谨和重要,文档的可读性高,所以非常适合编写配置文件。
JSON简介
JSON和YAML一样,是一种实现数据序列化的方法,并且YAML和JSON可以相互转换。
YAML1.2规范中兼容了JSON,所以在HASS的YAML文件中,我们可以直接使用JSON替代标准的YAML语法。
YAML的基本规则
- 大小写敏感
- 禁止使用TAB,必须用空格缩进
- 字符串可以不用引号标注
- 英文和符号必须使用半角字符
#
是唯一的注释方法
二、 字典结构★
1. 字典的概念
字典是一种重要的数据结构,字典(dict)这个词来源于Python等语言,YAML官方称为映射(mapping)、JSON称为对象(object)、也叫做哈希(hashes)或者散列表等。
2. 字典的特性
没有编程基础?没事,你只要按照字面意思去理解就可以!
字典就是一本工具书,里面包含很多个关键字(KEY,简称字),每个关键字对应着一段解释(Value,简称值)。
楼主认为,字典结构拥有以下特性:
1. 成对性
1个字对应1个值,字和值必然成对出现,我把这样的一对组合叫做字典的字值对
(后来发现阮一峰老师把它叫做键值对)。
2. 组合性
字值对
是构成字典的数据项(成员),若干个并列的字值对
可以组成一本字典;只含有1个字值对
的字典,本教程特称为单字典
。
3. 无序性
字典的字值对
之间平等并列,不讲究先后顺序,字典中字值对
排列的顺序不影响字典的使用和含义。
4. 唯一性
在一本字典中,字是唯一和不可重复的,不可能出现相同的字,并且字必须是一个字符串,不能是其它类型。比如,你不可能在一本字典的多个地方找到同一个字的解释。至少,在HASS中这样是不允许的。
5. 任意性
在一本字典中,值可以是任意的类型,包括复合数据类型(字典、列表)。比如,值可以是一本字典,从而构成复合字典。
3. 字典的写法
1. YAML标准写法★
name: Mirukuteii
gender: male
age: 34
···
解释:字+冒号+空格+值=字值对,N个并列的字值对构成字典。
注意:半角英文符号。YAML默认接受字符串类型,无需引号(除非使用Jinja代码,否则一般不需要为字符串类型加上引号)。
2. JSON写法
{"name":"Mirukuteii","gender":"male","age":34, ...}
解释:字+冒号+值=字值对,字值对之间通过逗号隔开,外面套上大括号,从而构成字典。
注意:半角英文符号。字必须加上引号,值的内容若为字符串也需要加上引号。冒号和逗号后面加不加空格都没关系。
为了弄明白字典这个重要概念,我们再回到上一篇教程的配置文件中进行分析:
homeassistant: # 调用核心组件
name: Hello # 配置该组件的name项,值为Hello
latitude: 30 # 配置该组件的latitude项,值为30
longitude: 120 # 配置该组件的logitude项,值为120
elevation: 0 # 配置该组件的elevation项,值为0
time_zone: Asia/Shanghai # 配置该组件的time_zone项,值为Asia/Shanghai
frontend: # 调用前端组件
例1: 典型的字值对
单独看第2行:
name: Hello
分析:这是一个典型的字值对
,它的字
为字符串"name",值
为字符串"Hello"。若找不到与其并列的字值对,我们也可以把这段代码看作为一本单字典(该字典
只有1个字值对
)。
例2: 典型的字典
接着,看第2-6行:
name: Hello
latitude: 30
longitude: 120
elevation: 0
time_zone: Asia/Shanghai
分析:这是一个典型的字典
,它是由5个并列的字值对
构成的。
例3: 典型的嵌套字典
,也是一个单字典
然后,看第1-6行:
homeassistant: # 调用核心组件
name: Hello # 配置该组件的name项,值为Hello
latitude: 30 # 配置该组件的latitude项,值为30
longitude: 120 # 配置该组件的logitude项,值为120
elevation: 0 # 配置该组件的elevation项,值为0
time_zone: Asia/Shanghai # 配置该组件的time_zone项,值为Asia/Shanghai
分析:这是一个典型的嵌套字典
,这个字典
只有1个字值对
,它的字
为homeassistant
,值
为例2中分析的那本字典
。(字典
作为字值对
的值
,从而构成嵌套字典
);同时,该字典
只有1个字值对
,所以属于单字典
。
例4: 特殊的字典
,也是一个单字典
然后,我们单独分析第7行
frontend:
分析:这是一个特殊的字典
,它看上去没有值。这样不是格式错误么?答案是否定的。在HASS中,组件及其配置项并不是由配置文件构建的,而是已经存在的。实际上,frontend组件是存在配置项并且所有的配置项是有默认值的。所以,frontend组件的值一旦留空,系统就会使用默认值来配置这个组件,并不会影响到组件的加载和正常工作。实质上它是一本隐形的嵌套字典
,因为它的值其实是一本隐藏的字典,这里暂不讨论,以免引起概念混淆。
例5: 典型的嵌套字典
,配置文件的本质结构
最后,我们看完整的配置文件
homeassistant: # 调用核心组件
name: Hello # 配置该组件的name项,值为Hello
latitude: 30 # 配置该组件的latitude项,值为30
longitude: 120 # 配置该组件的logitude项,值为120
elevation: 0 # 配置该组件的elevation项,值为0
time_zone: Asia/Shanghai # 配置该组件的time_zone项,值为Asia/Shanghai
frontend: # 调用前端组件
分析:这是一个典型的嵌套字典
,它有2个字值对
,对应的字分别为homeassistant
和frontend
。第1个字值对的值,又由1个字典构成,这个字典有5个字值对
。
例5是一个完整的配置文件,也就是说:HASS的配置文件
本质上就是一本字典
!
本章思考题:
frontend: # 调用前端组件
homeassistant: # 调用核心组件
elevation: 0 # 配置该组件的elevation项,值为0
latitude: 30 # 配置该组件的latitude项,值为30
longitude: 120 # 配置该组件的logitude项,值为120
time_zone: Asia/Shanghai # 配置该组件的time_zone项,值为Asia/Shanghai
name: Hello # 配置该组件的name项,值为Hello
以上代码和例5相比,有没有区别?
答案:没有区别,因为字典结构是无序的,改变字值对的顺序,并不会影响字典的意义。
二、 列表结构
1. 列表的概念
列表也是一种重要的数据结构,列表(list)这个词来源于Python等语言,YAML官方称为序列(Sequence)、JSON称为数组(Array)。
2. 列表的特性
列表比字典更易理解,就是数据的有序集合。列表也是由数据项(成员)组成的复合结构,每个数据项叫做列表的元素
,元素
就是单纯的数据,多个元素
按序存放在一个整体结构中,就成了列表。你可以把列表理解为一个大箱子,箱子里有N个按序号排列的格子,每个格子都存放了一个数据。
楼主认为,列表结构拥有以下特性:
1. 有序性
列表的元素
按照先后顺序排列,元素
的顺序不同,列表的含义也不同。不过,楼主认为HASS配置文件中的列表这一特性可以忽略。
2. 组合性
元素
是构成列表的数据项(成员),若干个元素
按照顺序排列组成一个列表;只含有1个元素
的列表,本教程特称为单列表
。
3. 任意性
在一个列表中,元素
可以是任意的类型,包括复合数据类型(字典、列表)。比如,元素
可以是一本字典,从而实现嵌套的复合结构。
3. 列表的写法
1. YAML标准写法★
- apple
- banana
- orange
···
解释:短横+空格+元素内容=元素,N个顺序的元素构成列表。
注意:半角英文符号。YAML默认接受字符串类型,无需引号(除非使用Jinja代码,否则一般不需要为字符串类型加上引号)。
2. JSON写法
["apple","banana","orange", ...]
解释:元素内容=元素,元素之间通过逗号隔开,外面套上中括号
,从而构成列表。
注意:半角英文符号。字符串类型的元素必须加上引号。
我们来看看下面这段例子
homeassistant:
whitelist_external_dirs:
- /usr/var/dumping-ground
- /tmp
以上例子的含义是为HASS指定外部访问目录的白名单
列表,也就是说这些目录将通过HASS前端暴露在网络上供人访问其中的资源。
这里只是作为编写配置文件的例子,没有特殊情况,请不要在实际使用中添加白名单,以免私密数据外泄。
下面按照YAML语法分析:
例子是一本单字典,这个单字典只有一个字值对,其值又是一本单字典。而这第二本单子典的值,则是一个列表。
即:
- /usr/var/dumping-ground
- /tmp
这个列表有2个元素,第一个元素为/usr/var/dumping-ground
,第二个元素为/tmp
。
列表的元素是有先后顺序的,但是在HASS的实际配置中,这种先后顺序不一定是有意义的。我们可以认为HASS中,列表元素是平等并列的,交换位置,不会改变总体的含义,不过在涉及前端等的配置中,这种先后顺序可能会比较明显的表现出来,比如通过group组件显示的实体位置次序。
在HASS中,列表结构不大可能单独使用。绝大多数情况下,列表是作为字典的字值对的值而存在。其意义我们将在下一篇教程中嵌套部分重点叙述。
本章思考题:
homeassistant:
whitelist_external_dirs:
- /usr/var/dumping-ground
- /tmp
以上代码如何转换为JSON格式?
答案:
{"homeassistant":{"whitelist_external_dirs":["/usr/var/dumping-ground","/tmp"]}}
三、 缩进的意义
1. 缩进的概念
缩进本没有特别的意义,只是为了提高可读性所作的一种人为的修饰方式。
但是,YAML中的缩进(借鉴于python语法)是决定数据结构的重要方法,请务必重视!
缩进就是管理行代码之间相互关系的一种语法。
2. 缩进的特性
缩进本质上不是一种数据结构,而用于构造数据结构的辅助方法。缩进的特性我归纳为以下方面。
1. 辅助性
缩进不是某种数据结构,必须依存在某种数据结构的语境中,但是不同的缩进方式将构建含义不同的数据结构。
2. 叠加性
缩进是不断叠加的,在结构完成构建之前,这种叠加效应会持续下去,所以修改上方代码的缩进会影响下方代码缩进的意思表达。
3. 可读性
使用缩进编写的YAML文件,可以很直观地看出行代码之间的层次关系。
缩进的写法:
1. YAML标准写法
name:
Hello
解释:(若干个空格)数据代码...,缩进就是每行内容前的空格数量。
注意:半角英文符号。
2. JSON写法
没有写法,但是缩进相当于json中{
、}
、[
、]
、,
等符号的具体位置。
解释:缩进就是对行代码的层次管理。
注意:半角英文符号。
缩进的意义:
1. 有效代码行
首先,我们把有意义的一行YAML代码,称为一个有效的代码行。
相对于有效的代码行而言,以下代码行是无效的:
- 只含有非可见字符(空格符、换行回车符等)的代码行,是无效的代码行。
-
由#字符作为该行首个可见字符的代码行,也是无效的代码行,因为其后所有内容都被注释了。
这里的无效,指的是不能够被YAML所解析,并非指的是毫无作用。
例如,下面的2行就是无效的代码行。
# 我是无效的代码行
2. 意义
缩进,或者说某行代码的缩进,表示该行与其上方有效代码行之间的关系。
就上面这个例子来分析:
Hello这行代码,相对于上一个有效的代码行,即name:,缩进了2个空格。
所以,Hello这行代码,属于上一行结构的一部分。
第2个例子:
fruit:
- apple
- banana
- orange
分析:
- banana这行代码,相对于上一个有效的代码行- apple,没有缩进。
所以,- banana这行代码与- apple是并列的,同理可得- orange也是一样。
而- apple这行代码,相对于上一个有效的代码行fruits:,缩进了2个空格,
所以,- apple这行代码,是属于fruits: 这行结构的一部分。
并且,由于- banana和- orange与- apple并列,所以它们都是fruits: 这行代码的一部分。
第3个例子:
fruit:
- apple
vegetables:
- tomato
- potato
分析:
- apple,相对于上一个有效的代码行fruit: ,缩进2个空格,所以是属于fruit: 结构的一部分。
- tomato、- potato,两行代码的缩进相同,所以并列,并且相对于vegetables:,缩进了2个空格,所以属于vegetables:结构的一部分。
而- apple、- tomato、- potato,这3行代码,虽然单独看这3行的缩进相同,但是- apple和- tomato这2行代码之间,并不能建立并列的关系,因为在这两行代码之间,有缩进层次更高的vegetables:,使得- tomato、- potato已经被包含进vegetables:的结构中,不可能再和- apple产生关系。
所以,2个缩进较多、层次较低的行代码之间,如果有缩进更少、层次更高的行代码,则这2行代码必然无法再产生关联性。
最后,fruit: 、vegetables:两行代码是并列的关系。
以上的例子,用JSON表示,即:
{'fruit':['apple'], 'vegetavles':['tomato','potato']}
思考:为什么说,缩进在JSON中指的是[].{}这些符号的具体位置。