Golang结构体具有io.Reader无法序列化。

huangapple go评论73阅读模式
英文:

Golang struct having io.Reader unable to serialize

问题

我正在尝试将以下结构体序列化为byte[],以便将其存储到数据库中,然后在从数据库中读取时进行反序列化。

type Response struct {
    Headers map[string][]string
    Body    io.Reader
    Status  int
}

以下是我创建响应对象并为其设置值的代码:

resp := new(Response)
resp.Body = bytes.NewReader(outBytes) // outBytes是byte[]
resp.Headers.SetKeyValue("Content-Type", "text/json") // SetKeyValue是用于添加头部的方法
resp.Headers.SetKeyValue("Url-Type", "broker")
resp.Status = 200

我使用json.Marshal()resp对象序列化如下:

b, _ := json.Marshal(resp)

以下是我用于反序列化的代码:

var r Response
r.Body = &bytes.Buffer{}
json.Unmarshal(b, &r)

问题出在反序列化上,我无法获取resp.Body对象。尽管我设置了body对象(参见上文),但它始终为nil或空白。我能够从反序列化中获取结构体的HeadersStatus字段,但无法获取Body字段。

我知道Body字段是一个io.Reader,需要进行一些处理。

非常感谢您的帮助。

英文:

I am trying to serialize below struct into byte[] to store it into DB and then while reading it from DB I am deserializing it.

type Response struct {
   	Headers map[string][]string
   	Body    io.Reader
   	Status  int
}

Below is the code how I am creating response object and setting up the value for it.

resp := new(Response)
resp.Body = bytes.NewReader(outBytes) //outBytes is byte[]
resp.Headers.SetKeyValue("Content-Type", "text/json") //SetKeyValue is the method created for adding headers
resp.Headers.SetKeyValue("Url-Type", "broker")
resp.Status = 200

I am using json.Marshal() to serialize the resp object as below.

b, _ := json.Marshal(resp)

Below is the code, I am using to deserialize.

var r Response
r.Body = &bytes.Buffer{}
json.Unmarshal(b,&r)

Problem is with deserialization, I am not able to get the resp.Body object. It is always nil or blank in spite of setting body object (see above). I am able to get Headers and Status field of the struct back from deserialize but not Body.

I know there is something to be handle with Body field which is an io.Reader.

Any help would be really great.

答案1

得分: 6

简短回答: JSON marshaller 不会使用 Read() 函数从 io.Reader 中读取字符串。你可以使用实现了 Marshaler 接口的类型,而不是使用 io.Reader

Marshaller 的工作原理:
Marshal 递归地遍历值 v。如果遇到的值实现了 Marshaler 接口且不是空指针,Marshal 将调用其 MarshalJSON 方法生成 JSON。如果没有 MarshalJSON 方法但值实现了 encoding.TextMarshaler 接口,Marshal 将调用其 MarshalText 方法。空指针异常并不是严格必要的,但它模仿了 UnmarshalJSON 行为中类似的必要异常。

否则,Marshal 使用以下 type-dependent 默认编码:

  • 布尔值编码为 JSON 布尔值。
  • 浮点数、整数和数字值编码为 JSON 数字。

实现:
你可以这样做:

type Response struct {
    Headers map[string][]string
    Body    *JSONReader
    Status  int
}

type JSONReader struct {
    *bytes.Reader
}

func NewJSONReader(outBytes []byte) *JSONReader {
    jr := new(JSONReader)
    jr.Reader = bytes.NewReader(outBytes)
    return jr
}

func (js JSONReader) MarshalJSON() ([]byte, error) {
    data, err := ioutil.ReadAll(js.Reader)
    if err != nil {
        return nil, err
    }
    data = []byte(`"` + string(data) + `"`)
    return data, nil
}

// UnmarshalJSON sets *jr to a copy of data.
func (jr *JSONReader) UnmarshalJSON(data []byte) error {
    if jr == nil {
        return errors.New("json.JSONReader: UnmarshalJSON on nil pointer")
    }
    if data == nil {
        return nil
    }
    data = []byte(strings.Trim(string(data), `"`))
    jr.Reader = bytes.NewReader(data)
    return nil
}

这是一个带有实现和示例用法的 Go Playground 链接:链接

英文:

Short Answer : JSON marshaller will not use Read() function to read the string from io.Reader . Instead of using io.Reader you may use a type that implements Marshaler interface.

How Marshaller works :
Marshal traverses the value v recursively. If an encountered value implements the Marshaler interface and is not a nil pointer, Marshal calls its MarshalJSON method to produce JSON. If no MarshalJSON method is present but the value implements encoding.TextMarshaler instead, Marshal calls its MarshalText method. The nil pointer exception is not strictly necessary but mimics a similar, necessary exception in the behavior of UnmarshalJSON.

Otherwise, Marshal uses the following type-dependent default encodings:

  • Boolean values encode as JSON booleans.
  • Floating point, integer, and Number values encode as JSON numbers.

Implementaton
This is what you may do

type Response struct {
	Headers map[string][]string
	Body    *JSONReader
	Status  int
}

type JSONReader struct {
	*bytes.Reader
}

func NewJSONReader(outBytes []byte) *JSONReader {
	jr := new(JSONReader)
	jr.Reader = bytes.NewReader(outBytes)
	return jr
}

func (js JSONReader) MarshalJSON() ([]byte, error) {
	data, err := ioutil.ReadAll(js.Reader)
	if err != nil {
		return nil, err
	}
	data = []byte(`"` + string(data) + `"`)
	return data, nil
}

// UnmarshalJSON sets *jr to a copy of data.
func (jr *JSONReader) UnmarshalJSON(data []byte) error {
	if jr == nil {
		return errors.New("json.JSONReader: UnmarshalJSON on nil pointer")
	}
	if data == nil {
		return nil
	}
	data = []byte(strings.Trim(string(data), "\""))
	jr.Reader = bytes.NewReader(data)
	return nil
}

Here is a go playground link with the implementation and sample use : link

答案2

得分: 1

概述

io.Reader 是一个接口,因此无法进行编组。每个编组结构属性都必须实现 Marshaler 接口才能进行编组。你可以声明自己的编组器包装结构体,以从 bytes.Reader 中编组数据。

为什么接口无法进行编组?

在Go语言中,接口提供了一种指定对象行为的方式:如果某个对象可以执行某个行为,那么它就可以在这里使用。相反,Go的结构体是字段的类型化集合。它们用于将数据组合在一起形成记录。Go支持在结构体类型上定义方法,而不是接口类型。

实现

type Response struct {
    Body *MarshalableReader
}

type MarshalableReader struct {
    *bytes.Reader
}

func (r MarshalableReader) MarshalJSON() ([]byte, error) {
    data, err := ioutil.ReadAll(r.Reader)
    if err != nil {
        return nil, err
    }
    return []byte(fmt.Sprintf("%q", data)), nil
}

func main() {
    resp := Response{&MarshalableReader{bytes.NewReader([]byte("Blah Blah"))}}

    marshaled, err := json.Marshal(resp)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("JSON: %s\n", marshaled)
}
英文:

Overview

io.Reader is an interface so it can't be marshaled. Each marshaling struct attribute must implement Marshaler interface to be marshaled. You could declare your own marshaler wrapper struct to marshal data from bytes.Reader.

Why interface can't be marshaled?

Interfaces in Go provide a way to specify the behavior of an object: if something can do this, then it can be used here. In opposite Go’s structs are typed collections of fields. They’re useful for grouping data together to form records. Go supports methods defined on struct types not interface types.

Implementation

type Response struct {
    Body *MarshalableReader
}

type MarshalableReader struct {
    *bytes.Reader
}

func (r MarshalableReader) MarshalJSON() ([]byte, error) {
    data, err := ioutil.ReadAll(r.Reader)
    if err != nil {
        return nil, err
    }
    return []byte(fmt.Sprintf("\"%s\"", data)), nil
}

func main() {
    resp := Response{&MarshalableReader{bytes.NewReader([]byte("Blah Blah"))}}

    marshaled, err := json.Marshal(resp)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("JSON: %s\n", marshaled)
}                                                                                               

答案3

得分: 0

我正在使用"encoding/json"包:https://golang.org/pkg/encoding/json/

我可以使用http.ResponseWriter发送JSON响应。以下是两个函数,您可以使用它们来发送JSON和从请求体中读取JSON:

// GetJSONContent返回请求的JSON内容
func GetJSONContent(v interface{}, r *http.Request) error {
    defer r.Body.Close()
    return json.NewDecoder(r.Body).Decode(v)
}

// JSONWithHTTPCode带有HTTP状态码的JSON输出
func JSONWithHTTPCode(w http.ResponseWriter, d interface{}, code int) {
    w.Header().Set("Content-Type", "application/json; charset=UTF-8")
    w.WriteHeader(code)
    if d != nil {
        err := json.NewEncoder(w).Encode(d)
        if err != nil {
            panic(err)
        }
    }
}

然后在您的处理程序中,只需按如下方式使用这些函数:

// 处理程序
func Handler(w http.ResponseWriter, r *http.Request) {
    s := YourStruct{}
    err = GetJSONContent(s, r) 
    if err != nil {
        panic(err)
    }
    return
}

JSONWithHTTPCode(w, s, http.StatusOK)

希望对您有所帮助。

英文:

I am using "encoding/json" package : https://golang.org/pkg/encoding/json/

As I can use the http.ResponseWriter to send JSON Response. Here is two functions you could use to send JSON and read JSON from the body :

// GetJSONContent returns the JSON content of a request
func GetJSONContent(v interface{}, r *http.Request) error {
	defer r.Body.Close()
	return json.NewDecoder(r.Body).Decode(v)
}

// JSONWithHTTPCode Json Output with an HTTP code
func JSONWithHTTPCode(w http.ResponseWriter, d interface{}, code int) {
	w.Header().Set("Content-Type", "application/json; charset=UTF-8")
	w.WriteHeader(code)
	if d != nil {
		err := json.NewEncoder(w).Encode(d)
		if err != nil {
			panic(err)
		}
	}
}

And then in your handlers just use those func as follow :

// Handler handler
func Handler(w http.ResponseWriter, r *http.Request) {
	    s := YourStruct{}
        err = GetJSONContent(s, r) 
        if err != nil {
            panic(err)
        }
		return
	}

	JSONWithHTTPCode(w, s, http.StatusOK)
}

Hope it helped

huangapple
  • 本文由 发表于 2016年12月30日 17:13:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/41393573.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定