英文:
Composing arbitrary JSON objects in embedded structs in Go
问题
我正在尝试通过在Go中嵌入结构体来生成形如{{"s":"v1", "t":"v2"}, {"s":"v3", "t":"v4"}, etc}
的JSON对象。
当type Problems []Problem
中的所有Problem
项在预先知道时,一切都正常,可以在下面的ONE()
函数和Playground演示中看到。
然而,如果某些K:V对包含空值,我希望避免得到{{a},{},{c}}
而不是期望的{{a},{c}}
,如下面的TWO()
函数和演示中所示。
或者,如何在不预先知道是否会添加或省略prob项的情况下任意组合probs := Problems{prob0, prob1, prob2, etc}
?
package main
import (
"encoding/json"
"fmt"
)
type Problem struct {
S string `json:"s,omitempty"`
T string `json:"t,omitempty"`
}
type Problems []Problem
func main() {
ONE()
TWO()
}
func ONE() {
prob0 := Problem{S: "s0", T: "t0"}
prob1 := Problem{S: "s1", T: "t1"}
prob2 := Problem{S: "s2", T: "t2"}
probs := Problems{prob0, prob1, prob2}
// fmt.Println(probs) // CORRECT: [{s0 t0} {s1 t1} {s2 t2}]
b, _ := json.Marshal(probs)
fmt.Println(string(b))
// CORRECT: [{"s":"s0","t":"t0"},{"s":"s1","t":"t1"},{"s":"s2","t":"t2"}]
}
func TWO() {
prob0 := Problem{S: "s0", T: "t0"}
prob1 := Problem{S: "", T: ""} // 空值被省略,但不是{}
prob2 := Problem{S: "s2", T: "t2"}
probs := Problems{prob0, prob1, prob2}
// fmt.Println(probs)
// GOT: [{s0 t0} { } {s2 t2}]
// WANTED: [{s0 t0} {s2 t2}]
b, _ := json.Marshal(probs)
fmt.Println(string(b))
// GOT: [{"s":"s0","t":"t0"},{},{"s":"s2","t":"t2"}]
// WANTED: [{"s":"s0","t":"t0"},{"s":"s2","t":"t2"}]
}
英文:
I'm trying to generate JSON objects in the form of {{"s":"v1", "t":"v2"}, {"s":"v3", "t":"v4"}, etc}
via embedded structs in Go.
It all works out fine when all Problem
items in type Problems []Problem
are known ahead of time, as can be seen in func ONE()
below and in <kbd>Playground demo</kbd> here.
However, if some K:V pairs contain empty values I'd like to avoid getting {{a},{},{c}}
instead of the desired {{a},{c}}
, as in func TWO()
below and in demo.
Or alternatively, how can I compose probs := Problems{prob0, prob1, prob2, etc}
below arbitrarily without knowing ahead of time whether a prob item will be added or omitted?
package main
import (
"encoding/json"
"fmt"
)
type Problem struct {
S string `json:"s,omitempty"`
T string `json:"t,omitempty"`
}
type Problems []Problem
func main() {
ONE()
TWO()
}
func ONE() {
prob0 := Problem{S: "s0", T: "t0"}
prob1 := Problem{S: "s1", T: "t1"}
prob2 := Problem{S: "s2", T: "t2"}
probs := Problems{prob0, prob1, probe}
// fmt.Println(probs) // CORRECT: [{s0 t0} {s1 t1} {s2 t2}]
b, _ := json.Marshal(probs)
fmt.Println(string(b))
// CORRECT: [{"s":"s0","t":"t0"},{"s":"s1","t":"t1"},{"s":"s2","t":"t2"}]``
}
func TWO() {
prob0 := Problem{S: "s0", T: "t0"}
prob1 := Problem{S: "", T: ""} // empty values omited BUT NOT {}
prob2 := Problem{S: "s2", T: "t2"}
probs := Problems{prob0, prob1, probe}
// fmt.Println(probs)
// GOT: [{s0 t0} { } {s2 t2}]
// WANTED: [{s0 t0} {s2 t2}]
b, _ := json.Marshal(probs)
fmt.Println(string(b))
// GOT: [{"s":"s0","t":"t0"},{},{"s":"s2","t":"t2"}]
// WANTED: [{"s":"s0","t":"t0"},{"s":"s2","t":"t2"}]
}
答案1
得分: 1
一旦你将一个元素添加到你要进行编组的数组/切片中,你就无法再对其进行操作了。如果一个元素在数组/切片中,它将被编组(将包含在JSON输出中)。json.Marshal()
函数如何猜测你不想编组的元素呢?它无法做到。
你必须排除那些你不希望出现在输出中的元素。在你的情况下,你想排除空的 Problem
结构体。
最好/最简单的方法是创建一个帮助函数,为你创建 []Problem
切片时排除空的结构体:
func createProbs(ps ...Problem) []Problem {
// 移除空的 Problem 结构体:
empty := Problem{}
for i := len(ps) - 1; i >= 0; i-- {
if ps[i] == empty {
ps = append(ps[:i], ps[i+1:]...)
}
}
return ps
}
使用这个函数创建切片的方式如下:
probs := createProbs(prob0, prob1, prob2)
在 Go Playground 上尝试修改后的应用程序。
修改后的代码输出(注意缺少了空的结构体):
[{"s":"s0","t":"t0"},{"s":"s1","t":"t1"},{"s":"s2","t":"t2"}]
[{"s":"s0","t":"t0"},{"s":"s2","t":"t2"}]
英文:
Once you add an element to the array/slice which you marshal, there's nothing you can do about it. If an element is in the array/slice, it will be marshalled (will be included in the JSON output). How could the json.Marshal()
function guess which elements you don't want to marshal? It can't.
You have to exclude elements which you don't what to appear in the output. In your case you want to exclude empty Problem
structs.
Best/easiest is to create a helper function which creates the []Problem
slice for you, empty structs excluded:
func createProbs(ps ...Problem) []Problem {
// Remove empty Problem structs:
empty := Problem{}
for i := len(ps) - 1; i >= 0; i-- {
if ps[i] == empty {
ps = append(ps[:i], ps[i+1:]...)
}
}
return ps
}
Using this creating a slice is like this:
probs := createProbs(prob0, prob1, prob2)
Try your modified application on the Go Playground.
Output of the modified code (notice the empty struct is missing):
[{"s":"s0","t":"t0"},{"s":"s1","t":"t1"},{"s":"s2","t":"t2"}]
[{"s":"s0","t":"t0"},{"s":"s2","t":"t2"}]
答案2
得分: 0
你无法轻易地实现这个。空结构也是一种结构,它将被序列化为{}
。即使是nil
值也会被序列化为null
。
以下是代码示例:
package main
import (
"encoding/json"
"fmt"
)
func main() {
xs := []interface{}{struct{}{},nil}
b, _ := json.Marshal(xs)
fmt.Println(string(b))
}
将产生以下输出:
[{},null]
解决方案是为Problems
类型实现json.Marshaler
接口,以跳过空结构。
更多信息请参考json.Marshaler接口。
英文:
You can't achieve this easily. Empty structure is also a structure and it will be serialized as {}
. Even nil
value will be serialized as null
.
The following code:
package main
import (
"encoding/json"
"fmt"
)
func main() {
xs := []interface{}{struct{}{},nil}
b, _ := json.Marshal(xs)
fmt.Println(string(b))
}
will produce:
[{},null]
<kbd>Playground</kbd>
The solution would be to implement json.Marshaller interface for Problems
type to skip empty structures.
答案3
得分: 0
func TWO() {
prob0 := Problem{S: "s0", T: "t0"}
prob1 := Problem{S: "", T: ""} // 空值被省略,但不是"{}"
prob2 := Problem{S: "s2", T: "t2"}
probs := Problems{prob0, prob1, prob2}
for i, v := range probs {
if v == reflect.Zero(reflect.TypeOf(v)).Interface() {
probs = append(probs[:i], probs[i+1:]...)
}
}
// fmt.Println(probs)
// 得到:[{s0 t0} { } {s2 t2}]
// 期望:[{s0 t0} {s2 t2}]
b, _ := json.Marshal(probs)
fmt.Println(string(b))
// 得到:[{"s":"s0","t":"t0"},{},{s":"s2","t":"t2"}]
// 期望:[{"s":"s0","t":"t0"},{"s":"s2","t":"t2"}]
}
或者,您可以使用反射
这是播放器的链接(https://play.golang.org/p/D0pW4xE4uf)。我不确定这是否是最好的答案,但这是一种替代方法。
英文:
func TWO() {
prob0 := Problem{S: "s0", T: "t0"}
prob1 := Problem{S: "", T: ""} // empty values omited BUT NOT "{}"
prob2 := Problem{S: "s2", T: "t2"}
probs := Problems{prob0, prob1, prob2}
for i,v := range probs {
if v == reflect.Zero(reflect.TypeOf(v)).Interface() {
probs = append(probs[:i], probs[i+1:]...)
}
}
// fmt.Println(probs)
// GOT: [{s0 t0} { } {s2 t2}]
// WANTED: [{s0 t0} {s2 t2}]
b, _ := json.Marshal(probs)
fmt.Println(string(b))
// GOT: [{"s":"s0","t":"t0"},{},{"s":"s2","t":"t2"}]
// WANTED: [{"s":"s0","t":"t0"},{"s":"s2","t":"t2"}]
}
Alternatively, you could use reflection
Here's the link to the playground (https://play.golang.org/p/D0pW4xE4uf). I'm not sure if this is the best answer but it's an alternative approach
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论