JSON和处理未导出的字段

时间:2012-06-20 19:29:27

标签: json go

编码/ json中是否存在未包含未导出字段的技术原因?如果没有,这是一个任意的决定,可以有一个额外的后门选项(说'+')包括即使未导出?

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

人们如何处理这个问题?只出口一切?

此外,不导出字段名称使得难以遵循建议的习语。我认为如果结构X有字段Y,则不能有一个存取方法Y()。如果你想提供对Y的界面访问,你必须为getter提出一个新的名字,不管你根据http://golang.org/doc/effective_go.html#Getters

获得什么非惯用的东西。

3 个答案:

答案 0 :(得分:85)

有一个技术原因。除非导出,否则json库无法使用reflect查看字段。包只能在其自己的包

中查看未导出的类型字段

为了解决您的问题,您可以做的是使用导出的字段创建一个未导出的类型。如果传递给它没有问题,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
}

答案 1 :(得分:56)

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

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

通过这种方式,编组Whatever会为字段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编组到浮点数之类的操作,从而导致精度损失。 (所有代码未经测试)

答案 2 :(得分:0)

使用界面是另一种选择。

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接口包装的它。

Playground