如何在Golang中将jsonb编码为http响应的一部分

I'm having a tough time moving json over the wire.

There is a jsonb field in a Postgres db that I need to add to a struct before sending it over the wire as an http response.

If the struct's payload field is a string, marshaling escapes the json like "{\"id\": \"3aa5fff0-ad91-41b1-84f0-d97f38e0e0f4\", \"user\": 1 }.

If the struct's payload field is a json.RawMessage, marshalling escapes the json as (what I imagine to be) a sequence of base64 encoded bytes.

This is the struct that I am marshaling and writing to the http response stream:

type NestJobReturn struct { Status string `json:"status"` Nest json.RawMessage `json:"nest"` }

I build a ret instance of this struct and print it out. If I use %v it shows bytes, and %s shows it as the proper, un-escaped json string:

log("Value of ret.Nest: %v", ret.Nest) // Value of ret.Nest: [123 34 105 ... log("Value of ret.Nest as a string: %s", ret.Nest) // Value of ret.Nest as a string: {"id": "f053...

Marshaling and i/o is done thusly:

js, _ := json.Marshal(ret) res.Header().Set("Content-Type", "application/json") res.Write(js)

The client currently receives the entire message looking kinda like this:

{"status":"ok","nest":"eyJpZCI6ICJmMD..."}

... but the intended value of "nest" is the valid json from my jsonb column in the database.

Any ideas?

You need to define the nested field as a pointer to json.RawMessage, e.g.

type NestJobReturn struct {
    Status string           `json:"status"`
    Nest   *json.RawMessage `json:"nest"`
}

jsonStr := `{"type": "Object", "desc": "Simple nested object"}`
raw := json.RawMessage(jsonStr)

ret := NestJobReturn {
    Status: "DONE",
    Nest:   &raw,
}

A working example https://play.golang.org/p/Ju7kgbawss

You need to marshal the pointer to your ret instead of the ret itself

simply change
js, _ := json.Marshal(ret) to
js, _ := json.Marshal(&ret)

and it should start working.
playground link

Better yet, you can make your struct idiot proof by either going with I Putu Susila's answer or replacing json.RawMessage with this very slightly tweaked version

type RawMessage []byte

// MarshalJSON returns m as the JSON encoding of m.
func (m RawMessage) MarshalJSON() ([]byte, error) {
    return m, nil
}

// UnmarshalJSON sets *m to a copy of data.
func (m *RawMessage) UnmarshalJSON(data []byte) error {
    if m == nil {
        return errors.New("json.RawMessage: UnmarshalJSON on nil pointer")
    }
    *m = append((*m)[0:0], data...)
    return nil
}

Then your struct will always Marshal itself correctly.
playground link

This is the fix that was suggested in the github issue for this quirk (link courtesy of I Putu Susila's comment), but the consensus is that even though this is how RawMessage ought to behave, they can't change it now due to the go standard library's compatibility guarantee. Fortunately, just because they couldn't fix it in the standard library doesn't mean you can't fix it in your own codebase.