在数据传输交换和配置文件的领域,JSON 和 YAML 是两种常见的格式,它们都可以表示复杂的数据结构和对象。
本文将通过一些具体的例子,介绍 JSON 和 YAML 的历史、语法、用法和区别,帮助你了解这两种格式的特点和适用场景。
JSON 简介
JSON 是一种纯文本的数据交换格式,它的全称是 JavaScript Object Notation,意思是用 JavaScript 语言的对象结构来表示数据。
可见,JSON 是 JavaScript 语言的一个子集,但它不包含 JavaScript 的代码逻辑,只包含数据的键值对。因此,JSON 可以被不同的平台和语言轻松地解析和使用。
相较于其他数据格式,JSON有以下几个优点:
优点 | 描述 |
---|---|
简洁 | JSON使用了很少的标记符号,比XML等其他格式更紧凑 |
易读 | JSON的结构清晰,易于人类阅读和理解。 |
易写 | JSON的语法简单,易于编写和修改。 |
易解析 | JSON可以被JavaScript和许多其他语言直接解析为对象或数组,无需额外的转换步骤。 |
要使用 JSON 格式,我们就必须遵循 JSON 语法。JSON 的语法非常简单,接下来,让我们逐步了解 JSON 的语法。
JSON 语法
键值对
当 JSON 数据是一个对象时,就像 JavaScript 中那样,它由键值对(key-value pair)组成。具体来说,JSON 的每个键值对用冒号(:)隔开,多个键值对用逗号(,)隔开。键值对可以表示一个属性、一个变量、一个参数等。
例如,下面的 JSON 数据表示一个人的信息,包含姓名、年龄、性别三个键值对:
{
"name": "Tom",
"age": 25,
"gender": "male"
}
键
JSON 数据中的键必须是字符串(string),用双引号(")括起来,不能使用单引号(')。键可以是任意合法的 JavaScript 标识符,但不能包含空格或特殊字符。
例如,下面的 JSON 数据中有四个键:name、age、gender、scores:
{
"name": "Alice",
"age": 18,
"gender": "female",
"scores": {
"math": 90,
"english": 85,
"science": 95
}
}
值
JSON 数据中的值可以是以下几种类型:
类型 | 描述 |
---|---|
数字(number) | 可以是十进制整数或小数,不能使用科学计数法或其他进制表示。 |
字符串(string) | 用双引号括起来,可以包含任意字符,包括转义字符(\),例如换行符(\n)、双引号(\")、反斜杠(\)等。 |
布尔值(boolean) | true或false,表示逻辑真或假。 |
空值(null) | null,表示没有值或者未知的值。 |
对象(object) | 用花括号({ })括起来,表示一个由键值对(key-value pair)组成的数据结构。对象可以嵌套其他对象或数组,形成复杂的层次结构。 |
例如,下面的 JSON 数据中有五种类型的值:对象、数字、字符串、布尔值、空值:
{
"id": 123,
"name": "Tom",
"is_admin": true,
"email": null,
"address": {
"city": "New York",
"zip": 10001
}
}
需要强调的是,下面这些格式 JSON 是不支持的:
格式 |
---|
函数(function) |
日期(date) |
正则表达式(regexp) |
undefined |
NaN |
Infinity |
-Infinity |
但我们都知道,JavaScript 中的函数、日期、正则表达式等数据类型都可以转换为字符串,因此,JSON 中可以使用字符串来表示这些类型的数据。
例如,下面的 JSON 数据中使用字符串来表示日期和正则表达式:
{
"birthday": "1990-01-01",
"pattern": "^\\w+@[a-z]+\\.[a-z]+$"
}
数组
上面的例子中的 JSON 数据都是一个对象,事实上,JSON 数据也可以是一个数组(array)。
JSON 数组中的元素必须用方括号([ ])括起来,表示一个有序的数据集合,包含多个元素。数组中的元素可以是任意类型的 JSON 数据,包括对象和数组。
例如,下面的 JSON 数据表示一组颜色,包含红色、绿色、蓝色三个元素:
[
"red",
"green",
"blue"
]
另外,就像刚才介绍的,数组中的元素也可以是对象,例如,下面的 JSON 数据表示一组超级英雄的信息,它包含两个超级英雄:
[
{
"name": "Captain Marvel",
"age": 32,
"secretIdentity": "Carol Danvers",
"powers": [
"Super strength",
"Flight",
"Energy projection"
]
},
{
"name": "Spider-Man",
"age": 18,
"secretIdentity": "Peter Parker",
"powers": [
"Spider sense",
"Web shooting",
"Wall crawling"
]
}
]
编码
JSON 数据必须使用 UTF-8 编码,否则在多语言环境下,可能导致解析错误或乱码。UTF-8 编码可以支持多种语言和字符,包括中文、日文、韩文、阿拉伯文、希腊文等。
拓展阅读
更多有关字符编码的内容,可参阅 了解字符编码:ASCII、UTF-8。
例如,下面的 JSON 数据中使用 UTF-8 编码来表示中文和日文:
{
"language": "日文",
"greeting": "はちみはちみはちみはちみはちみ,はちみを舐めると~"
}
提示
如果你没看到乱码,那基本说明你在浏览本网页时,默认使用了 UTF-8 编码。
格式
JSON 数据标准不允许有注释(comment)。这是为了保持 JSON 数据的简洁和一致性。
例如,下面的 JSON 数据是一个不合法的格式,因为它包含了注释:
{
// 用户信息 (这是一个注释,JSON不允许有注释)
"name": "Tom",
"age": 25
}
把注释去掉就好了:
{
"name": "Tom",
"age": 25
}
JSON 用法
JSON 通常应用于以下场景:
场景 | 描述 |
---|---|
存储数据 | 作为一种轻量级的数据库格式,存储简单的数据结构和对象 |
配置文件 | 作为一种配置文件格式,存储应用程序或系统的设置和参数 |
数据交换 | 作为一种数据交换格式,在不同的平台和语言之间传递和处理数据。例如,Web服务和API通常使用JSON作为响应格式,返回给客户端请求的数据 |
下面我们通过一些具体的例子来介绍 JSON 的用法,并让你对 JSON 有更深入的了解。
存储数据
假设我们要存储一个学生信息表,每个学生有姓名、年龄、性别、成绩等属性。我们可以使用JSON来表示这个数据结构,如下所示:
[
{
"name": "Alice",
"age": 18,
"gender": "female",
"scores": {
"math": 90,
"english": 85,
"science": 95
}
},
{
"name": "Bob",
"age": 19,
"gender": "male",
"scores": {
"math": 80,
"english": 75,
"science": 85
}
},
{
"name": "Charlie",
"age": 20,
"gender": "male",
"scores": {
"math": 70,
"english": 65,
"science": 75
}
}
]
这个 JSON 数据是一个数组,数组中的每个元素是一个对象,对象中的每个键值对表示一个学生的一个属性。 而对象中的值可以是另一个对象,例如 scores 属性的值是一个对象,表示学生的各科成绩。
我们可以将这个 JSON 数据保存为一个文件,例如 student.json
,然后在需要的时候读取和解析这个文件,获取我们想要的数据。
配置文件
现在假设我们有一个Web服务器,自然地,我们需要配置该服务器的基本信息,例如端口号、日志文件路径、静态文件目录等。
我们可以使用 JSON 来表示这些参数,如下所示:
{
"port": 8080,
"log_file": "/var/log/webserver.log",
"static_dir": "/var/www/html"
}
这个 JSON 数据是一个对象,对象中的每个键值对表示一个参数的名称和值。另外,你应该还记得,值可以是数字、字符串、布尔值或空值。
同样地,我们可以将这个 JSON 数据保存为一个文件,例如 webserver.json
,然后在启动Web服务器的时候读取和解析这个文件,设置相应的参数。
数据交换
当我们需要通过 Web 服务器来查询某些数据,例如天气信息时,JSON 也是一个很好的选择。
我们假设某个 Web 服务提供了一个 API 接口,接受一个城市名称作为参数,返回该城市的当前温度、湿度、风速等信息。我们可以使用JSON来表示这个请求和响应的格式,如下所示:
请求:
{
"city": "ShenZhen"
}
响应:
{
"city": "ShenZhen",
"temperature": 25,
"humidity": 60,
"wind_speed": 5
}
注意,请求和响应的 JSON 数据必须使用 UTF-8 编码,并且遵循 HTTP 协议的规范。
总之,我们可以使用 JavaScript 或其他语言来编写客户端程序,发送请求并接收响应,然后解析响应中的JSON数据,获取我们想要的信息。
YAML 是什么?
在上面,我们介绍了JSON的用法,但你有没有觉得 JSON 还是有点复杂? 有没有一种更简洁、易读、易写的数据序列化格式呢?答案是肯定的,那就是 YAML。
YAML 是一种数据序列化格式,它的全称是YAML Ain't Markup Language,意思是:“YAML 不是一种标记语言”。所谓标记语言,就是使用标记来表示数据的语言,例如XML、HTML等。 YAML 最初是为了取代 XML 而设计的,但后来也被用于取代 JSON。YAML 的目标是让人类可以方便地阅读和编写数据,同时也便于被机器解析。
YAML 在日常开发、配置文件、博客文章等领域都有广泛的应用,例如 Docker Compose、Kubernetes、Jekyll、RASA 等都使用了 YAML。下面让我们从学习 YAML 的语法开始,进入 YAML 的世界。
YAML 语法
YAML 的语法非常简单,它主要有以下几个特点:
- YAML 使用缩进来表示层级关系,缩进的空格数目不重要,只要相同层级的元素左对齐即可。
- YAML 支持使用
#
来添加注释,注释必须在行尾或者单独一行。 - YAML 使用冒号
:
来表示键值对,冒号后面必须有一个空格。 - YAML 使用短横线
-
来表示列表项,短横线后面也必须有一个空格。 - YAML 支持使用双引号
"
或者单引号'
来表示字符串,也可以不使用引号,但要注意避免与保留字符冲突。 - YAML 支持使用
|
或者>
来表示多行字符串,|
会保留换行符,而>
会折叠换行符。 - YAML 支持使用
&
和*
来表示锚点和引用,可以实现复用和继承的功能。
目前你只需要对这些要点有个大概的印象即可,下面我们会通过具体的例子来学习 YAML 的语法和用法。
基本语法
我们先来看一个最简单的 YAML 文件,它只包含了一个键值对:
url: http://127.0.0.1:3000
这个文件的含义是:url
是一个键,http://127.0.0.1:3000
是它的值。这个值是一个字符串,它表示一个 URL 地址。
我们可以用 JSON 来表示同样的数据:
{
"url": "http://127.0.0.1:3000"
}
可以看到,YAML 比 JSON 更加简洁,不需要使用花括号、逗号等符号。YAML 也更加易读,因为它使用了缩进和空格来表示结构。
要注意,YAML格式下,键的冒号后面必须有一个空格,这是因为,在没有空格的情况下,冒号后的字符串如果也含有冒号,就会被误认为是键值对的分隔符。
比如,如果我们把上面的文件改成下面这样,就会出错:
# 错误的示范
url:http://127.0.0.1:3000
如果我们想要定义多个键值对,我们只需要在同一层级下添加更多的键值对即可:
name: Alice
age: 18
gender: female
这个文件的含义是:定义了三个键值对,分别是 name
、age
和 gender
。它们的值分别是字符串 Alice
、数字 18
和字符串 female
。我们可以用 JSON 来表示同样的数据:
{
"name": "Alice",
"age": 18,
"gender": "female"
}
可以看到,YAML 和 JSON 的数据结构是一致的,只是表达方式不同。
纯量
在 YAML 中,纯量来示基本的数据类型。纯量就是单个的值,它没有层级关系或者结构。纯量可以是字符串、数字、布尔值、日期、时间等。可见,纯量的范围非常广泛。
我们先来看一个纯量的例子:
pi: 3.14159
这个文件的含义是:定义了一个名为 pi
的键,它的值是一个数字 3.14159
。我们可以用 JSON 来表示同样的数据:
{
"pi": 3.14159
}
如果我们想要定义多个纯量,我们只需要在同一层级下添加更多的键即可:
pi: 3.14159
e: 2.71828
g: 9.8
date: 2020-01-01
这个文件的含义是:定义了四个键,分别是 pi
、e
、g
和 date
。前三个键的值都是数字,分别是 3.14159
、2.71828
和 9.8
;最后一个键 date
的值是日期 2020-01-01
。我们可以用 JSON 来表示同样的数据:
{
"pi": 3.14159,
"e": 2.71828,
"g": 9.8,
"date": "2020-01-01"
}
注意,JSON 中的日期必须使用字符串来表示,而 YAML 中的日期可以直接使用日期类型。
对象
在 YAML 中,我们可以使用对象来表示复杂的数据结构。对象就是由多个键值对组成的集合,它们之间有层级关系。对象可以嵌套对象,也可以嵌套列表(数组)。
我们先来看一个对象的例子:
person:
name: Alice
age: 18
gender: female
这个文件的含义是:定义了一个名为 person
的键,它的值是一个对象。这个对象包含了三个键值对,分别是 name
、age
和 gender
。我们可以用 JSON 来表示同样的数据:
{
"person": {
"name": "Alice",
"age": 18,
"gender": "female"
}
}
如果我们想要定义多个对象,我们只需要在同一层级下添加更多的键即可:
person1:
name: Alice
age: 18
gender: female
person2:
name: Bob
age: 20
gender: male
这个文件的含义是:定义了两个键,分别是 person1
和 person2
。它们的值都是对象,分别包含了三个键值对,分别是 name
、age
和 gender
。我们可以用 JSON 来表示同样的数据:
{
"person1": {
"name": "Alice",
"age": 18,
"gender": "female"
},
"person2": {
"name": "Bob",
"age": 20,
"gender": "male"
}
}
同样地,你会发现 YAML 比 JSON 更加简洁!
数组
在 YAML 中,我们可以使用数组来表示一组有序的数据。数组就是由多个元素组成的列表,它们之间没有键值对的关系。数组可以嵌套数组,也可以嵌套对象。
我们先来看一个数组的例子:
colors:
- red
- green
- blue
这个文件的含义是:定义了一个名为 colors
的键,它的值是一个数组。当然,别忘了,YAML 中的数组也是有层级关系的,它们之间也要缩进。并且,数组的元素前面的短横线 -
后,也要有一个空格。
上面的数组包含了三个元素,分别是字符串 red
、green
和 blue
。我们可以用 JSON 来表示同样的数据:
{
"colors": [
"red",
"green",
"blue"
]
}
可以看到,YAML 使用了短横线来表示数组中的元素,没有方括号和逗号等符号。但事实上,你也可以使用方括号来表示数组:
colors: [red, green, blue]
YAML 中的方括号和短横线是等价的,它们都表示数组。在实际开发中,我们一般会使用短横线,因为这样更加易读。
如果我们想要定义多个数组,我们只需要在同一层级下添加更多的键即可:
colors:
- red
- green
- blue
fruits:
- apple
- banana
- orange
这个文件的含义是:定义了两个键,分别是 colors
和 fruits
。它们的值都是数组,分别包含了三个元素,分别是字符串 red
、green
和 blue
,以及字符串 apple
、banana
和 orange
。我们也可以用 JSON 来表示同样的数据:
{
"colors": [
"red",
"green",
"blue"
],
"fruits": [
"apple",
"banana",
"orange"
]
}
锚点和引用
在 YAML 中有一项强大的功能,叫做锚点和引用。它们可以让我们复用和继承数据。
锚点就是给某个值或者对象起一个别名,引用就是使用这个别名来代替原来的值或者对象。
我们再来看一个纯量的锚点和引用的例子:
port: &port '8080'
server1:
port: *port
server2:
port: *port
这个文件的含义是:定义了一个名为 port
的键,它的值是一个字符串 '8080'
。这个字符串使用了 &port
来表示一个锚点,意思是给这个字符串起了一个别名叫做 port
。
然后定义了两个键,分别是 server1
和 server2
。它们的值都是对象,并且包含了一个键值对 port
。这两个键值对的值都是一个引用,使用了 *port
来表示,意思是把 port
这个字符串的内容作为当前键的值。
我们可以用 JSON 来表示同样的数据,只是 JSON 中没有锚点和引用的概念,因此当我们需要复用的时候,就需要重复在两个 server
的配置中都写上 8080
,并且当我们需要同时修改它们的端口时,也需要修改两次。
{
"port": "8080",
"server1": {
"port": "8080"
},
"server2": {
"port": "8080"
}
}
我们再来看一个对象的锚点和引用的例子:
defaults: &defaults
adapter: postgres
host: localhost
development:
database: dev_db
<<: *defaults
test:
database: test_db
<<: *defaults
这个文件的含义是:定义了一个名为 defaults
的键,它的值是一个对象,这个对象包含了两个键值对,分别是 adapter
和 host
。这个对象使用了 &defaults
来表示一个锚点,意思是给这个对象起了一个别名叫做 defaults
。
然后定义了两个键,分别是 development
和 test
。它们的值都是对象,并且包含了一个键值对 database
。同时,这两个对象都使用了 <<: *defaults
来表示一个引用,意思是把 defaults
这个对象的内容合并到当前对象中。
我们可以用 JSON 来表示同样的数据:
{
"defaults": {
"adapter": "postgres",
"host": "localhost"
},
"development": {
"database": "dev_db",
"adapter": "postgres",
"host": "localhost"
},
"test": {
"database": "test_db",
"adapter": "postgres",
"host": "localhost"
}
}
可见,<<
这个符号的作用是把引用的对象的内容合并到当前对象中,它在 YAML 中被称为“合并键”。
YAML 用法
就像我们一开始介绍的,YAML 的用法非常广泛。下面我们来看一些 YAML 的常见用法。
配置文件
YAML 是一种非常适合作为配置文件的格式,因为它可以表示复杂的数据结构,同时又非常简洁和易读。 许多流行的软件或者框架都使用 YAML 作为配置文件的格式,例如 Docker、Kubernetes、Ansible、Ruby on Rails 等。
例如,下面是一个 Docker Compose 的配置文件,它使用 YAML 来定义了一个服务集合:
version: '3'
services:
web:
image: nginx
ports:
- '80:80'
depends_on:
- app
app:
image: node
volumes:
- .:/app
command: node app.js
这个文件的含义是:定义了一个版本为 3
的配置文件,它包含了两个服务,分别是 web
和 app
。web
服务使用了 nginx
镜像,并且映射了端口 80
。web
服务依赖于 app
服务。app
服务使用了 node
镜像,并且挂载了当前目录到 /app
目录。app
服务执行了命令 node app.js
。
可以看到,YAML 使用了缩进、短横线、冒号等符号来表示服务之间的层级关系和属性。YAML 的优势在于它更加简洁和易读。
数据交换
许多流行的数据交换协议或者格式都支持 YAML ,例如 RESTful API、Swagger、OpenAPI 等。
例如,下面是一个 Swagger 的配置文件,它使用 YAML 来定义了一个 API 文档:
swagger: '2.0'
info:
title: Sample API
description: A sample API for testing
version: '1.0'
paths:
/users:
get:
summary: Get all users
responses:
'200':
description: OK
schema:
type: array
items:
$ref: '#/definitions/User'
definitions:
User:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
email:
type: string
format: email
可以看到,YAML 使用了缩进、冒号、引用等符号来表示 API 文档的结构和内容。
这个文件的含义是:定义了一个版本为 2.0
的 Swagger 文件,它包含了一些基本信息,例如标题、描述、版本等。然后定义了一个路径 /users
,它支持一个 get
操作,用于获取所有用户。这个操作有一个 summary
和一个 responses
。responses
包含了一个 200
状态码,它的描述是 OK
,它的返回值是一个数组,数组的元素是一个 User
对象。最后定义了一个 User
对象,它包含了三个属性,分别是 id
、name
和 email
。
文档生成
下面是一个 Jekyll 的配置文件,它使用 YAML 来定义了一个静态网站的设置:
title: My Blog
description: A personal blog about tech and life
url: https://example.com
theme: minima
plugins:
- jekyll-feed
- jekyll-seo-tag
这个文件的含义是:定义了一个标题为 My Blog
的静态网站,它有一个描述为 A personal blog about tech and life
的静态网站,它的网址是 https://example.com
。它使用了一个主题为 minima
的静态网站,它有两个插件,分别是 jekyll-feed
和 jekyll-seo-tag
。
总结
在这篇博客中,我们学习了 JSON 和 YAML 这两种数据格式的基本知识和应用场景。 我们学习了它们的定义、特点、优势、语法和用途,以及它们与其他数据格式的比较。 我们希望这篇博客能够帮助你更好地使用 JSON 和 YAML 来处理数据和配置文件,提高你的工作效率和质量。
如果你对 JSON 和 YAML 还有任何疑问,欢迎在评论区留言!