81

编码/json不包含未导出的字段是否有技术原因?如果不是,这是一个任意决定,即使未导出,是否还有一个额外的后门选项(比如“+”)要包括在内?

要求导出客户端代码以获取此功能感觉很不幸,特别是如果小写提供封装或编组结构的决定远晚于它们的设计。

人们如何处理这个问题?只导出所有内容?

此外,不导出字段名称会使遵循建议的习语变得困难。我认为如果结构 X 具有字段 Y,则不能有访问器方法 Y()。如果您想提供对 Y 的接口访问,您必须为 getter 取一个新名称,并且根据http://golang.org/doc/effective_go.html#Getters,无论如何您都会得到一些不习惯的东西

4

3 回答 3

108

有技术原因。json 库无权使用反射查看字段,除非它们被导出。一个包只能查看自己包中未导出的类型字段

为了解决您的问题,您可以做的是使用导出的字段创建一个未导出的类型。如果传递给它没有问题,Json 将解组为未导出的类型,但它不会出现在 API 文档中。然后,您可以制作一个嵌入未导出类型的导出类型。然后,此导出的类型将需要实现json.Marshalerjson.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
}
于 2012-06-20T23:10:57.490 回答
67

斯蒂芬的回答是完整的。顺便说一句,如果您真正想要的是 json 中的小写键,您可以手动指定键名,如下所示:

type Whatever struct {
    SomeField int `json:"some_field"`
}

以这种方式,编组一个 What 会为字段 SomeField 生成键“some_field”(而不是在您的 json 中包含“SomeField”)。

如果您对保留未导出的字段一无所知,您还可以通过定义带有签名的方法来实现 json.Marshaler 接口MarshalJSON() ([]byte, error)。一种方法是使用结构文字,它只是导出了未导出字段的版本,如下所示:

type Whatever struct {
    someField int
}

func (w Whatever) MarshalJSON() ([]byte, error) {
    return json.Marshal(struct{
        SomeField int `json:"some_field"`
    }{
        SomeField: w.someField,
    })
}

这可能有点麻烦,因此您也可以使用 amap[string]interface{}如果您愿意:

func (w Whatever) MarshalJSON() ([]byte, error) {
    return json.Marshal(map[string]interface{}{
        "some_field": w.SomeField,
    })
}

但是应该注意,编组interface{}有一些注意事项,并且可以执行诸如编组uint64到浮点数之类的操作,从而导致精度损失。(所有代码未经测试)

于 2012-06-20T23:31:05.553 回答
1

使用接口将是另一种选择。

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接口中。

操场

于 2020-09-03T02:30:25.897 回答