Golang JSON解码器无法识别类型

时间:2017-07-17 10:58:56

标签: json go

仍然是Golang初学者,我正在尝试编写一个通用函数来提供ReST请求。我传递一个函数来创建一个新的资源(struct),并在其上实现一个接口,因为我也会在struct上调用方法。解码JSON时,记录类型会显示正确的(struct)类型,但JSON解码器似乎只能识别无法解码的接口。

package main

import (
    "encoding/json"
    "github.com/julienschmidt/httprouter"
    "log"
    "net/http"
    "strings"
)

// general resource interface
type resource interface {
    // check semantics and return an array of errors or nil if no error found
    check() []string
    // update the resource in backend
    update() error
}

// specific resource named "anchor"
type anchor struct {
    ID   string `json:"id"`
    Name string `json:"name"`
}

func newAnchor() resource {
    return anchor{}
}

func (a anchor) check() []string {
    return nil
}

func (a anchor) update() error {
    return nil
}

// generic function to create (POST) a new resource
func restCreate(newResource func() resource) httprouter.Handle {
    return func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
        const F = "restCreate"
        var checkErrs []string

        res := newResource()
        log.Printf("%s res type %T\n", F, res)
        dcdr := json.NewDecoder(r.Body)
        err := dcdr.Decode(&res)
        log.Printf("%s Unmarshalled into %T: %+v\n", F, res, res)
        if err == nil {
            checkErrs = res.check()
        }
        switch {
        case err != nil:
            w.WriteHeader(http.StatusInternalServerError)
            log.Printf("[ERR] %s: %v\n", F, err)
        case checkErrs != nil:
            w.WriteHeader(http.StatusBadRequest)
            w.Write([]byte(strings.Join(checkErrs, "\n")))
            log.Printf("%s: %v\n", F, err)
        default:
            res.update()
            bs, _ := json.Marshal(res)
            w.Write(bs)
        }
    }
}

func main() {
    r := httprouter.New()
    r.POST("/anchors", restCreate(newAnchor))
    http.ListenAndServe(":8080", r)
}

执行日志显示:

restCreate res type main.anchor
restCreate unmarshalled into main.anchor:{ID:Name:}
[ERR] restCreate:json:无法将对象解组为 main.resource 类型的Go值

为什么Printf显示结构类型和json.Decoder接口?
我很欣赏任何关于出了什么问题以及如何以通用方式解决这个问题的指标...

2 个答案:

答案 0 :(得分:2)

这是因为您尝试使用指向接口的指针来解组。您需要在函数中返回指针

func newAnchor() resource {
    return &anchor{}
}

而且您不需要在此行中获取地址: err := dcdr.Decode(&res)

以下是一个小工作示例:https://play.golang.org/p/3E0RmGTURO

答案 1 :(得分:0)

除非变量持有指向所需具体类型的指针,否则不能解组成接口,因为json.Decode将不知道要使用哪种具体类型。您可以使用两种解决方法:

newResource在引擎盖下返回具体类型:

func newResource() resource {
    return &anchor{}
}

这种方式json.Decode知道将您的JSON解组为anchor

使用newAnchor代替newResource:这在restCreate函数中更具可读性,更具惯用性[1]。

[1] http://idiomaticgo.com/post/best-practice/accept-interfaces-return-structs/