Golang中json.Compact函数会验证JSON但文档未提及

Golang中json.Compact函数会验证JSON但文档未提及 json.Compact 的文档非常简洁:

// Compact 将去除无关空格的 JSON 编码 src 追加到 dst。

阅读相关代码部分后,我发现 Compact 不仅压缩 JSON,还会验证 JSON。在调用 json.Compact 之后再调用 json.Valid 是多余的。我认为文档应该明确说明这一点。

以下是相关代码部分: https://go.googlesource.com/go/+/go1.16.2/src/encoding/json/indent.go#17 https://go.googlesource.com/go/+/go1.16.2/src/encoding/json/scanner.go#30

以下是我对文档修改的建议:

// Compact 将去除无关空格的 JSON 编码 src 追加到 dst。Compact 同时会验证 JSON 编码。

大家觉得如何?


更多关于Golang中json.Compact函数会验证JSON但文档未提及的实战教程也可以访问 https://www.itying.com/category-94-b0.html

8 回复

ValidcheckValid 是在哪里被调用的?我找不到它。

更多关于Golang中json.Compact函数会验证JSON但文档未提及的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Compact 不会调用 Valid 或 checkvalid,但它们是等效的。

Valid 通过在 checkValid 中调用 scan.step 来检查编码是否有效。Compact 也是如此。

func main() {
    fmt.Println("hello world")
}

我能想到的唯一未被接受的原因是,如果 json.Compact 不能保证进行验证。如果 Go 的参考实现进行了验证,那固然很好,但如果他们将其添加到文档中,那就意味着其他替代实现也必须进行验证。

func main() {
    fmt.Println("hello world")
}

由于 Compact 函数会返回一个错误,我认为它隐含了输入必须是有效的 JSON。如果能将这个保证明确地记录在文档中,那就更好了。

感谢 petrus 的反馈,我认为文档应该这样写:

// Compact 将去除空白字符后的 JSON 编码 src 追加到 dst 中。
// 如果 src 不是有效的 JSON,则不会追加 src 并返回一个非 nil 的错误。

zamicol:

json.Compact 的文档很简洁


文档是清晰的。

json 包

json 包实现了 RFC 7159 中定义的 JSON 编码和解码。

func Compact

func Compact(dst *bytes.Buffer, src []byte) error

Compact 将去除无关空格的 JSON 编码的 src 追加到 dst。

Compact 追加的是 JSON 编码的 src,其中 JSON 编码的定义遵循 RFC 7159。如果 src 不是 JSON 编码的,则 src 不会被追加,并且会返回一个非 nil 的错误。

dst 可以是 JSON 编码的,也可以不是。

我同意你的观点,特别是文档即契约这一点。这也是我对在文档中说明该函数会执行验证的担忧。然而,我认为让函数的功能含糊不清也是不可取的。

问题依然存在:如果不是因为无效的 JSON,error 又是什么呢?

因此,或许更好的文档说明是:“对于此 Compact 实现,如果 src 不是有效的 JSON,则不会追加 src 并返回非 nil 错误。” 或者,“未来版本的 Compact 可能不提供此保证。” 我认为后者是我的偏好。

所以,这是我更新的提案:

// Compact appends to dst the JSON-encoded src with insignificant space
// characters elided. If src is not valid JSON, src is not appended and a non-nil
// error is returned.  Future versions Compact may not have this guarantee.

这听起来像是一个巧合。当然,你可以将其作为 PR 或 issue 提交到 GitHub,但他们可能会说 Compact 并不保证验证。也许未来的实现不会遍历 JSON 状态机并报告错误。

我不知道 Go 标准库是否以这种方式工作,但在 C++ 中,标准库描述了其函数的先决条件和结果。有一个参考实现,但编译器和第三方可以实现自己的标准库,其中必须满足标准描述的最低要求。Go 文档将 Compact 定义为移除不必要的空白。Go 参考实现使用的机制会像 Validate 函数一样检测错误,但这可能是实现的一个令人愉快的副作用。如果它被记录为验证 JSON,那么所有自定义实现也必须验证 JSON。

可能会出现这样的情况:有人想出了一种无需 JSON 状态机的超快速压缩 JSON 的方法,但如果文档说明在压缩时会验证 JSON,那么它必须是有效的,因为人们会依赖这一点。这个超快速 Compact 实现的实现者将不得不添加对 Valid 的调用,这可能会降低实现速度,即使调用 Compact 的人可能不需要这种保证。

文档是描述函数前置条件和后置条件的“契约”。代码只是实现。

是的,你的观察是正确的。json.Compact 函数确实会在压缩过程中验证 JSON 的有效性。如果传入无效的 JSON,它会返回一个错误。因此,在调用 json.Compact 后再次调用 json.Valid 确实是多余的。

以下是一个示例代码,演示了 json.Compact 的验证行为:

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
)

func main() {
	// 有效的 JSON
	validJSON := []byte(`{"name": "Alice", "age": 30}`)
	// 无效的 JSON(缺少闭合括号)
	invalidJSON := []byte(`{"name": "Bob", "age": 25`)

	var buf bytes.Buffer

	// 压缩有效 JSON
	err := json.Compact(&buf, validJSON)
	if err != nil {
		fmt.Printf("有效 JSON 压缩错误: %v\n", err)
	} else {
		fmt.Printf("压缩结果: %s\n", buf.String())
	}

	buf.Reset()

	// 压缩无效 JSON
	err = json.Compact(&buf, invalidJSON)
	if err != nil {
		fmt.Printf("无效 JSON 压缩错误: %v\n", err)
	} else {
		fmt.Printf("压缩结果: %s\n", buf.String())
	}
}

运行此代码,你会看到对于无效 JSON,json.Compact 返回了错误:

压缩结果: {"name":"Alice","age":30}
无效 JSON 压缩错误: unexpected end of JSON input

因此,你的文档修改建议是合理的。明确说明 json.Compact 同时验证 JSON 编码,可以帮助其他开发者避免不必要的验证调用。

回到顶部