在Go语言中嵌套结构中组合任意的JSON对象。

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

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 (
    &quot;encoding/json&quot;
    &quot;fmt&quot;
)

type Problem struct {
     S string `json:&quot;s,omitempty&quot;`
     T string `json:&quot;t,omitempty&quot;`
}

 type Problems []Problem

 func main() {
     ONE()
     TWO()
}

 func ONE() {
     prob0 := Problem{S: &quot;s0&quot;, T: &quot;t0&quot;}
     prob1 := Problem{S: &quot;s1&quot;, T: &quot;t1&quot;}
     prob2 := Problem{S: &quot;s2&quot;, T: &quot;t2&quot;}

     probs := Problems{prob0, prob1, probe}

       // fmt.Println(probs) // CORRECT: [{s0 t0} {s1 t1} {s2 t2}]

     b, _ := json.Marshal(probs)
     fmt.Println(string(b))

       // CORRECT: [{&quot;s&quot;:&quot;s0&quot;,&quot;t&quot;:&quot;t0&quot;},{&quot;s&quot;:&quot;s1&quot;,&quot;t&quot;:&quot;t1&quot;},{&quot;s&quot;:&quot;s2&quot;,&quot;t&quot;:&quot;t2&quot;}]``
}

 func TWO() {
     prob0 := Problem{S: &quot;s0&quot;, T: &quot;t0&quot;}
     prob1 := Problem{S: &quot;&quot;, T: &quot;&quot;} // empty values omited BUT NOT {}
     prob2 := Problem{S: &quot;s2&quot;, T: &quot;t2&quot;}

     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:    [{&quot;s&quot;:&quot;s0&quot;,&quot;t&quot;:&quot;t0&quot;},{},{&quot;s&quot;:&quot;s2&quot;,&quot;t&quot;:&quot;t2&quot;}]
       // WANTED: [{&quot;s&quot;:&quot;s0&quot;,&quot;t&quot;:&quot;t0&quot;},{&quot;s&quot;:&quot;s2&quot;,&quot;t&quot;:&quot;t2&quot;}]
}

答案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 &gt;= 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):

[{&quot;s&quot;:&quot;s0&quot;,&quot;t&quot;:&quot;t0&quot;},{&quot;s&quot;:&quot;s1&quot;,&quot;t&quot;:&quot;t1&quot;},{&quot;s&quot;:&quot;s2&quot;,&quot;t&quot;:&quot;t2&quot;}]
[{&quot;s&quot;:&quot;s0&quot;,&quot;t&quot;:&quot;t0&quot;},{&quot;s&quot;:&quot;s2&quot;,&quot;t&quot;:&quot;t2&quot;}]

答案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接口,以跳过空结构。

Playground

更多信息请参考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 (
    &quot;encoding/json&quot;
    &quot;fmt&quot;
)

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: &quot;s0&quot;, T: &quot;t0&quot;}
	prob1 := Problem{S: &quot;&quot;, T: &quot;&quot;} // empty values omited BUT NOT &quot;{}&quot;
	prob2 := Problem{S: &quot;s2&quot;, T: &quot;t2&quot;}
	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:    [{&quot;s&quot;:&quot;s0&quot;,&quot;t&quot;:&quot;t0&quot;},{},{&quot;s&quot;:&quot;s2&quot;,&quot;t&quot;:&quot;t2&quot;}]
	// WANTED: [{&quot;s&quot;:&quot;s0&quot;,&quot;t&quot;:&quot;t0&quot;},{&quot;s&quot;:&quot;s2&quot;,&quot;t&quot;:&quot;t2&quot;}]
}

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

huangapple
  • 本文由 发表于 2015年4月1日 19:43:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/29389889.html
匿名

发表评论

匿名网友

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

确定