在 Go 中的嵌入式结构中组合任意 JSON 个对象

Composing arbitrary JSON objects in embedded structs in Go

我正在尝试通过 Go 中的嵌入式结构以 {{"s":"v1", "t":"v2"}, {"s":"v3", "t":"v4"}, etc} 的形式生成 JSON 对象。

当提前知道 type Problems []Problem 中的所有 Problem 项时,一切都很好,正如下面的 func ONE()[ 中所见=20=] 这里.

但是,如果一些 K:V 对包含空值,我想避免得到 {{a},{},{c}} 而不是所需的 {{a},{c}},如下面的 func TWO() 和在演示中。

或者,如何在事先不知道是否会添加或省略概率项的情况下,随意编写下面的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, 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"}]
}

你无法轻易做到这一点。空结构也是一种结构,它会被序列化为{}。即使 nil 值也会被序列化为 null

代码如下: 主包

import (
    "encoding/json"
    "fmt"
)

func main() {
     xs := []interface{}{struct{}{},nil}
     b, _ := json.Marshal(xs)
     fmt.Println(string(b))
}

将产生:

[{},null]

Playground

解决方案是为 Problems 类型实现 json.Marshaller 接口以跳过空结构。

一旦将元素添加到编组的 array/slice 中,您就无能为力了。如果一个元素在 array/slice 中,它将被编组(将包含在 JSON 输出中)。 json.Marshal() 函数如何猜测您不想封送哪些元素?不能。

您必须排除不希望出现在输出中的元素。在您的情况下,您想排除空 Problem 结构。

Best/easiest 是创建一个辅助函数,为您创建 []Problem 切片,排除空结构:

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
}

使用这个创建切片是这样的:

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"}]
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"}]
}

或者,您可以使用反射

这是去游乐场 (https://play.golang.org/p/D0pW4xE4uf) 的 link。我不确定这是否是最佳答案,但这是另一种方法