日期补全

在数据统计时,有时候会出现某天没有数据的情况,比如某天某类产品销量为0,这样就没有真实订单。我们统计的时候就可能出现下面的数据:

1
2
3
4
5
    dt     | val
----------------
2023-05-06 | 12
2023-05-08 | 80
2023-05-10 | 110

我们看到在上面的数据里,5月7日和5月9日的数据是缺失的。业务上往往要求,我们要把缺失的补0.也就是需要得到下面的结果:

1
2
3
4
5
6
7
    dt     | val
----------------
2023-05-06 | 12
2023-05-07 | 0
2023-05-08 | 80
2023-05-09 | 0
2023-05-10 | 110

把5月7日和5月9日的数据补上,那么怎么实现呢?先看完整的SQL:

1
2
3
4
5
6
7
8
9
10
11
12
with a as (select '2023-05-06' as dt, 12 as val union select '2023-05-10',110 union select '2023-05-08',80),
b as (select max(dt) max_dt,min(dt) min_dt from a),
z as (select DATE(DATE_SUB(CURDATE(),INTERVAL rn day)) as dt from
(SELECT ROW_NUMBER() over() as rn
FROM
(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) a
,(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) b
,(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) c
) t
),
t as (select z.* from z,b where dt>=b.min_dt and dt <= b.max_dt )
select t.dt,ifnull(a.val,0) as val from t left join a on t.dt = a.dt order by t.dt;

看到上面的SQL 是不是有一种要放弃的感觉。太长了,确实太长了。我们来解析一下 都是干嘛的。我们这里用的是MySQL with 的语法,这个语法的好处就是可以把SQL写的贼长。然后还能年的懂。

先看:

1
with a as (select '2023-05-06' as dt, 12 as val union select '2023-05-10',110 union select '2023-05-08',80)

这一段就是我们造三行数据,实际业务中应该用真实的业务表数据替换。接下来

1
b as (select max(dt) max_dt,min(dt) min_dt from a),

这个就是找到业务里最大的日期,和最小的日期。以方便后面的补全。然后就是特别长的 z

1
2
3
4
5
6
7
8
z as (select DATE(DATE_SUB(CURDATE(),INTERVAL rn day)) as dt from
(SELECT ROW_NUMBER() over() as rn
FROM
(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) a
,(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) b
,(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) c
) t
),

这一段就是 获取最近1000天的日期。 这里用三个 select 0 一直 union 到9的表,三个表每个表有10个数字。101010 就等于1000. 然后通过 ROW_NUMBER() over() 获得行号,也就是1 至 1000 。再通过 DATE(DATE_SUB(CURDATE(),INTERVAL rn day)) as dt 转成日期。因为我们不需要这么的日期,所以需要限定一下日期的数据,也就是

1
t as (select z.* from z,b where dt>=b.min_dt and dt <= b.max_dt ) 

到了这里 就只会返回 最小日期和最大日期之间的日期了。然后 做一下表关联和排序。也就是

1
select t.dt,ifnull(a.val,0) as val from t left join a on t.dt = a.dt order by t.dt;

这样我们就得到了想要的,日期补全后的数据了。

长度优化

看到这里,有的朋友要说了,这个SQL也太长了,能不能精简一点。我们看上面的 1 到 1000 这一步,确实太长了。我们可以用视图来优化一下。我们先创建一个视图:

1
2
3
4
5
create view vn as SELECT ROW_NUMBER() over() as rn
FROM
(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) a
,(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) b
,(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) c

这样一个 1 到 1000 的视图就创建好了。前面的SQL 也可以简化为:

1
2
3
4
5
with a as (select '2023-05-06' as dt, 12 as val union select '2023-05-10',110 union select '2023-05-08',80),
b as (select max(dt) max_dt,min(dt) min_dt from a),
z as (select DATE(DATE_SUB(CURDATE(),INTERVAL rn day)) as dt from vn),
t as (select z.* from z,b where dt>=b.min_dt and dt <= b.max_dt )
select t.dt,ifnull(a.val,0) from t left join a on t.dt = a.dt order by t.dt;

月份数据补全

对于数据:

1
2
3
4
5
    dt     | val
----------------
2023-04 | 12
2023-06 | 110
2023-07 | 80

我们可以通过下面的SQL来对期进行补全:

1
2
3
4
5
with a as (select '2023-04' as dt, 12 as val union select '2023-06',110 union select '2023-07',80),
b as (select max(dt) max_dt,min(dt) min_dt from a),
z as (select date_format(DATE_SUB(CURDATE(),INTERVAL rn month),'%Y-%m') as dt from vn),
t as (select z.* from z,b where dt>=b.min_dt and dt <= b.max_dt )
select t.dt,ifnull(a.val,0) from t left join a on t.dt = a.dt order by t.dt;

运行上面的SQL,得到:

1
2
3
4
5
6
    dt     | val
----------------
2023-04 | 12
2023-05 | 0
2023-06 | 110
2023-07 | 80

对于 月份的处理,我们用到了 date_format(DATE_SUB(CURDATE(),INTERVAL rn month),’%Y-%m’) 来把月份变成 2023-05 这样的一个格式。
这个方法,各位觉得怎么样?欢迎留言讨论

最近在查一个SQL Server 事务引起的表锁问题,折腾好久才发现要开启 快照读取模式。

1
2
3
4
5
6
-- 查询数据库是否开启了 快照读取模式
select name, is_read_committed_snapshot_on from sys.databases

-- 打开 快照读取模式
ALTER DATABASE my_db SET READ_COMMITTED_SNAPSHOT ON

直接看SQL

1
2
3
4
5
6
7
8
select DATE(DATE_SUB(CURDATE(),INTERVAL rn day)) as date from
(SELECT ROW_NUMBER() over() as rn
FROM
(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) a
,(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) b
,(SELECT 0 AS N UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) c
) t

返回结果

1
2
3
4
5
6
7
8
9
2023-06-26
2023-06-25
2023-06-24
2023-06-23
...省略N行
2020-10-03
2020-10-02
2020-10-01
2020-09-30

Nutuml 测试

时序图

a -> b : hahah
b --> a: ooo

思维导图

# root
## first
## second

我们在业务中常常需要统计这个月销售量多少,同比增加多少,环比增加多少。这篇博文我们就看看如何利用窗口函数实现同比及环比的计算。
用到的关键字包括:lag, window

准备工作

首先我们得有MySQL 8.0及以上版本, 然后我们准备一张统计表。

1
2
3
4
CREATE TABLE `my_stat` (
`month` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
`profit` decimal(10,2) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

表里的内容如下:

month profit
2022-05 10.00
2022-06 12.00
2022-07 6.00
2022-08 30.00
2022-09 20.00
2022-10 8.00
2022-11 2.00
2022-12 4.00
2023-01 14.00
2023-02 10.00
2023-03 8.00
2023-04 9.00
2023-05 7.00
2023-06 10.00
2023-07 15.00

数据 插入的SQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
INSERT INTO `my_stat` (`month`, `profit`)
VALUES
('2022-05', 10.00),
('2022-06', 12.00),
('2022-07', 6.00),
('2022-08', 30.00),
('2022-09', 20.00),
('2022-10', 8.00),
('2022-11', 2.00),
('2022-12', 4.00),
('2023-01', 14.00),
('2023-02', 10.00),
('2023-03', 8.00),
('2023-04', 9.00),
('2023-05', 7.00),
('2023-06', 10.00),
('2023-07', 15.00);

环比计算

先看最终SQL及输出结果

1
2
3
4
5
6
select `month`,profit,
lag(profit) OVER w AS 上月,
(profit - lag(profit) OVER w) as 环比,
100*(profit - lag(profit) OVER w)/lag(profit) OVER w as 环比比例
from my_stat
WINDOW w AS (ORDER BY `month`);

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
month|profit|上月|环比|环比比例
--|--|--|--|--
2022-05|10.00|NULL|NULL|NULL
2022-06|12.00|10.00|2.00|20.000000
2022-07|6.00|12.00|-6.00|-50.000000
2022-08|30.00|6.00|24.00|400.000000
2022-09|20.00|30.00|-10.00|-33.333333
2022-10|8.00|20.00|-12.00|-60.000000
2022-11|2.00|8.00|-6.00|-75.000000
2022-12|4.00|2.00|2.00|100.000000
2023-01|14.00|4.00|10.00|250.000000
2023-02|10.00|14.00|-4.00|-28.571429
2023-03|8.00|10.00|-2.00|-20.000000
2023-04|9.00|8.00|1.00|12.500000
2023-05|7.00|9.00|-2.00|-22.222222
2023-06|10.00|7.00|3.00|42.857143
2023-07|15.00|10.00|5.00|50.000000

lag

lag 函数的完整定义如下:

1
LAG(expr [, N[, default]]) [null_treatment] over_clause
  • 参数一 expr(表达式)是必须的,这里可以是字段名,如上面的示例SQL,也可以是其它运算表达式。

  • 参数二 N 是可选的,必须是大于等于0的整数。默认1,1表示上一行,0表示当前行,12表示前12行,后面计算同比时会用到

  • 参数三 是默认值,比如第一行的 上一行是不存在的,这个时候 返回什么值就可以通过参数三来控制,默认是 NULL

  • null_treatment 的定义是处理NULL的策略,但是因为MySQL只实现了 RESPECT NULLS 并且作为默认值,就感觉它只是提醒我们 计算结果需要考虑NULL

over_clause 是必须的,它描述窗口的定义, 如上面的SQL示例最后一行:

1
WINDOW w AS (ORDER BY `month`);

这里定义了一个 窗口(window), 基于 month这个字段升序。

window

上一节的 over_clause 定义如下:

1
2
over_clause:
{OVER (window_spec) | OVER window_name}

我们看到,可以 over (窗口定义) 或者 over 窗口名称。

1
lag(profit) OVER w AS 上月

这就是一个 over 窗口名称的示例。

接下来我们看 window_spec 的定义:

1
2
window_spec:
[window_name] [partition_clause] [order_clause] [frame_clause]
  • window_name 就是给窗口起个名字,方便使用
  • partition_clause 分区定义, 就是定义查询结果如何分组,有点类似group by 的意思。
  • order_clause 排序定义,定义窗口内容排序字段及方式,如前面示例里的 order by `month` 。如果省略了order by , 那就按查询到结果的顺序来排序。
  • frame_clause frame_clause(框架子句)指定如何定义子集

同比环比计算

还是先看最终SQL

1
2
3
4
5
6
7
8
9
10
11
select `month`,profit,
lag(profit) OVER w AS 上月,
(profit - lag(profit) OVER w) as 环比,
100*(profit - lag(profit) OVER w)/lag(profit) OVER w as 环比比例,

lag(profit,12) OVER w AS 上年同月,
(profit - lag(profit,12) OVER w) as 同比,
100*(profit - lag(profit,12) OVER w)/lag(profit,12) OVER w as 同比比例

from my_stat
WINDOW w AS (ORDER BY `month`);

返回结果

month profit 上月 环比 环比比例 上年同月 同比 同比比例
2022-05 10.00 NULL NULL NULL NULL NULL NULL
2022-06 12.00 10.00 2.00 20.000000 NULL NULL NULL
2022-07 6.00 12.00 -6.00 -50.000000 NULL NULL NULL
2022-08 30.00 6.00 24.00 400.000000 NULL NULL NULL
2022-09 20.00 30.00 -10.00 -33.333333 NULL NULL NULL
2022-10 8.00 20.00 -12.00 -60.000000 NULL NULL NULL
2022-11 2.00 8.00 -6.00 -75.000000 NULL NULL NULL
2022-12 4.00 2.00 2.00 100.000000 NULL NULL NULL
2023-01 14.00 4.00 10.00 250.000000 NULL NULL NULL
2023-02 10.00 14.00 -4.00 -28.571429 NULL NULL NULL
2023-03 8.00 10.00 -2.00 -20.000000 NULL NULL NULL
2023-04 9.00 8.00 1.00 12.500000 NULL NULL NULL
2023-05 7.00 9.00 -2.00 -22.222222 10.00 -3.00 -30.000000
2023-06 10.00 7.00 3.00 42.857143 12.00 -2.00 -16.666667
2023-07 15.00 10.00 5.00 50.000000 6.00 9.00 150.000000

同比与环比的差别其实不大,只是 lag(profit)变成了 lag(profit,12)。 lag(profit,12)表示取前12行的数据。

需要注意的是计算同比的时候,我们需要保证数据是连续的,不然数据会有偏差,因为这里的lag(profit,12)取的是前12行的数据,如果月份数据有缺失就可以取错数据。比如:如果少了一个月,那么前12行取的可能就是去年上个月的数据了。

partition_clause 聊聊分区

我们先准备一下数据,先创建一张表 my_stat_food

1
2
3
4
5
CREATE TABLE `my_stat_food` (
`month` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
`type` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL,
`profit` decimal(10,2) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;

然后插入数据

1
2
3
4
5
6
7
8
9
10
11
INSERT INTO `my_stat_food` (`month`, `type`, `profit`)
VALUES
('2023-05', '蔬菜', 1.00),
('2023-05', '水果', 2.00),
('2023-05', '肉类', 3.00),
('2023-06', '蔬菜', 4.00),
('2023-06', '水果', 5.00),
('2023-06', '肉类', 6.00),
('2023-07', '蔬菜', 7.00),
('2023-07', '水果', 8.00),
('2023-07', '肉类', 9.00);

定义

1
2
partition_clause:
PARTITION BY expr [, expr] ...

然后我们试试pratition的效果

1
select *,lag(`profit`) over (partition by `type`) as lp from my_stat_food

输出:

month type profit lp
2023-05 水果 2.00 NULL
2023-06 水果 5.00 2.00
2023-07 水果 8.00 5.00
2023-05 肉类 3.00 NULL
2023-06 肉类 6.00 3.00
2023-07 肉类 9.00 6.00
2023-05 蔬菜 1.00 NULL
2023-06 蔬菜 4.00 1.00
2023-07 蔬菜 7.00 4.00

我们的数据是按月分排序的,但我们看到通过 partition进行分区后,就会分区来返回内容了。这样我们就可以通过分区来统计不同品类的环比。

问题

  1. 上面的给数据都是按月统计好的数据。但原始数据往往是一笔笔订单,那么如何通过原始数据生成每个月的统计数据呢?
  2. 如何保证数据的连续性?在业务上每个月的数据往往是有保证的,毕竟一个月一单不成交的话,也没有统计的必要了。但是考虑到天呢?可以一些小店一天一个成交也没有,也是正常的。那么如何实现如果某天没有数据,统计的时候让那天的统计数据变成0呢?

参考

  1. https://dev.mysql.com/doc/refman/8.0/en/window-function-descriptions.html

在go语言的标准库里,可以通过time来处理日期和时间。我们需要引用标准库

1
import "time"

1. 获取当前时间

1
2
3
4
5
6
7
now := time.Now()
fmt.Println(now)
// 当前时间戳,1970年1月1日到现在的秒数
fmt.Println(" 秒", now.Unix())
fmt.Println("豪秒", now.UnixMilli())
fmt.Println("微秒", now.UnixMicro())
fmt.Println("纳秒", now.UnixNano())

输出

1
2
3
4
5
2023-04-13 13:10:17.8577739 +0800 CST m=+0.004403301
秒 1681362617
豪秒 1681362617857
微秒 1681362617857773
纳秒 1681362617857773900

time.Now 返回的结构是 time.Time

1
2
3
4
5
type Time struct {
wall uint64
ext int64
loc *Location
}

按go的约定,这个结构里的数据都是私有变量,对于我们使用来说没有价值。

2. 日期时间格式化

特别注意:go的格式化字符串不是常见的 yy-MM-dd HH:mm:ss,而是2006-01-02 15:04:05 Go的成立日期。

1
2
fmt.Println(now.Format("2006-01-02 15:04:05"))
fmt.Println(now.Format("06-1-2 3:4:5"))

输出

1
2
2023-04-13 14:21:21
23-4-13 2:21:21

格式化字符说明:

字符 说明
1 月份
01 月份,保证两位数字,1月会输出01
2 日期
02 日期,保证两位数字,2日会输出02
3 小时,12小时制
03 小时,保证两位数字,3时会输出03
15 小时,24小时制,保证两位数字,3时会输出03
4 分钟
04 分钟,保证两位数字,4分会输出04
5
05 秒,保证两位数字,5秒会输出05
06 年,输出最后两位
2006 年,输出4位
.000 毫秒

可以快速记忆: 1月2日3时4分5秒6年

3. 字符串日期时间转time

通过time.Parse来把字符串转为 Time 时间对象

1
2
3
4
5
6
t, err := time.Parse("2006-01-02 15:04:05", "2023-04-14 07:03:04")
if err == nil {
fmt.Print(t.Year())
} else {
fmt.Print(err)
}

输出: 2023

如果出错了,会在返回的err 里有说明。比如:

1
2
3
4
5
6
t, err := time.Parse("2006-01-02 15:04:05", "04-14 07:03:04")
if err == nil {
fmt.Print(t.Year())
} else {
fmt.Print(err)
}

就会输出

1
parsing time "04-14 07:03:04" as "2006-01-02 15:04:05": cannot parse "04-14 07:03:04" as "2006"

4. 获取年/月/周/日/时/分/秒

1
2
3
4
5
6
7
8
9
10
11
12
13
t, err := time.Parse("2006-01-02 15:04:05", "04-14 07:03:04")

fmt.Println("年", t.Year())
fmt.Println("月", t.Month())
fmt.Println("月", t.Month() == 4)
fmt.Println("日", t.Day())
fmt.Println("时", t.Hour())
fmt.Println("分", t.Minute())
fmt.Println("秒", t.Second())
fmt.Println("星期几", t.Weekday())
year, week := t.ISOWeek()
fmt.Println("某年第几周", year, week)
fmt.Println("当年第几天", t.YearDay())

输出

1
2
3
4
5
6
7
8
9
10
年 2023
月 April
月 true
日 14
时 7
分 3
秒 4
星期几 Friday
某年第几周 2023 15
当年第几天 104

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package main

import (
"errors"
"fmt"
"time"

"gorm.io/driver/sqlite"
"gorm.io/gorm"
)

type Code struct {
ID string `gorm:"primaryKey"`
CreatedAt time.Time
UpdatedAt time.Time
Batch string
Code int64
}

func main() {
db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}

// 迁移 schema
db.AutoMigrate(&Code{})

fmt.Println(IncAndGet(db, "test", "oo"))
}

func IncAndGet(db *gorm.DB, idType string, batch string) (id int64, err error) {
code := Code{ID: idType, Batch: batch}
var result *gorm.DB
for i := 0; i <= 10; i++ {
result = db.First(&code)
if result.Error == nil {
if batch == code.Batch {
newCode := code.Code + 1
result = db.Debug().Model(&code).Where("code = ?", code.Code).Updates(Code{Code: newCode})
if result.RowsAffected > 0 {
return newCode, nil
} else {
continue
}
} else {
var newCode int64
newCode = 1
result = db.Debug().Model(&code).Where("code = ? and batch = ?", code.Code, code.Batch).Updates(Code{Code: newCode, Batch: batch})
if result.RowsAffected > 0 {
return newCode, nil
} else {
continue
}
}
}
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
code.Code = 1
result = db.Create(&code)
if result.Error != nil {
continue
}
return code.Code, nil
}
}

return 0, result.Error
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import (
"crypto/sha1"
"encoding/hex"
"fmt"
)

func SHA1(s string) string {
o := sha1.New()
o.Write([]byte(s))
return hex.EncodeToString(o.Sum(nil))
}

func main(){
fmt.Println(SHA1("123456"))
}

简介

Jackson是一个解析json的java库。springboot默认引用了这个json库。Jackson基于Apache-2.0 license开源,在github上有超过3k的star。 Jackson 在 mvnrepository.com 网站上的json分类里排名第一。如此优秀的开源库,值得我们去学习它的使用方法。

依赖说明

spring-boot-starter-web里已经通过 spring-boot-starter-josn 依赖了 jackson-databind包。 对于springboot的web项目,我们可以直接使用Jackson。 如果没有使用springboot可以在pom.xml中通过以下方式引入jar包。

1
2
3
4
5
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.1</version>
</dependency>

把json文本解析为java对象

先定义一个java类 Student.java

1
2
3
4
5
6
7
import lombok.Data;

@Data
public class Student {
String name;
Integer age;
}

然后通过以下代码完成json到java对象的转换

1
2
3
4
5
6
7
8
9
10
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Simple {
ObjectMapper objectMapper = new ObjectMapper();
public void testJson2Java() throws JsonProcessingException {
String str = "{\"name\":\"tom\",\"age\":16}";
Student student = objectMapper.readValue(str,Student.class);
System.out.println(student);
}
}

把java对象序列化为json文本

关键方法:ObjectMapper.writeValueAsString, 代码示例:

1
2
3
4
Student student = new Student();
student.setAge(11);
student.setName("jack");
String json = objectMapper.writeValueAsString(student);

把json文本解析为Jackson JsonNode

1
2
3
String str = "{\"name\":\"tom\",\"age\":16}";
JsonNode jsonNode = objectMapper.readTree(str);
String name = jsonNode.get("name").asText();

把json文本解析为Java List

借助 TypeReference

1
2
String str = "[{\"name\":\"tom\",\"age\":16},{\"name\":\"lucy\",\"age\":15}]";
List<Student> list = objectMapper.readValue(str, new TypeReference<List<Student>>(){});

把json文件解析为Java 数组

1
2
3
4
String str = "[{\"name\":\"tom\",\"age\":16},{\"name\":\"lucy\",\"age\":15}]";
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);
Student[] students = objectMapper.readValue(str, Student[].class);

把json文本解析为Java Map

借助 TypeReference

1
2
String str = "{\"name\":\"tom\",\"age\":16}";
Map<String,Object> map = objectMapper.readValue(str, new TypeReference<Map<String,Object>>(){});