在Go语言中解组对象数组。

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

Unmarshaling array of objects in Go

问题

在GO语言中,我尝试生成以下JSON:

[["my",257.14,257.24],["txt", 121.11, 65.555]]

但在进行解组时失败了。

这是我尝试的代码:

x := []MyStruct{{Zero: map[int]string{0: "str"}, One: map[int]float32{1: 5.6}, Two: map[int]float32{1: 5.88}}}

其中MyStruct定义如下:

type Timestamp struct {
Zero map[int]string json:"0"
One map[int]float32 json:"1"
Two map[int]float32 json:"2"
}

这会生成错误的JSON结构:

"myStruct":[{"0":{"0":"has"},"1":{"1":5.6},"2":{"1":5.88}}]

我也尝试了这个解答

如果有任何指导方向,将不胜感激。

英文:

in GO, I've tried to produce the following json :

[["my",257.14,257.24],["txt", 121.11, 65.555]]

from a struct that's undergo unmarshaling - and i'm failing to do so.

Here is what I tried:

x := []MyStruct{{Zero: map[int]string{0: "str"}, One: map[int]float32{1: 5.6}, Two: map[int]float32{1: 5.88}}}

where MyStruct is :

type Timestamp struct {
  Zero		map[int]string		`json:"0"`
  One		map[int]float32		`json:"1"`
  Two		map[int]float32		`json:"2"`
}

this produces the wrong json structure:

"myStruct":[{"0":{"0":"has"},"1":{"1":5.6},"2":{"1":5.88}}]

tried this as well

any clue in the right direction will be highly appreciated.

答案1

得分: 4

也许这是你期望的。可以实现自定义的MarshalJSON/UnmarshalJSON。

package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
)

type Timestamp struct {
	Zero []string
	One  []float32
	Two  []float32
}

func (t *Timestamp) UnmarshalJSON(b []byte) error {
	var arr [][3]interface{}
	err := json.Unmarshal(b, &arr)
	if err != nil {
		return nil
	}
	t.Zero = nil
	t.One = nil
	t.Two = nil
	for _, v := range arr {
		if len(v) != 3 {
			return errors.New("invalid json")
		}
		if s, ok := v[0].(string); ok {
			t.Zero = append(t.Zero, s)
		}
		if f, ok := v[1].(float64); ok {
			t.One = append(t.One, float32(f))
		}
		if f, ok := v[2].(float64); ok {
			t.Two = append(t.Two, float32(f))
		}
	}
	return nil
}

func (t *Timestamp) MarshalJSON() ([]byte, error) {
	var arr [][3]interface{}
	var max int
	if max < len(t.Zero) {
		max = len(t.Zero)
	}
	if max < len(t.One) {
		max = len(t.One)
	}
	if max < len(t.Two) {
		max = len(t.Two)
	}
	for i := 0; i < max; i++ {
		var v [3]interface{}
		if i < len(t.Zero) {
			v[0] = t.Zero[i]
		}
		if i < len(t.One) {
			v[1] = t.One[i]
		}
		if i < len(t.Two) {
			v[2] = t.Two[i]
		}
		arr = append(arr, v)
	}
	return json.Marshal(arr)
}

const j = `[["my",257.14,257.24],["txt", 121.11, 65.555]]`

func main() {
	var ts Timestamp
	err := json.Unmarshal([]byte(j), &ts)
	if err != nil {
		log.Fatal(err)
	}
	b, err := json.Marshal(&ts)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(b))
}

链接:https://play.golang.org/p/WtVEja1JDY

英文:

Maybe this is your expected. It's possible to implement custom MarshalJSON/UnmarshalJSON.

package main
import (
&quot;encoding/json&quot;
&quot;errors&quot;
&quot;fmt&quot;
&quot;log&quot;
)
type Timestamp struct {
Zero []string
One  []float32
Two  []float32
}
func (t *Timestamp) UnmarshalJSON(b []byte) error {
var arr [][3]interface{}
err := json.Unmarshal(b, &amp;arr)
if err != nil {
return nil
}
t.Zero = nil
t.One = nil
t.Two = nil
for _, v := range arr {
if len(v) != 3 {
return errors.New(&quot;invalid json&quot;)
}
if s, ok := v[0].(string); ok {
t.Zero = append(t.Zero, s)
}
if f, ok := v[1].(float64); ok {
t.One = append(t.One, float32(f))
}
if f, ok := v[2].(float64); ok {
t.Two = append(t.Two, float32(f))
}
}
return nil
}
func (t *Timestamp) MarshalJSON() ([]byte, error) {
var arr [][3]interface{}
var max int
if max &lt; len(t.Zero) {
max = len(t.Zero)
}
if max &lt; len(t.One) {
max = len(t.One)
}
if max &lt; len(t.Two) {
max = len(t.Two)
}
for i := 0; i &lt; max; i++ {
var v [3]interface{}
if i &lt; len(t.Zero) {
v[0] = t.Zero[i]
}
if i &lt; len(t.One) {
v[1] = t.One[i]
}
if i &lt; len(t.Two) {
v[2] = t.Two[i]
}
arr = append(arr, v)
}
return json.Marshal(arr)
}
const j = `[[&quot;my&quot;,257.14,257.24],[&quot;txt&quot;, 121.11, 65.555]]`
func main() {
var ts Timestamp
err := json.Unmarshal([]byte(j), &amp;ts)
if err != nil {
log.Fatal(err)
}
b, err := json.Marshal(&amp;ts)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(b))
}

https://play.golang.org/p/WtVEja1JDY

答案2

得分: 2

你遇到的问题是你试图解组一个映射(map),而映射会对应一个 JSON 对象。你期望的输出是一个列表,所以你需要解组一个数组或切片来获取一个列表作为你的值。

尝试创建一个适配器。

以下是一个简单的示例:

type Object struct {
    Base   float32 
    Radius float32 
    Height float32 
    X      float32 
    Y      float32 
}

func (obj *Object) ToCircle() *Circle {
    return &Circle{
        Radius: obj.Radius,
        X:      obj.X,
        Y:      obj.Y,
    }
}

func (obj *Object) ToRectangle() *Rectangle {
    return &Rectangle{
        Base:   obj.Base,
        Height: obj.Height,
        X:      obj.X,
        Y:      obj.Y,
    }
}

在上面的示例中,Object 通过 ToRectangle()ToCircle() 适配器分别转换为 RectangleCircle。在你的情况下,你需要将 Timestamp 转换为 []interface{}。然后你可以解组,你将得到一个包含在该切片中的任何值的列表,这是你在这种情况下期望的输出。

例如,你的适配器的签名可以是这样的:

func (t *Timestamp) ToFoo() []interface{} {
    var ret []interface{}

    // 做一些处理,将 't' 的值添加到 'ret' 中

    return ret
}

func main() {
    var result []interface{}

    json.Unmarshal(t.ToFoo(), &result)

    // ...
}

具体的实现细节留给你来完成。

英文:

The problem you are having is you're trying to unmarshal a map, and a map will correlate to a JSON object. Your desired output is a list, so you need to unmarshal an array or a slice to get a list as your values.

Try making an adapter.

Small example:

type Object struct {
Base   float32 
Radius float32 
Height float32 
X      float32 
Y      float32 
}
func (obj *Object) ToCircle() *Circle {
return &amp;Circle{
Radius: obj.Radius,
X:      obj.X,
Y:      obj.Y,
}
}
func (obj *Object) ToRectangle() *Rectangle {
return &amp;Rectangle{
Base:   obj.Base,
Height: obj.Height,
X:      obj.X,
Y:      obj.Y,
}
}

In the example above, Object is converted to a Rectangle or a Circle using the ToRectangle() and ToCircle() adapters, respectively. In your case, you need to convert Timestamp to a []interface{}. Then you can unmarshal and you'll just get a list of whatever values are in that slice, which is your desired output in this case.

For intsance, the signature if your adapter could look like this:

func (t *Timestamp) ToFoo() []interface{} {
var ret []interface{}
// Do some stuff to take values of &#39;t&#39; and append to &#39;ret&#39;
return ret
}
func main() {
var result []interface{}
json.Unmarshal(t.ToFoo(), &amp;result)
// ...
}

I'll leave the implementation details for you.

huangapple
  • 本文由 发表于 2017年7月3日 23:31:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/44889361.html
匿名

发表评论

匿名网友

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

确定