go 编码/json:解组器破坏了DisallowUnknownFields

lx0bsm1f  于 2个月前  发布在  Go
关注(0)|答案(9)|浏览(29)

你正在使用的Go版本是什么( go version )?

$ go version
go version go1.15rc1 linux/amd64

这个问题在最新版本中是否会重现?

是的

你正在使用什么操作系统和处理器架构( go env )?

go env 输出

$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/alvaro/.gocache"
GOENV="/home/alvaro/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/alvaro/git/golang/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/alvaro/git/golang"
GOPRIVATE=""
GOPROXY="direct"
GOROOT="/usr/local/go"
GOSUMDB="off"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build855135854=/tmp/go-build -gno-record-gcc-switches"

你做了什么?

构建了一个自定义的 json.Unmarshaler 来默认一个配置文件,然后使用了一个 json.DecoderDisallowUnknownFields 来解析一个具有额外未知字段的json表示。我原以为这会失败,但它没有。
https://play.golang.org/p/8cEJU-Y0-9L

你期望看到什么?

一个错误,指出json文档中的未知、额外字段

你实际上看到了什么?

没有错误

cqoc49vn

cqoc49vn1#

当你在 UnmarshalJSON 方法中调用 json.Unmarshal 时,它会创建一个新的 decodeState,这个新示例对顶层的 json.Decoder 示例一无所知。如果你想在未知字段上出错,你需要在你 json.Unmarshaler 实现中强制执行这一点。

bd1hkmkf

bd1hkmkf2#

当你在UnmarshalJSON方法中调用json.Unmarshal时,它会创建一个新的decodeState,这个decodeState并不知道顶层的json.Decoder示例。如果你想在未知字段上出错,你需要在你实现的json.Unmarshaler中强制执行这一点。
我知道这一点,但我不想一般地强制执行。我希望使用顶层json.Decoder示例中的设置,似乎很难找到这一点(或者我漏掉了什么?)

yv5phkfx

yv5phkfx3#

这里的问题是,json.Unmarshaler 没有内置对选项的支持,所以在当前API中没有很好的方法来解决这个问题。如果你愿意的话,你可以自己记住选项并在嵌套的Unmarshal/Decode调用中重新应用它们。

我认为在当前API中没有一种方法可以在不重复多个代码块或完全破坏它的情况下修复这个问题,而这在Go 1.x中并不是一个选项。我最近整理了一下关于编码/json重构的想法,如果你感兴趣的话,选项的问题几乎排在列表的最前面。

pprl5pva

pprl5pva4#

这里的问题是,json.Unmarshaler没有内置对选项的支持,所以用当前的API无法很好地解决这个问题。如果你愿意的话,可以自己记住这些选项,并在嵌套的Unmarshal/Decode调用中重新应用它们。

我无法控制所有调用Unmarshal的代码,所以我真的没有办法记住这个,或者还有其他方法吗?

这个问题基本上是关于以某种方式更改API,以便能够传递这些选项,例如通过一个额外的、可选的接口(UnmarshalerWithOptions?)。我对它应该是什么样子的意见不多,但我想确认我的怀疑,那就是今天这是不可能的,并且希望对此有一个跟踪问题。

zsbz8rwp

zsbz8rwp5#

我猜想这已经存在一个问题,但我现在真的找不到它。可选地扩展接口是一个选择,但老实说不是一个很好的选择——然后你把问题转移到修复所有实现以做正确的事情上,这也增加了相当多的代码更改,几乎就像切换到v2 API一样。

mlnl4t2r

mlnl4t2r6#

可选地扩展接口是一个选项,但老实说,这并不是一个很好的选择——然后你把问题转移到修复所有实现以做正确的事情上,这也增加了相当多的代码更改,几乎就像切换到v2 API一样。

如前所述,我对这个应该看起来如何没有太多意见,但我想象一下,v2 API设计将需要很长时间,而我更希望在可预见的未来有一个不太好的解决方案,而不是在非常遥远的未来有一个很好的解决方案(另外,向当前API添加一些内容不会阻止我们在v2 API中实现一些合适的东西)

lvjbypge

lvjbypge7#

事实上,重新设计可能需要一段时间。

不好的解决方案的问题在于,有时候它们比什么都不做还要糟糕,我认为目前的共识是基本上冻结编码/json API不变。你可以查看所有冻结的json提案作为例子。@rsc@dsnet可能能够以比我更有权威的方式确认:)

如果你想提议向包API添加内容,那么你应该将其变成一个提案,并像任何其他提议的API更改一样进行审查。你可以重用这个问题,或者另开一个新问题。参见https://github.com/golang/proposal

qhhrdooz

qhhrdooz8#

正如@mvdan提到的,我不知道是否有关于顶级选项传递问题的特定问题,但这是我在许多不同的encoding/json问题中评论过的一个问题。添加新功能需要O(n!)的维护成本,因为你需要考虑如何与已经提供的所有功能进行交互(以及这些功能的全部可能组合)。我认为在仔细思考影响之前,我们不应该添加另一个UnmarshalerWithOptions接口。作为一个高级概述,这样的接口可能需要依赖于encoding/json包,这已经与Unmarshaler接口本身产生了显著的分歧(它除了基本类型(如[]byteinterror)之外没有任何依赖关系)。如果我们通过接口强制这种依赖关系,那么是否应该让接口提供比选项更多的东西也成了一个问题。例如,目前调用嵌套的Unmarshaler是O(n^2),因为encoding/json包必须重复解析整个子树,才能在调用UnmarshalJSON之前找到JSON令牌的结束位置。如果我们能传递类似于json.Decoder的东西,可能会更有效。

eqzww0vc

eqzww0vc9#

大家好,我们启动了一个讨论这个提案精神的项目。在v2中,我们提议支持以下接口:

type UnmarshalerV2 interface {
	UnmarshalJSONV2(*jsontext.Decoder, Options) error
}

特别需要注意的是,这传递了一个Options值,可以保留DisallowUnknownFields语义。
现有的UnmarshalJSON方法显然无法受益,但实现UnmarshalJSONV2方法的类型可以在将来受益。

相关问题