Go:JSON编组嵌套结构;错误地省略外部字段

huangapple go评论105阅读模式
英文:

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.InstanceMarshalJSON 方法,当然不会输出你的 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.

huangapple
  • 本文由 发表于 2017年6月7日 09:40:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/44402160.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定