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

时间:2016-12-30 09:13:44

标签: serialization go deserialization

我正在尝试将struct下面的序列化为byte []以将其存储到DB中,然后在从DB中读取它时我将其反序列化。

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

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

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

我正在使用json.Marshal()来序列化resp对象,如下所示。

b, _ := json.Marshal(resp)

下面是代码,我用来反序列化。

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

问题在于反序列化,我无法获得resp.Body对象。尽管设置了体对象,但它总是为零或空白(见上文)。我能够从反序列化中获取结构的Headers和status字段,而不是Body。

我知道有一些东西需要处理Body字段,它是一个io.Reader。

任何帮助都会非常棒。

3 个答案:

答案 0 :(得分:5)

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

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

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

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

<强> Implementaton 这就是你可以做的事情

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操场链接,其中包含实现和示例用法:link

答案 1 :(得分:0)

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

因为我可以使用http.ResponseWriter发送JSON响应。这里有两个函数可用于发送JSON并从正文中读取JSON:

// 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)
        }
    }
}

然后在你的处理程序中使用这些函数如下:

// 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)
}

希望有所帮助

答案 2 :(得分:0)

概述

io.Reader是一个接口,因此无法封送。每个编组结构属性必须实现要编组的Marshaler接口。您可以声明自己的封送程序包装器结构来封送来自bytes.Reader的数据。

为什么界面无法编组?

Go中的接口提供了一种指定对象行为的方法:如果有什么可以做到这一点,那么可以在这里使用它。在对面的Go中,结构是字段的类型集合。它们可用于将数据分组在一起以形成记录。 Go支持在struct类型而非接口类型上定义的方法。

实施

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)
}