Go jsontext Token 怎样在读取 token 数值(输入边界)
来源:17golang原创
时间:2026-10-25 05:16:12 251浏览 收藏
在 Go 1.27 的 encoding/json/jsontext 中,Token 表示 JSON 词法层的一个 token。读取数字时,关键不是直接调用 Int() 或 Uint(),而是先确认 Kind() == jsontext.KindNumber,再根据业务需要选择整数或浮点读取方法。
官方文档:https://pkg.go.dev/encoding/json/jsontext
安全读取 token 数值可以记成四步:先判数字类型,再选 accessor;用errors.Is识别语法或范围错误;最后注意ReadToken返回值可能依赖 Decoder 缓冲区。Int、Uint出错时仍可能返回一个合理值,不能只看数值不看 error。
KindNumber只说明 token 是 JSON 数字,不保证它适合目标 Go 类型。Int和Uint可能发生向零截断,超范围时会返回饱和值并带错误。- 从
Decoder.ReadToken得到的原始 token,在下一次 Decoder 调用后不应继续依赖;需要保存时先Clone。
先用 Kind 确认 token 真的是数字
Token 不只表示数字,还可以表示 null、布尔值、字符串以及对象和数组的起止分隔符。数字 accessor 对错误的 token 类型会 panic,因此输入来自外部 JSON 时,第一层判断应放在读取方法之前。
下面的辅助函数只把 JSON 数字交给 Int,其它 token 直接返回带上下文的错误。注释中的每个判断都对应一个输入边界,业务层可以再把错误映射成自己的字段错误。
package main
import (
"fmt"
"encoding/json/jsontext"
)
func readInt(tok jsontext.Token) (int64, error) {
// 先挡住字符串、布尔值和结构分隔符,避免 Int 对非数字 token 触发 panic。
if tok.Kind() != jsontext.KindNumber {
return 0, fmt.Errorf("expected JSON number, got %s", tok.Kind())
}
// Int 会解析带符号的整数语义,返回值是否可用必须和 error 一起判断。
n, err := tok.Int()
if err != nil {
return 0, fmt.Errorf("read integer %q: %w", tok.String(), err)
}
return n, nil
}
这里的 KindNumber 是必要条件,但不是完整的业务校验。例如 token 文本可以是带小数或指数的 JSON 数字,是否允许这类输入要交给 Int 的错误结果和业务规则共同决定。

Int、Uint 和 Float 的选择不要混用
有符号计数、可能出现负值的偏移量适合 Int();业务字段明确不允许负数且范围对应 uint64 时才使用 Uint()。普通 JSON 数字需要小数或指数语义时使用 Float()。Float32() 只适合调用方已经知道这个 token 按 32 位精度生成的场景,不能为了节省一个变量宽度就默认使用。
func readCount(tok jsontext.Token) (uint64, error) {
// 无符号计数不接受非数字 token,负数和不符合无符号整数语义的文本交给 Uint 报错。
if tok.Kind() != jsontext.KindNumber {
return 0, fmt.Errorf("count must be a JSON number, got %s", tok.Kind())
}
n, err := tok.Uint()
if err != nil {
return 0, fmt.Errorf("invalid count %q: %w", tok.String(), err)
}
return n, nil
}
func readRatio(tok jsontext.Token) (float64, error) {
// 比例允许小数,因此不能用 Int;Float 按 64 位精度读取 JSON 数字。
if tok.Kind() != jsontext.KindNumber {
return 0, fmt.Errorf("ratio must be a JSON number, got %s", tok.Kind())
}
n, err := tok.Float()
if err != nil {
return 0, fmt.Errorf("invalid ratio %q: %w", tok.String(), err)
}
return n, nil
}
不要把字符串形式的 "12" 当成数字 token 来调用这些方法。Token.String() 对字符串返回解码后的字符串值,对数字返回原始 JSON 表示;它适合放进错误信息或日志,但不能代替 Kind() 做类型判断。
语法错误、范围错误和截断要分开处理
Int() 与 Uint() 会用与 strconv.ErrSyntax、strconv.ErrRange 匹配的错误说明问题。范围错误时仍可能返回一个合理值,超出目标整数表示范围时还会饱和到最近的可表示值,所以生产代码应先处理 error,再决定是否把结果交给业务层。
import (
"errors"
"fmt"
"strconv"
"encoding/json/jsontext"
)
func classifyInt(tok jsontext.Token) (int64, error) {
// 先确认调用前提,数值方法本身不会替调用方检查 Kind。
if tok.Kind() != jsontext.KindNumber {
return 0, fmt.Errorf("not a number: %s", tok.Kind())
}
n, err := tok.Int()
if err == nil {
return n, nil
}
// ErrSyntax 表示输入形态不符合当前整数读取语义,例如负号/小数规则不匹配。
if errors.Is(err, strconv.ErrSyntax) {
return 0, fmt.Errorf("integer syntax is invalid for %q: %w", tok.String(), err)
}
// ErrRange 表示不能无损放进 int64;即使 n 有值,也不能当成精确结果。
if errors.Is(err, strconv.ErrRange) {
return 0, fmt.Errorf("integer is out of int64 range: %w", err)
}
return 0, fmt.Errorf("read integer: %w", err)
}
文档明确说明整数读取会忽略小数部分并向零截断,因此 12.9 这类输入不能在业务上默认视为“合法整数”。如果字段必须是整数,应在 token 层保留原始表示并增加格式约束,不能把截断后的结果悄悄写回数据库或计数器。

ReadToken 返回值需要注意 Clone
如果 token 是通过 Decoder.ReadToken 得到的,它的原始 JSON 文本可能由 Decoder 的内部缓冲区支持。下一次调用 Decoder 的方法后,这个 token 不应再被当成独立值使用。需要把 token 放进队列、缓存、异步任务或错误记录时,先调用 Clone()。
func keepNumber(dec *jsontext.Decoder) (jsontext.Token, error) {
tok, err := dec.ReadToken()
if err != nil {
return jsontext.Token{}, err
}
if tok.Kind() != jsontext.KindNumber {
return jsontext.Token{}, fmt.Errorf("expected number, got %s", tok.Kind())
}
// 跨过下一次 Decoder 调用仍要使用,因此复制脱离 Decoder 缓冲区的 token。
return tok.Clone(), nil
}
相反,jsontext.Int(42)、jsontext.Uint(42)、jsontext.Float(0.5) 等构造出来的 token 本身已经独立保存,不需要为了生命周期再次 Clone。这一区别只影响 token 的存活时间,不会改变数字解析的类型或范围规则。
常见问题
调用 Token.Int 之前一定要判断 Kind 吗?
只要 token 来源不受你完全控制,就应该判断。Int、Uint 会对非数字 token panic,提前判断能把输入问题转成普通 error。
Uint 能读取负数吗?
不能把负数当作无符号整数接受。应让 Uint 返回错误,并按 strconv.ErrSyntax 或 strconv.ErrRange 分类处理。
为什么 Int 返回了数值还要看 error?
因为语法不匹配或超出 int64 范围时,文档允许返回一个合理值或饱和值。只有 error 为 nil 时,才适合把该数值当作无损读取结果。
Token.Clone 会把数字转换成别的类型吗?
不会。Clone 只把依赖 Decoder 缓冲区的原始 token 复制成独立 token,不负责类型转换、范围检查或业务校验。
-
178 收藏
-
121 收藏
-
112 收藏
-
309 收藏
-
226 收藏
-
195 收藏
-
107 收藏
-
103 收藏
-
338 收藏
-
417 收藏
-
312 收藏
-
314 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习