Pascal
Pascal

Reputation: 2154

go un/marshal struct as json

I need to wrap json rpc messages in a go struct This was my first idea and it's working for outgoing messages like this

// Message wrapper
type Message struct {
    ID      *string      `json:"id,omitempty"`
    JSONRPC string       `json:"jsonrpc"`
    Method  *string      `json:"method,omitempty"`
    Params  *interface{} `json:"params,omitempty"`
    Result  *interface{} `json:"result,omitempty"`
}

// NewNotification creates a RPC Notification
func NewNotification(method string, params interface{}) Message {

    m := Message{}
    m.JSONRPC = "2.0"
    m.Method = &method
    m.Params = &params

    return m

}

type Test struct {
        A string `json:"a"`
        B string `json:"b"`
}

t := Test{"abc", "def"}

m := NewNotification("testMethod", t)

socket.WriteJSON(m)

But now for the receiving direction I have a problem with the Params *interface{} declaration.

I identify the Params payload type via the Method field and want to unmarshal the Params to that struct ... but therefore I need the type json.RawMessage for Params to get this working.

I do not want to define a MessageIn and MessageOut struct!

m := Message{}
socket.ReadJSON(m)

t := Test{}

json.Unmarshal(m.Params, &t)

Upvotes: 0

Views: 385

Answers (2)

Pascal
Pascal

Reputation: 2154

Ok this idea has a MessageIn and MessageOut struct but they share the common part from Message. This way it's possible to let WriteJson do the marshalling in sending direction. The receiving unmarshal must be done manually when the method and the supposed payload type is known.

// Message wrapper
type Message struct {
    ID      *string `json:"id,omitempty"`
    JSONRPC string  `json:"jsonrpc"`
    Method  *string `json:"method,omitempty"`
}

// MessageIn wrapper
type MessageIn struct {
    Message
    Params *json.RawMessage `json:"params,omitempty"`
    Result *json.RawMessage `json:"result,omitempty"`
}

// MessageOut wrapper
type MessageOut struct {
    Message
    Params *interface{} `json:"params,omitempty"`
    Result *interface{} `json:"result,omitempty"`
}

// NewNotification creates a RPC Notification
func NewNotification(method string, params interface{}) MessageOut {

    m := MessageOut{}
    m.JSONRPC = "2.0"
    m.Method = &method
    m.Params = &params

    return m

}

type Test struct {
        A string `json:"a"`
        B string `json:"b"`
}

# let WriteJSON do all marshalling 
t := Test{"abc", "def"}

m := NewNotification("testMethod", t)

socket.WriteJSON(m)

# unmarshal manually received message when method is known
m := Message{}
socket.ReadJSON(m)

t := Test{}

json.Unmarshal(m.Params, &t)

Upvotes: 0

Imaxd
Imaxd

Reputation: 366

You should use the json.RawMessage as the type for your Params and Result Fields. This will delay the decoding of the these fields until you know what is the Method, on the receiving end. Check out the documentation and the examples: it's the same use case: https://golang.org/pkg/encoding/json/#RawMessage

Upvotes: 1

Related Questions