Serialize a mixed type JSON array in Go

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

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

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

发表评论

匿名网友

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

确定