【问题标题】:Marshal slice with jsonapi使用 jsonapi 进行元帅切片
【发布时间】:2017-07-10 21:17:30
【问题描述】:

我有一块结构体,我想使用https://github.com/google/jsonapi 对其进行编组。

使用单个结构,一切正常。我只是将指针作为第二个参数传递。

package main

import (
    "fmt"
    "os"

    "github.com/google/jsonapi"
)

type Spider struct {
    ID       int `jsonapi:"primary,spiders"`
    EyeCount int `jsonapi:"attr,eye_count"`
}

func main() {
    jsonapi.MarshalPayload(os.Stdout, &Spider{ID: 2, EyeCount: 12})
    // {"data":{"type":"spiders","id":"2","attributes":{"eye_count":12}}}
}

但是,当我尝试对切片执行相同操作时,情况就大不相同了。

首先,我将切片转换为指向这些结构的指针切片(不知道这里还能做什么,将指针传递给切片不起作用)。

spiders := []Spider{Spider{ID: 1, EyeCount: 8}, Spider{ID: 2, EyeCount: 12}}

var spiderPointers []*Spider
for _, x := range spiders {
    spiderPointers = append(spiderPointers, &x)
}
jsonapi.MarshalPayload(os.Stdout, spiderPointers)

它有效。有点。这是问题:

{"data":[
  {"type":"spiders","id":"2","attributes":{"eye_count":12}},
  {"type":"spiders","id":"2","attributes":{"eye_count":12}}]}

最后一个元素被重复并替换了其他元素。

最后,我想出了一个可行的解决方案:

spiders := []Spider{Spider{ID: 1, EyeCount: 8}, Spider{ID: 2, EyeCount: 12}}
var spiderPointers []interface{}
for _, x := range spiders {
    var spider Spider
    spider = x
    spiderPointers = append(spiderPointers, &spider)
}

jsonapi.MarshalPayload(os.Stdout, spiderPointers)
// {"data":[{"type":"spiders","id":"1","attributes":{"eye_count":8}},
//          {"type":"spiders","id":"2","attributes":{"eye_count":12}}]}

但我确信一定有更好的方法,因此提出了这个问题。

【问题讨论】:

标签: go


【解决方案1】:

好奇你能用这样的东西吗??

package main

import (
    "encoding/json"
    "fmt"
    "os"

    "github.com/google/jsonapi"
)

type Spider struct {
    ID       int `jsonapi:"primary,spiders"`
    EyeCount int `jsonapi:"attr,eye_count"`
}

func main() {
    // spiders is a slice of interfaces.. they can be unmarshalled back to spiders later if the need arises
    // you don't need a loop to create pointers either... so better in memory usage and a bit cleaner to read.. you can in theory load the []interface{} with a for loop as well since you mentioned getting this data from a db
    var spiders []interface{}
    spiders = append(spiders, &Spider{ID: 1, EyeCount: 8}, &Spider{ID: 2, EyeCount: 12})
    fmt.Println("jsonapi, MarshalPayload")
    jsonapi.MarshalPayload(os.Stdout, spiders)

    // test against marshall indent for sanity checking
    b, _ := json.MarshalIndent(spiders, "", " ")
    fmt.Println("marshall indent")
    fmt.Println(string(b))
}

【讨论】:

  • >"spiders is a slice of interfaces.." 抱歉,我想我应该更好地表述它。我从数据库得到的原始切片是spiders := []Spider{Spider{ID: 1, EyeCount: 8}, Spider{ID: 2, EyeCount: 12}}。它不是一片接口,而是一片蜘蛛。这就是为什么我在问题中使用 for 循环将其转换为一片接口。
  • 既然 jsonapi 需要一片接口。那么也许你已经在做最好的事情了。在我看来,唯一更清洁的选择是不使用。 jsonapi 并创建自己的服装编组器。鉴于 jsonapi 也严重依赖接口,相比之下反射已经非常慢了。
  • 我觉得你是对的,我刚刚看了github.com/manyminds/api2go,它似乎更适合这种情况(它不需要执行这样的转换github.com/manyminds/api2go#manual-marshalling--unmarshalling
  • 我刚刚检查了它我也认为你会用 api2go 玩得更好
【解决方案2】:

为什么不那样 [either3]?

var spiders interface{}
db.Model(spiders).Select()
jsonapi.MarshalPayload(os.Stdout, spiders)

【讨论】:

  • 啊,那是因为我从数据库中获取这些记录,而数据库库以这种形式返回记录。
  • 为什么也不这样呢?
  • 我试过了,但在这种情况下,MarshalPayload 根本不会向 Stdout 写入任何内容。不过没有错误。
  • 数据库返回什么(哪种类型)?
  • 我使用 go-pg。所以首先我定义一个像 var spiders []Spider 这样的切片,然后调用像 db.Model(&spiders).Select() 这样写入它的库函数。
【解决方案3】:

您遇到的是指针问题,而不是 jsonapi 问题。

当您循环一个值时,循环变量会被创建一次,并且会一遍又一遍地重新分配该值。指向循环变量的指针将始终指向相同的地址,您只需不断更新值。这会为您留下一个重复相同指针的切片,所有这些指针都指向放置在那里的最后一个值。

解决方案是创建一个作用域闭包,该闭包捕获新变量中的值,然后使用该变量的指针。请尝试以下操作:

spiders := []Spider{Spider{ID: 1, EyeCount: 8}, Spider{ID: 2, EyeCount: 12}}

var spiderPointers []*Spider
for _, x := range spiders {
    x := x // capture loop variable
    spiderPointers = append(spiderPointers, &x)
}
jsonapi.MarshalPayload(os.Stdout, spiderPointers)

【讨论】:

    猜你喜欢
    • 2019-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-06
    • 1970-01-01
    • 2010-09-20
    • 1970-01-01
    • 2023-03-06
    相关资源
    最近更新 更多