【问题标题】:Unmarshaling array of objects in Go在 Go 中解组对象数组
【发布时间】:2017-12-06 22:42:29
【问题描述】:

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

也试过this

任何正确方向的线索都将受到高度赞赏。

【问题讨论】:

  • 如果你想要 JSON 数组,为什么要使用地图?你需要从那个确切的数据结构开始吗?
  • ^.您需要使用切片/数组而不是地图。映射与 JSON 对象相关,而切片/数组与列表相关。试试[]interface{},而不是map[int]stringmap[int]float32,因为你有一个变量类型列表。
  • 您可能需要一种适配器方法,将一种结构转换为另一种结构,以便正确解组。在某些情况下,根据数据类型的独特要求,您无法直接进行 1:1 映射来解组,而您的情况可能就是这种情况。例如,传入一个结构切片,返回一个 interface{} 切片,其中值是结构的值。
  • @Li3ro,您的结构是与 JSON 完全不同的结构。具有与 JSON 匹配的结构的 Marshal/unmarshal。
  • 如果我的回答对你有用,请告诉我。

标签: arrays go


【解决方案1】:

也许这是您的预期。可以实现自定义 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

【讨论】:

  • 这对我有用,所以这是公认的答案。谢谢你们俩
【解决方案2】:

您遇到的问题是您试图解组地图,而地图将与 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{}

    // Do some stuff to take values of 't' and append to 'ret'

    return ret
}

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

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

    // ...
}

我会为你留下实现细节。

【讨论】:

  • 这个编译..我想会工作!但我无法测试它,因为这个小结构是我使用的结构怪物树的一部分,当我需要解组时,它会将它们一起解组。我想知道是否有办法在解组时调用此函数..
  • 哦,我明白了.. 嗯,是的,我不相信解组有任何形式的“回调”,可以让你在生命周期中访问做类似的事情。但是您可以尝试在解组之前为每个Timestamp 属性递归地调用怪物结构上的适配器方法。
  • @Lansana,当然有。这就是MarshalerUnmarshaler 接口。
  • @JimB 啊,好消息!我还没有接触过这些接口。
猜你喜欢
  • 2020-10-07
  • 1970-01-01
  • 2017-07-11
  • 1970-01-01
  • 2016-11-20
  • 1970-01-01
  • 1970-01-01
  • 2018-08-30
  • 1970-01-01
相关资源
最近更新 更多