英文:
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 (
"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))
}
答案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()
适配器分别转换为 Rectangle
或 Circle
。在你的情况下,你需要将 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 &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,
}
}
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 't' and append to 'ret'
return ret
}
func main() {
var result []interface{}
json.Unmarshal(t.ToFoo(), &result)
// ...
}
I'll leave the implementation details for you.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论