JSON 和处理未导出的字段
Posted
技术标签:
【中文标题】JSON 和处理未导出的字段【英文标题】:JSON and dealing with unexported fields 【发布时间】:2012-06-23 00:24:12 【问题描述】:encoding/json 不包含未导出的字段是否有技术原因?如果不是,并且这是一个任意决定,即使未导出,是否还有一个额外的后门选项(比如“+”)要包括在内?
要求导出客户端代码以获取此功能感觉很不幸,尤其是在小写提供封装或编组结构的决定远晚于它们的设计时。
人们如何处理这个问题?只导出所有内容?
此外,导出字段名称不会使遵循建议的习语变得困难。我认为如果结构 X 具有字段 Y,则不能有访问器方法 Y()。如果您想提供对 Y 的接口访问,则必须为 getter 取一个新名称,并且根据http://golang.org/doc/effective_go.html#Getters,无论如何您都会得到一些不惯用的东西
【问题讨论】:
【参考方案1】:有技术原因。 json 库无权使用反射查看字段,除非它们被导出。包只能查看自己包中未导出的类型字段
为了解决您的问题,您可以做的是使用导出的字段创建一个未导出的类型。如果传递给它没有问题,Json 将解组为未导出的类型,但它不会出现在 API 文档中。然后,您可以制作一个嵌入未导出类型的导出类型。然后,这个导出的类型需要实现json.Marshaler
和json.Unmarshaler
接口的方法。
注意:所有代码都未经测试,甚至可能无法编译。
type jsonData struct
Field1 string
Field2 string
type JsonData struct
jsonData
// Implement json.Unmarshaller
func (d *JsonData) UnmarshalJSON(b []byte) error
return json.Unmarshal(b, &d.jsonData)
// Getter
func (d *JsonData) Field1() string
return d.jsonData.Field1
【讨论】:
为了记录,我不得不使用“json.Unmarshal(b, &d.jsonData)”解组。我是不是做错了什么,还是意料之中? @Derek,谢谢,我更新了我的答案。正如我所说,代码未经测试。我显然也忘记了我的UnmarshalJSON()
方法中的return 语句。我也解决了这个问题。
我有点迟到了,但是......虽然上面的作品,Field1 和 Field2 被导出。您可以在该包之外读取和写入 JsonData(大写 J)的 Field1 和 Field2。因此,虽然这在理论上很酷,但实际上与导出类型和字段并没有什么不同。
@davidjosepha 您是正确的,因为最初显示的答案是尽管有人必须查看您的源代码才能知道存在 Field1
和 Field2
,因为 godoc 不会列出未导出的结构jsonData
。但是,可以通过在 JsonData 结构中不使用匿名字段并使用未导出的命名字段来解决此问题。这要求通过命名的、未导出的字段访问Field1
和Field2
。我编辑了答案以使用这种方法。【参考方案2】:
斯蒂芬的回答是完整的。顺便说一句,如果您真正想要的是 json 中的小写键,您可以手动指定键名,如下所示:
type Whatever struct
SomeField int `json:"some_field"`
以这种方式,编组一个 What 为字段 SomeField 生成键“some_field”(而不是在您的 json 中包含“SomeField”)。
如果您对保留未导出的字段一无所知,您还可以通过定义带有签名MarshalJSON() ([]byte, error)
的方法来实现 json.Marshaler 接口。一种方法是使用结构文字,该结构文字仅具有未导出字段的导出版本,如下所示:
type Whatever struct
someField int
func (w Whatever) MarshalJSON() ([]byte, error)
return json.Marshal(struct
SomeField int `json:"some_field"`
SomeField: w.someField,
)
这可能有点麻烦,所以如果您愿意,也可以使用map[string]interface
:
func (w Whatever) MarshalJSON() ([]byte, error)
return json.Marshal(map[string]interface
"some_field": w.SomeField,
)
但是应该注意的是,封送interface
有一些注意事项,并且可以将uint64
封送为浮点数,从而导致精度损失。 (所有代码未经测试)
【讨论】:
从技术上讲,javascript 中的所有数字都是浮点数。 JSON 不是 JavaScript。 JSON 规范仅说明哪些字符是可接受的,而不是哪些数字范围是有效的。像 876234958273645982736459827346598237465923847561203947812435968234659827346 这样的数字在 JSON 中仍然有效,即使它无法被 JavaScript 理解。对于现实世界的示例,Twitter API 将推文 ID 表示为 64 位无符号整数,这在 JavaScript 中无效,但在 JSON 中是有效的。 目前,一切都正确,但名称仍然是 JSON,它在历史上代表“JavaScript Object Notation”。让您想将其重命名为 NJSON(NewJSON 或更确切地说 NotJavaScript)的那些微小的令人惊讶的位之一:)【参考方案3】:使用界面将是另一种选择。
type Person interface
Name() string
SetName(name string) Person
Age() int
SetAge(age int) Person
type person struct
Name_ string
Age_ int
func (p *person) Name() string
return p.Name_
func (p *person) SetName(name string) Person
p.Name_ = name
return p
func (p *person) Age() int
return p.Age_
func (p *person) SetAge(age int) Person
p.Age_ = age
return p
func NewPerson() Person
return &person
由于person struct
是小写字母,您将无法访问其包之外的公共字段。要实例化一个 person 值,您需要提供一个构造函数,该构造函数返回用大写 Person
接口包装的值。
Playground
【讨论】:
以上是关于JSON 和处理未导出的字段的主要内容,如果未能解决你的问题,请参考以下文章
使用“jq”从 JSON 文件导出所需的输出(按正确顺序的字段)
axios设置responseType===blob导出文件和失败返回json处理
axios设置responseType===blob导出文件和失败返回json处理