英文:
Serialize a mixed type JSON array in Go
问题
我想返回一个类似这样的结构:
{
"results": [
["ooid1", 2.0, "一些文本"],
["ooid2", 1.3, "其他文本"],
]
}
这是一个由数组组成的数组,其中包含字符串、浮点数和Unicode字符。
如果是在Python中,我可以这样做:
import json
json.dumps({'results': [["ooid1", 2.0, u"一些文本"], ...]})
但是在Go语言中,你不能有一个混合类型的数组(或切片)。
我想过使用这样的结构体:
type Row struct {
Ooid string
Score float64
Text rune
}
但我不想让每个元素变成一个字典,我希望它们变成一个由3个元素组成的数组。
英文:
I want to return a structure that looks like this:
{
results: [
["ooid1", 2.0, "Söme text"],
["ooid2", 1.3, "Åther text"],
]
}
That's an array of arrags that is string, floating point number, unicode character.
If it was Python I'd be able to:
import json
json.dumps({'results': [["ooid1", 2.0, u"Söme text"], ...])
But in Go you can't have an array (or slice) of mixed types.
I thought of using a struct like this:
type Row struct {
Ooid string
Score float64
Text rune
}
But I don't want each to become a dictionary, I want it to become an array of 3 elements each.
答案1
得分: 15
我们可以通过实现json.Marshaler
接口来自定义对象的序列化方式。对于我们的特定情况,我们似乎有一个要将Row
元素切片编码为异构值数组的切片。我们可以通过在Row
类型上定义一个MarshalJSON
函数来实现这一点,使用一个中间的interface{}
切片来编码混合值。
这个示例演示了这一点:
package main
import (
"encoding/json"
"fmt"
)
type Row struct {
Ooid string
Score float64
Text string
}
func (r *Row) MarshalJSON() ([]byte, error) {
arr := []interface{}{r.Ooid, r.Score, r.Text}
return json.Marshal(arr)
}
func main() {
rows := []Row{
{"ooid1", 2.0, "Some text"},
{"ooid2", 1.3, "Other text"},
}
marshalled, _ := json.Marshal(rows)
fmt.Println(string(marshalled))
}
当然,我们也可能希望反过来,从JSON字节回到结构体。因此,有一个类似的json.Unmarshaler
接口可以使用。
func (r *Row) UnmarshalJSON(bs []byte) error {
arr := []interface{}{}
json.Unmarshal(bs, &arr)
// TODO: 在这里添加错误处理。
r.Ooid = arr[0].(string)
r.Score = arr[1].(float64)
r.Text = arr[2].(string)
return nil
}
这使用了类似的技巧,首先使用一个中间的interface{}
切片,使用解组器将值放入这个通用容器,然后将值放回我们的结构体。
package main
import (
"encoding/json"
"fmt"
)
type Row struct {
Ooid string
Score float64
Text string
}
func (r *Row) UnmarshalJSON(bs []byte) error {
arr := []interface{}{}
json.Unmarshal(bs, &arr)
// TODO: 在这里添加错误处理。
r.Ooid = arr[0].(string)
r.Score = arr[1].(float64)
r.Text = arr[2].(string)
return nil
}
func main() {
rows := []Row{}
text := `
[
["ooid4", 3.1415, "pi"],
["ooid5", 2.7182, "euler"]
]
`
json.Unmarshal([]byte(text), &rows)
fmt.Println(rows)
}
你可以在这里阅读一个完整的示例。
英文:
We can customize how an object is serialized by implementing the json.Marshaler interface. For our particular case, we seem to have a slice of Row
elements that we want to encode as an array of heterogenous values. We can do so by defining a MarshalJSON
function on our Row
type, using an intermediate slice of interface{}
to encode the mixed values.
This example demonstrates:
package main
import (
"encoding/json"
"fmt"
)
type Row struct {
Ooid string
Score float64
Text string
}
func (r *Row) MarshalJSON() ([]byte, error) {
arr := []interface{}{r.Ooid, r.Score, r.Text}
return json.Marshal(arr)
}
func main() {
rows := []Row{
{"ooid1", 2.0, "Söme text"},
{"ooid2", 1.3, "Åther text"},
}
marshalled, _ := json.Marshal(rows)
fmt.Println(string(marshalled))
}
Of course, we also might want to go the other way around, from JSON bytes back to structs. So there's a similar json.Unmarshaler interface that we can use.
func (r *Row) UnmarshalJSON(bs []byte) error {
arr := []interface{}{}
json.Unmarshal(bs, &arr)
// TODO: add error handling here.
r.Ooid = arr[0].(string)
r.Score = arr[1].(float64)
r.Text = arr[2].(string)
return nil
}
This uses a similar trick of first using an intermediate slice of interface{}
, using the unmarshaler to place values into this generic container, and then plop the values back into our structure.
package main
import (
"encoding/json"
"fmt"
)
type Row struct {
Ooid string
Score float64
Text string
}
func (r *Row) UnmarshalJSON(bs []byte) error {
arr := []interface{}{}
json.Unmarshal(bs, &arr)
// TODO: add error handling here.
r.Ooid = arr[0].(string)
r.Score = arr[1].(float64)
r.Text = arr[2].(string)
return nil
}
func main() {
rows := []Row{}
text := `
[
["ooid4", 3.1415, "pi"],
["ooid5", 2.7182, "euler"]
]
`
json.Unmarshal([]byte(text), &rows)
fmt.Println(rows)
}
You can read a full example here.
答案2
得分: 12
使用[]interface{}
type Results struct {
Rows []interface{} `json:"results"`
}
如果你想访问存储在[]interface{}
中的值,你将需要使用类型断言。
for _, row := range results.Rows {
switch r := row.(type) {
case string:
fmt.Println("string", r)
case float64:
fmt.Println("float64", r)
case int64:
fmt.Println("int64", r)
default:
fmt.Println("not found")
}
}
英文:
Use []interface{}
type Results struct {
Rows []interface{} `json:"results"`
}
You will then have to use type assertion if you want to access the values stored in []interface{}
for _, row := range results.Rows {
switch r := row.(type) {
case string:
fmt.Println("string", r)
case float64:
fmt.Println("float64", r)
case int64:
fmt.Println("int64", r)
default:
fmt.Println("not found")
}
}
答案3
得分: 0
一些笨拙的代码,但你可以这样写:
type result [][]interface{}
type results struct {
Results result
}
工作示例:https://play.golang.org/p/IXAzZZ3Dg7
英文:
Some clumsy, but you can
type result [][]interface{}
type results struct {
Results result
}
Working example https://play.golang.org/p/IXAzZZ3Dg7
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论