2017-07-17 80 views
1

Golang初学者,我试图编写一个通用函数来服务ReST请求。我传递一个函数来创建一个新的资源(结构),其上实现了一个接口,因为我也会调用结构体上的方法。在解码JSON时,记录类型显示正确的(结构)类型,但JSON解码器似乎只识别它无法解码的接口。Golang 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 类型main.anchor
restCreate解组成main.anchor:{ID:名称:}
[ERR] restCreate:JSON:不能解组object into Go值main.resource

为什么printf显示结构体类型和json.Decoder的接口?
我会很感激在什么地方出了错,以及如何在一个通用的方法来解决这个任何指标...

回答

2

这是因为你尝试使用指针接口来解组。你需要在功能

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

返回一个指针,你并不需要在此行中获得地址: err := dcdr.Decode(&res)

这里是小工作示例:https://play.golang.org/p/3E0RmGTURO

+0

这工作 - 谢谢帕夫洛。只是有点困惑,为什么你可以返回合成文字和它的地址,将进一步探索。 – mhcbinder

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/

+0

谢谢您的回答。我想为一些不同的具体结构使用“restCreate”,但你的回答(和Pavlo's)解决了这个问题。 – mhcbinder

+0

在阅读您提供的链接后,我认为作者正在争论我实现的确切解决方案 - 在基础具体类型上使用接口。再次感谢您提供的信息! – mhcbinder