为什么这里使用了json包的Decode和Marshal方法?

时间:2016-07-27 18:02:24

标签: json go

在以下Web Development with Go by Shiju Varghese示例中,该示例用于为每个HTTP请求使用新的MongoDB会话实现HTTP服务器:

  • 为什么在json函数中使用Decode包的PostCategory方法?

  • 为什么在json函数中使用Marshal包的GetCategories方法?

起初我认为Decode中的PostCategoryMarshal中的GetCategories彼此相反,但后来我发现有Unmarshal方法,可能是Encode包中的json个方法。所以我之前问过a question

这是程序

package main
import (
    "encoding/json"
    "log"
    "net/http"
    "github.com/gorilla/mux"
    "gopkg.in/mgo.v2"
    "gopkg.in/mgo.v2/bson"
)
var session *mgo.Session

type (
    Category struct {
Id bson.ObjectId `bson:"_id,omitempty"`
Name string
Description string
    }
    DataStore struct {
session *mgo.Session
    }
)
//Close mgo.Session
func (d *DataStore) Close() {
    d.session.Close()
}
//Returns a collection from the database.
func (d *DataStore) C(name string) *mgo.Collection {
    return d.session.DB("taskdb").C(name)
}
//Create a new DataStore object for each HTTP request
func NewDataStore() *DataStore {
    ds := &DataStore{
session: session.Copy(),
    }
    return ds
}

//Insert a record
func PostCategory(w http.ResponseWriter, r *http.Request) {
    var category Category
    // Decode the incoming Category json
    err := json.NewDecoder(r.Body).Decode(&category)
    if err != nil {
panic(err)
    }
    ds := NewDataStore()
    defer ds.Close()
    //Getting the mgo.Collection
    c := ds.C("categories")
    //Insert record
    err = c.Insert(&category)
    if err != nil {
panic(err)
    }
w.WriteHeader(http.StatusCreated)
}

//Read all records
func GetCategories(w http.ResponseWriter, r *http.Request) {
    var categories []Category
    ds := NewDataStore()
    defer ds.Close()
    //Getting the mgo.Collection
    c := ds.C("categories")
    iter := c.Find(nil).Iter()
    result := Category{}
    for iter.Next(&result) {
categories = append(categories, result)
    }
w.Header().Set("Content-Type", "application/json")
j, err := json.Marshal(categories)
if err != nil {
panic(err)
    }
w.WriteHeader(http.StatusOK)
w.Write(j)
}

func main() {
    var err error
    session, err = mgo.Dial("localhost")
    if err != nil {
panic(err)
    }
    r := mux.NewRouter()
r.HandleFunc("/api/categories", GetCategories).Methods("GET")
r.HandleFunc("/api/categories", PostCategory).Methods("POST")
    server := &http.Server{
Addr:    ":8080",
Handler: r,
    }
    log.Println("Listening...")
    server.ListenAndServe()
}

2 个答案:

答案 0 :(得分:4)

我认为在这里使用json.NewDecoder的主要原因是直接从响应的正文(r.Body)读取,因为NewDecoder需要io.Reader作为输入。

您可以使用json.Unmarshal,但是您必须首先将响应正文读入[]byte并将该值传递给json.UnmarshalNewDecoder在这里更方便。

答案 1 :(得分:1)

TL; DR - Marshal / Unmarshal获取并返回字节切片,而Encode / Decode做同样的事情,但是读取来自流的字节,例如网络连接(读者和作者)。

encoding/json包使用EncoderDecoder类型来处理数据的,即io.Reader和{ {3}}的。这意味着您可以直接从网络套接字(或者在这种情况下实现io.Reader的HTTP主体)获取数据,并在字节进入时将其转换为JSON。这样做,我们可以继续并开始在任何数据可用之后但在我们收到整个文档之前处理JSON(在使用大文档的慢速网络连接上,这可以节省我们很多时间,并且对于一些具有“无限大小”文档流的流协议这绝对是必要的!)

然而,

MarshalUnmarshal对字节切片进行操作,这意味着您必须先将整个JSON文档放在内存中才能使用它们。在您的示例中,作者使用Marshal,因为他们已经有一个[]byte切片,因此使用字节切片构造缓冲区没有意义,然后使用该缓冲区创建一个编码器,然后调用encode:相反,他们只能让元帅为他们做到了。

实际上,Marshal / Unmarshal只是编码器和解码器之上的便捷方法。如果我们看一下io.Writer,我们会发现它只是构建一个编码器(或编码器的内部表示,但相信我,它们是相同的,如果你想要证明你可以看看source for Unmarshal并看到它还创建了一个encodeState),然后返回输出字节:

func Marshal(v interface{}) ([]byte, error) {
    e := &encodeState{}
    err := e.marshal(v)
    if err != nil {
        return nil, err
    }
    return e.Bytes(), nil
}