当前位置:首页 > 文章列表 > Golang > Go教程 > Go jsontext.Value 怎样保留 JSON 片段原始表示

Go jsontext.Value 怎样保留 JSON 片段原始表示

来源:17golang原创 2026-11-14 08:24:41 0浏览 收藏

如果你的任务是把 JSON 片段转发、审计或延迟处理,直接解码成 map[string]any 往往会丢掉数字写法、字符串转义和原始字段顺序。Go 的 encoding/json/jsontext 把一个完整 JSON 值表示成 jsontext.Value,本质上是保存原始文本的 []byte。

真正容易踩坑的是生命周期:Decoder.ReadValue 返回的值由 Decoder 的内部缓冲区支撑,下一次读取后就不能继续把它当作长期副本。要保留片段,应立即调用 Clone;要保留字符串里的转义写法,则在重新格式化时显式使用 PreserveRawStrings(true)。

一句话结论:ReadValue 负责拿到当前 JSON 值,Clone 负责把它保存下来,Format/Indent/Compact 负责有边界地改变排版;如果要改对象字段,应重新构造合法 Value,而不是把 Value 当成可随意编辑的 map。

先分清 Value 保留的是什么

Value 可以代表字符串、数字、布尔值、null,也可以代表整个对象或数组;Token 更偏向词法级的单个结构单元,不能独立表示完整对象或数组。两者都处理 JSON 语法,不等同于把数据解码成 Go 结构体。

因此,“保留原始表示”至少有三层含义:

  • 保留当前值的原始字节,而不是立刻变成 Go 值;
  • 保留副本的生命周期,不受 Decoder 后续读取影响;
  • 在格式化时保留字符串转义或数字写法,而不是主动规范化。
jsontext.Value 从 Decoder 缓冲区到 Clone 独立副本的静态结构说明图
图1:结构说明图,展示 Decoder、ReadValue、Value、Clone 与下游保存边界;这是静态说明图,不是运行截图。

ReadValue 读取的是完整 JSON 值

Decoder 可以把输入看成连续的顶层 JSON 值。调用 ReadValue 后,读取位置会向前推进;返回值会去掉首尾空白,但值内部的原始字节仍被保留。读取完成后要区分正常结束和语法错误,不能把所有错误都当成没有数据。

package main

import (
	"errors"
	"fmt"
	"io"
	"strings"

	"encoding/json/jsontext"
)

func readValues(input string) ([]jsontext.Value, error) {
	dec := jsontext.NewDecoder(strings.NewReader(input))
	var saved []jsontext.Value

	for {
		value, err := dec.ReadValue()
		if errors.Is(err, io.EOF) {
			break // 只有没有更多顶层值时才结束读取。
		}
		if err != nil {
			return nil, fmt.Errorf("读取 JSON 值失败: %w", err) // 保留语法错误上下文。
		}

		saved = append(saved, value.Clone()) // 立即复制,避免后续读取覆盖内部缓冲区。
	}
	return saved, nil
}

func main() {
	values, err := readValues(`{"id":7,"raw":"\\u5f00"}
 {"id":8,"raw":"plain"}`)
	if err != nil {
		panic(err) // 示例只把错误交给顶层;服务代码应返回或记录错误。
	}
	for _, value := range values {
		fmt.Println(string(value)) // 输出的是每个值的原始 JSON 片段。
	}
}

这里用两个顶层对象演示流式读取。第一个对象中的 \\u5f00 仍是输入里的转义文本;代码把每次读取结果先 Clone,所以保存到切片后仍可安全使用。若只在当前调用栈里立即消费,也可以不复制,但不要把未复制的 Value 放进缓存、队列或异步任务。

Clone 才是跨读取保存的边界

官方文档对 ReadValue 的限制很明确:返回结果只在下一次 Peek、Read 或 Skip 调用前有效,并且 Decoder 仍在使用时不能修改它。这个设计让读取可以复用缓冲区,但也意味着“拿到 Value 就存起来”并不安全。

使用方式很简单:

value, err := dec.ReadValue()
if err != nil {
	return err // 先处理 EOF 或语法错误,再使用 value。
}
stable := value.Clone() // 复制底层字节,stable 不再依赖 dec 的下一次读取。

queue = append(queue, stable) // 现在才适合交给缓存、队列或异步消费者。

不要用 append([]byte(nil), value...) 来替代表达意图不清的临时复制;虽然这种写法也能复制字节,但 Clone 直接表达了“把原始 JSON Value 变成独立副本”的语义。

格式化时怎样保留字符串和数字写法

Value.Format 默认会生成最小 JSON 表示,可能去掉空白并采用更短的字符串编码。它适合希望得到规范排版的场景,但不等价于逐字保留输入。只想调整缩进、又不想改写字符串和数字时,可以使用 Indent,或者给 Format 传入 PreserveRawStrings(true)。

func prettyRaw(input jsontext.Value) (jsontext.Value, error) {
	copy := input.Clone() // 不直接改动调用方持有的原始副本。
	if err := copy.Format(
		jsontext.PreserveRawStrings(true), // 保留字符串中的已有转义序列。
		jsontext.WithIndent("  "),          // 使用两个空格进行多行排版。
	); err != nil {
		return nil, fmt.Errorf("格式化 JSON 失败: %w", err) // 输出前仍处理语法错误。
	}
	return copy, nil
}

需要特别注意:PreserveRawStrings 只影响编码阶段,不能阻止你主动调用规范化选项;它也不能让对象成员顺序或数字精度永远保持不变。若业务必须保留数字的字面写法,应避免对它做数字规范化;若只需要删除空白,可考虑 Compact,它不会重新格式化字符串或数字。

jsontext.Value 的 PreserveRawStrings、Indent、Compact 与 Canonicalize 静态关系说明图
图2:关系说明图,对比 Clone 后的 Value 在保留原始表示、排版压缩与规范化之间的边界;这是静态说明图,不是运行结果截图。

保留原文和修改字段不是一回事

jsontext.Value 是原始 JSON 值,不是带字段索引的可变对象。你可以在 Value 上执行排版、压缩、合法性判断或规范化,但“把 name 改成另一个值”通常意味着重新构造 JSON,或者使用 Token/Encoder 重新写出结构。

如果修改来自用户输入,不要直接用字符串替换拼接 JSON。更稳妥的边界是:先把需要修改的字段解码到明确的 Go 类型,使用 jsontext.String 等构造合法 Token,或通过 Encoder 重新输出;不需要修改的片段仍可以作为 raw Value 写回,从而把结构化修改和原始转发分开。

func replaceWithNewRaw() jsontext.Value {
	// 新 Value 必须是完整且合法的 JSON 值;动态内容应使用构造器或编码器转义。
	return jsontext.Value(`{"id":7,"name":"Lin","price":1e2}`)
}

func keepOriginalNumber(v jsontext.Value) jsontext.Value {
	// 只转发原始副本,不调用 Canonicalize,避免改写 1e2 这类数字表示。
	return v.Clone()
}

落地时按这张表选择 API

目标建议需要留意
读取完整 JSON 值Decoder.ReadValue遇到 io.EOF 才表示没有更多值。
跨读取、异步或缓存保存Value.Clone复制应紧跟读取发生。
调整缩进Indent 或 Format + WithIndent这是重新写出,不是逐字复制。
删除空白Compact仍要区分“无空白”和“原始文本完全不变”。
签名、审计或严格转发保存 Clone 后的原始字节不要调用会规范化数字、字符串或对象顺序的 API。

兼容性与常见疑问

encoding/json/jsontext 随 Go 1.27 标准库提供。项目如果仍使用较旧的 Go 工具链,应先检查实际编译环境;不能因为本机安装了新版本就假设所有构建节点都能导入该包。

ReadValue 会保留输入首尾空格吗?不会,官方语义是去除首尾空白,但值内部的原始字节会保留。

Clone 会把 JSON 解析成结构体吗?不会,它只复制 Value 的字节,不改变数据层级和文本表示。

为什么格式化后转义形式变了?Format 默认追求最小 JSON 表示;需要保留字符串已有转义时,显式传入 PreserveRawStrings(true),并避免其它规范化选项覆盖你的目标。

Value 能直接修改对象字段吗?它不是 map。字段级修改应重新编码结构,或者用 Token 与 Encoder 组成新的合法值;原始片段转发则使用 Clone。

小结

把 ReadValue 看成“借用当前读取窗口”,把 Clone 看成“取得长期副本”,就能避开大多数生命周期问题。随后根据目标选择 Indent、Compact、PreserveRawStrings 或结构化重写;如果目标是签名和审计,最重要的是不要在保存前无意调用规范化 API。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go encoding/json/v2 怎样把大对象流式编码到 WriterGo encoding/json/v2 怎样把大对象流式编码到 Writer
上一篇
Go encoding/json/v2 怎样把大对象流式编码到 Writer
Go http.Cookie 怎样设置 Partitioned 属性
下一篇
Go http.Cookie 怎样设置 Partitioned 属性
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    1020次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    1021次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    1157次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    1123次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    964次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码