Marshalling json.RawMessage returns base64 encoded string - json

I run the following code:
package main
import (
"encoding/json"
"fmt"
)
func main() {
raw := json.RawMessage(`{"foo":"bar"}`)
j, err := json.Marshal(raw)
if err != nil {
panic(err)
}
fmt.Println(string(j))
}
Playground: http://play.golang.org/p/qbkEIZRTPQ
Output:
"eyJmb28iOiJiYXIifQ=="
Desired output:
{"foo":"bar"}
Why does it base64 encode my RawMessage as if it was an ordinary []byte?
After all, RawMessage's implementation of MarshalJSON is just returning the byte slice
// MarshalJSON returns *m as the JSON encoding of m.
func (m *RawMessage) MarshalJSON() ([]byte, error) {
return *m, nil
}

Found the answer in a go-nuts thread
The value passed to json.Marshal must be a pointer for json.RawMessage to work properly:
package main
import (
"encoding/json"
"fmt"
)
func main() {
raw := json.RawMessage(`{"foo":"bar"}`)
j, err := json.Marshal(&raw)
if err != nil {
panic(err)
}
fmt.Println(string(j))
}

Related

How to convert a time to UTC before marshaling as JSON in Go?

I'm trying to define a Time struct which implements the Marshaler interface such that, when it is marshaled to JSON, it is represented in the format YYYY-mm-ddTHH:MM:SSZ, that is, the time is converted to UTC and rounded to the nearest second. I've tried the following program:
package main
import (
"encoding/json"
"fmt"
"log"
"time"
)
type Time struct {
time.Time
}
func (t *Time) MarshalJSON() ([]byte, error) {
return []byte(t.Time.UTC().Round(time.Second).Format(time.RFC3339)), nil
}
func main() {
tm := time.Now()
// tm := time.Now().UTC().Round(time.Second)
tmJSON, err := json.Marshal(tm)
if err != nil {
log.Fatalf("marshal time: %v", err)
}
fmt.Println(string(tmJSON))
}
When I run this, however, it prints
> go run main.go
"2022-12-07T16:32:51.494597-08:00"
If, by contrast, I pass in time.Now().UTC().Round(time.Second) as the input to be marshaled (i.e., use the commented-out line in the snippet above), I get the desired output:
> go run main.go
"2022-12-08T00:41:10Z"
My question is: why can't I perform the conversion to UTC and rounding to the nearest second in the MarshalJSON method itself?
what are you trying to do?
I tried running your MarshalJSON function and it works as expected
here is what I tried to do:
package main
import (
"encoding/json"
"fmt"
"log"
"time"
)
type Time struct {
time.Time
}
func (t *Time) MarshalJSON() ([]byte, error) {
return []byte(t.Time.UTC().Round(time.Second).Format(time.RFC3339)), nil
}
func main() {
// tm := time.Now().UTC()
tm := time.Now().UTC().Round(time.Second)
tmJSON, err := json.Marshal(tm)
if err != nil {
log.Fatalf("marshal time: %v", err)
}
fmt.Println(string(tmJSON))
marshal_time := Time{time.Now().UTC()}
byt_arr, _ := marshal_time.MarshalJSON()
fmt.Println(string(byt_arr))
}
and i got the following output:
"2022-12-08T04:41:59Z"
2022-12-08T04:41:59Z
The first line is your previous output and the second output is of your MarshalJSON function.
You can use AppendFormat to convert your time string into buffer.
Also in your question you are not initialising your Time struct for Marshalling.
Here is a probable solution
package main
import (
"encoding/json"
"errors"
"fmt"
"log"
"time"
)
type Time struct {
time.Time
}
func (t *Time) MarshalJSON() ([]byte, error) {
if y := t.Year(); y < 0 || y >= 10000 {
return nil, errors.New("Time.MarshalJSON: year outside of range [0,9999]")
}
b := make([]byte, 0, len(time.RFC3339)+2)
b = append(b, '"')
b = t.UTC().Round(time.Second).AppendFormat(b, time.RFC3339)
b = append(b, '"')
return b, nil
}
func main() {
now := time.Now()
mt := &Time{now}
bytArr, err := json.Marshal(mt)
if err != nil {
log.Fatalf("marshal time: %v", err)
}
fmt.Println(string(bytArr))
}

Unmarshal JSON to map[any]any

I want to unmarshal a JSON object to a map of map[any]any. This yields the error json: cannot unmarshal object into Go value of type map[interface {}]interface {}. Why is this not possible?
package main
import (
"encoding/json"
"fmt"
)
func main() {
bytes := []byte(`{"sendMsg":{"user":"ANisus","msg":"Trying to send a message"},"say":"Hello"}`)
var bytesInMap map[any]any
err := json.Unmarshal(bytes, &bytesInMap)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("Hello World %s", bytesInMap)
}

Unmarshalling to same struct but different json name

I am trying to unmarshal a particular json data, perform some data transformations and then marshal the data and send it. However I want to marshal it with different json variable name.
Can I marshal the data to another json name like have xyz instead of abc
{"abc":1}
to
{"xyz":1}
package main
import (
"fmt"
"encoding/json"
)
type SomeStruct struct {
SomeField int `json:"abc"`
}
func main(){
jsonData := []byte(`{"abc":1}`)
strct := SomeStruct{}
json.Unmarshal([]byte(jsonData), &strct)
fmt.Println(strct)
x, err := json.Marshal(strct)
if err != nil {
fmt.Println("errs", err)
}
finalValue := string(x)
fmt.Println(finalValue)
}
Define two structures, one for your input, one for your output, and convert like this:
package main
import (
"fmt"
"encoding/json"
)
type inStruct struct {
SomeField int `json:"abc"`
}
type outStruct struct {
SomeField int `json:"xyz"`
}
func main(){
jsonData := []byte(`{"abc":1}`)
strct := inStruct{}
if err := json.Unmarshal(jsonData, &strct); err != nil {
log.Fatal(err)
}
// Edit to reflect mkopriva's comment
// outStruct := &outStruct{SomeField: strct.SomeField}
outStruct := outStruct(strct)
x, err := json.Marshal(outStruct)
if err != nil {
log.Fatal(err)
}
finalValue := string(x)
fmt.Println(finalValue)
}
You can potentially implement a custom ToJSON method for the struct, as follows:
package main
import (
"encoding/json"
"fmt"
)
type SomeStruct struct {
SomeField int `json:"abc"`
}
func (s *SomeStruct) ToJSON() string {
return fmt.Sprintf("{\"xyz\":%d}", s.SomeField)
}
func main() {
jsonData := []byte(`{"abc":1}`)
strct := SomeStruct{}
json.Unmarshal([]byte(jsonData), &strct)
fmt.Println(strct)
x, err := json.Marshal(strct)
if err != nil {
fmt.Println("errs", err)
}
finalValue := string(x)
fmt.Println(finalValue)
fmt.Println("custom ToJSON", strct.ToJSON())
}
Playground link: https://play.golang.org/p/mjW0dBPN59Q
It might not be flexible in the long run though. Personally, for requirement like this, I would prefer the solution posted by #Clément

Removing escape characters after Marshalling

In the above code I am trying to convert my key to byte array via Marshal. Although I am successful doing it, when I see the Marshalled string, I am seeing \ escape character before my json keys.
Can you please help me remove that?
func GetBytes(key interface{}) ([]byte, error) {
b, err := json.Marshal(key)
if err != nil {
return nil, err
}
log.Println("%%%%%%%%%%")
log.Printf("%+v\n", key)
log.Println("##########")
log.Println(string(b));
return b, nil
}
Current Output:
%%%%%%%%%%
{"entityId":5010740929,"eventTime":1542706233458}
##########
"{\"entityId\":5010740929,\"eventTime\":1542706233458}"
I think you sending string as key in func GetBytes(key interface{}) ([]byte, error). That's why you are seeing escape characters.
package main
import (
"encoding/json"
"fmt"
"log"
)
func f(key interface{}) {
json, err := json.Marshal(key)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(json))
}
func main() {
data := map[string]interface{}{
"A": 1,
"B": 2,
}
f(data)
json, err := json.Marshal(data)
if err != nil {
log.Fatal(err)
}
f(string(json))
}
Output:
{"A":1,"B":2}
"{\"A\":1,\"B\":2}"
Go playground link: https://play.golang.org/p/iF1ckvZ9cQC
In that case, You can use Unquote to remove quote and escape character:
package main
import (
"encoding/json"
"fmt"
"log"
"strconv"
)
func f(key interface{}) {
json, err := json.Marshal(key)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(json))
st, err := strconv.Unquote(string(json))
fmt.Println(st)
}
func main() {
data := map[string]interface{}{
"A": 1,
"B": 2,
}
json, err := json.Marshal(data)
if err != nil {
log.Fatal(err)
}
f(string(json))
}
Output:
"{\"A\":1,\"B\":2}"
{"A":1,"B":2}
Go playground link: https://play.golang.org/p/aoTEQtJslTs

Cannot unmarshal object into Go value of type []uint8

I am fairly new to Go. I have the following code:
package main
import (
"encoding/json"
"fmt"
)
func main() {
byt := []byte(`{"num":6.13,"strs":["a","b"]}`)
dat := []byte(`{"num":7.13,"strs":["c","d"]}`)
if err := json.Unmarshal(byt, &dat); err != nil {
panic(err)
}
fmt.Println(dat)
}
Getting the error:
cannot "unmarshal object into Go value of type []uint8".
How can I fix this please?
You have 2 JSON inputs, and you're trying to unmarshal one into the other. That doesn't make any sense.
Model your JSON input (the object) with a type (struct), and unmarshal into that. For example:
type Obj struct {
Num float64 `json:"num"`
Strs []string `json:"strs"`
}
func main() {
byt := []byte(`{"num":6.13,"strs":["a","b"]}`)
var obj Obj
if err := json.Unmarshal(byt, &obj); err != nil {
panic(err)
}
fmt.Println(obj)
}
Output (try it on the Go Playground):
{6.13 [a b]}
I think you meant to do something like this:
package main
import (
"encoding/json"
"fmt"
)
func main() {
var dat interface{}
byt := []byte(`{"num":6.13,"strs":["a","b"]}`)
if err := json.Unmarshal(byt, &dat); err != nil {
panic(err)
}
fmt.Println(dat)
}
What you were trying to do makes no sense, since you're trying to unmarshal two JSON objects one into another.