英文:
Go: JSON marshalling nested struct; incorrectly omitting outer fields
问题
我正在尝试编组嵌套的结构体。在这里可以看到一个不起作用的示例(我无法在Go Playground中导入"compute"和"pretty",但我已经重新创建了我的测试逻辑并粘贴了输出)。
package main
import (
"encoding/json"
"fmt"
"github.com/kylelemons/godebug/pretty"
compute "google.golang.org/api/compute/v1"
)
type CreateInstance struct {
compute.Instance
// Additional metadata to set for the instance.
Metadata map[string]string `json:"metadata,omitempty"`
// OAuth2 scopes to give the instance. If none are specified
// https://www.googleapis.com/auth/devstorage.read_only will be added.
Scopes []string `json:",omitempty"`
// StartupScript is the Sources path to a startup script to use in this step.
// This will be automatically mapped to the appropriate metadata key.
StartupScript string `json:",omitempty"`
// Project to create the instance in, overrides workflow Project.
Project string `json:",omitempty"`
// Zone to create the instance in, overrides workflow Zone.
Zone string `json:",omitempty"`
// Should this resource be cleaned up after the workflow?
NoCleanup bool
// Should we use the user-provided reference name as the actual resource name?
ExactName bool
// The name of the disk as known internally to Daisy.
daisyName string
}
func main() {
ci := &CreateInstance{}
j, _ := json.MarshalIndent(ci, "", " ")
fmt.Println(string(j))
pretty.Print(ci) // Pretty prints the struct.
}
输出如下:
{
"disks": [
{
"source": "disk"
}
],
"machineType": "${machine_type}",
"name": "${instance_name}"
}
{Instance: {CanIpForward: false,
CpuPlatform: "",
CreationTimestamp: "",
Description: "",
Disks: [{AutoDelete: false,
Boot: false,
DeviceName: "",
DiskEncryptionKey: nil,
Index: 0,
InitializeParams: nil,
Interface: "",
Kind: "",
Licenses: [],
Mode: "",
Source: "disk",
Type: "",
ForceSendFields: [],
NullFields: []}],
Id: 0,
Kind: "",
MachineType: "${machine_type}",
Metadata: nil,
Name: "${instance_name}",
NetworkInterfaces: [],
Scheduling: nil,
SelfLink: "",
ServiceAccounts: [],
Status: "",
StatusMessage: "",
Tags: nil,
Zone: "",
ServerResponse: {HTTPStatusCode: 0,
Header: {}},
ForceSendFields: [],
NullFields: []},
Metadata: {},
Scopes: [],
StartupScript: "",
Project: "",
Zone: "",
NoCleanup: false,
ExactName: false}
基本上,我有一个名为CreateInstance的结构体,它嵌入了来自Google Compute Engine API客户端库的Instance结构体。CreateInstance还有两个没有JSON标签的bool字段,ExactName和NoCleanup。
当我尝试编组CreateInstance时,无论ExactName和NoCleanup是true还是false,它们都会被省略。
英文:
I'm trying to marshal nested structs. See a NON-FUNCTIONING example here (I can't import "compute" and "pretty" in the Go playground, but I've recreated my test logic and pasted the output).
package main
import (
"encoding/json"
"fmt"
"github.com/kylelemons/godebug/pretty"
compute "google.golang.org/api/compute/v1"
)
type CreateInstance struct {
compute.Instance
// Additional metadata to set for the instance.
Metadata map[string]string `json:"metadata,omitempty"`
// OAuth2 scopes to give the instance. If none are specified
// https://www.googleapis.com/auth/devstorage.read_only will be added.
Scopes []string `json:",omitempty"`
// StartupScript is the Sources path to a startup script to use in this step.
// This will be automatically mapped to the appropriate metadata key.
StartupScript string `json:",omitempty"`
// Project to create the instance in, overrides workflow Project.
Project string `json:",omitempty"`
// Zone to create the instance in, overrides workflow Zone.
Zone string `json:",omitempty"`
// Should this resource be cleaned up after the workflow?
NoCleanup bool
// Should we use the user-provided reference name as the actual resource name?
ExactName bool
// The name of the disk as known internally to Daisy.
daisyName string
}
func main() {
ci := <a *CreateInstance part of a larger data structure>
j, _ := json.MarshalIndent(ci, "", " ")
fmt.Println(string(j))
pretty.Print(ci) # Pretty prints the struct.
}
##### OUTPUT #####
{
"disks": [
{
"source": "disk"
}
],
"machineType": "${machine_type}",
"name": "${instance_name}"
}
{Instance: {CanIpForward: false,
CpuPlatform: "",
CreationTimestamp: "",
Description: "",
Disks: [{AutoDelete: false,
Boot: false,
DeviceName: "",
DiskEncryptionKey: nil,
Index: 0,
InitializeParams: nil,
Interface: "",
Kind: "",
Licenses: [],
Mode: "",
Source: "disk",
Type: "",
ForceSendFields: [],
NullFields: []}],
Id: 0,
Kind: "",
MachineType: "${machine_type}",
Metadata: nil,
Name: "${instance_name}",
NetworkInterfaces: [],
Scheduling: nil,
SelfLink: "",
ServiceAccounts: [],
Status: "",
StatusMessage: "",
Tags: nil,
Zone: "",
ServerResponse: {HTTPStatusCode: 0,
Header: {}},
ForceSendFields: [],
NullFields: []},
Metadata: {},
Scopes: [],
StartupScript: "",
Project: "",
Zone: "",
NoCleanup: false,
ExactName: false}
Basically, I have a struct, CreateInstance, that embeds an Instance struct from the Google Compute Engine API client lib. CreateInstance also has two bool fields with no JSON tags, ExactName and NoCleanup.
When I try to marshal a CreateInstance, ExactName and NoCleanup are omitted, be they true or false.
答案1
得分: 2
一个 compute.Instance
是一个 json.Marshaler
,所以通过嵌入该类型,实际上是给你的 CreateInstance
结构体提供了来自 compute.Instance
的 MarshalJSON
方法,当然不会输出你的 CreateInstance
结构体中的任何字段。
你可以定义自己的 MarshalJSON
方法,尝试手动编组 Instance
,或者重新分配给一个将使用默认 JSON 输出的新类型,但是 API 可能依赖于内部的 MarshalJSON
行为,所以不能保证与当前版本或将来版本兼容。
最好避免嵌入类型进行 JSON 编组,因为这样很容易产生类似这样的混淆性错误。我建议尝试以不同的方式组合它们。
英文:
A compute.Instance
is a json.Marshaler
, so by embedding that type you are in essence giving your CreateInstance
the MarshalJSON
method from compute.Instance
, which of course isn't going to output any of the fields in your CreateInstance
struct.
You could define your own MarshalJSON
method, and try to marshal the Instance
manually or re-assign it to a new type that will use the default json output, but the API may be relying on the internal MarshalJSON
behavior, so that's not guaranteed to be compatible, currently or with future versions.
Embedding types for json marshaling is is best avoided because it's all too easy to create confusing bugs like this. I would try to compose them in a different manner.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论