Removing escape characters after Marshalling - json

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

Related

json: cannot unmarshal object into Go value of type []*main.Config

I'm new to golang and json, we are using gorilla mux library and I'd like to do a post request in postman. In config struct entries needs to be a map like that and in post server I need to have an array of *Config in postServer struct. I have 3 go files.
Service.go file is this:
package main
import (
"errors"
"github.com/gorilla/mux"
"mime"
"net/http"
)
type Config struct {
Id string `json:"id"`
entries map[string]string `json:"entries"`
}
type postServer struct {
data map[string][]*Config
}
func (ts *postServer) createPostHandler(w http.ResponseWriter, req *http.Request) {
contentType := req.Header.Get("Content-Type")
mediatype, _, err := mime.ParseMediaType(contentType)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if mediatype != "application/json" {
err := errors.New("Expect application/json Content-Type")
http.Error(w, err.Error(), http.StatusUnsupportedMediaType)
return
}
rt, err := decodeBody(req.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
id := createId()
ts.data[id] = rt
renderJSON(w, rt)
}
func (ts *postServer) getAllHandler(w http.ResponseWriter, req *http.Request) {
allTasks := []*Config{}
for _, v := range ts.data {
allTasks = append(allTasks, v...)
}
renderJSON(w, allTasks)
}
func (ts *postServer) getPostHandler(w http.ResponseWriter, req *http.Request) {
id := mux.Vars(req)["id"]
task, ok := ts.data[id]
if !ok {
err := errors.New("key not found")
http.Error(w, err.Error(), http.StatusNotFound)
return
}
renderJSON(w, task)
}
func (ts *postServer) delPostHandler(w http.ResponseWriter, req *http.Request) {
id := mux.Vars(req)["id"]
if v, ok := ts.data[id]; ok {
delete(ts.data, id)
renderJSON(w, v)
} else {
err := errors.New("key not found")
http.Error(w, err.Error(), http.StatusNotFound)
}
}
I wanted to test createPostHandler.
Then I have helper.go file where I decoded json into go and rendered into json:
package main
import (
"encoding/json"
"github.com/google/uuid"
"io"
"net/http"
)
func decodeBody(r io.Reader) ([]*Config, error) {
dec := json.NewDecoder(r)
dec.DisallowUnknownFields()
var rt []*Config
if err := dec.Decode(&rt); err != nil {
return nil, err
}
return rt, nil
}
func renderJSON(w http.ResponseWriter, v interface{}) {
js, err := json.Marshal(v)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(js)
}
func createId() string {
return uuid.New().String()
}
and the last one go file is main.go where I have this:
package main
import (
"context"
"github.com/gorilla/mux"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func main() {
quit := make(chan os.Signal)
signal.Notify(quit, os.Interrupt, syscall.SIGTERM)
router := mux.NewRouter()
router.StrictSlash(true)
server := postServer{
data: map[string][]*Config{},
}
router.HandleFunc("/config/", server.createPostHandler).Methods("POST")
router.HandleFunc("/configs/", server.getAllHandler).Methods("GET")
router.HandleFunc("/config/{id}/", server.getPostHandler).Methods("GET")
router.HandleFunc("/config/{id}/", server.delPostHandler).Methods("DELETE")
// start server
srv := &http.Server{Addr: "0.0.0.0:8000", Handler: router}
go func() {
log.Println("server starting")
if err := srv.ListenAndServe(); err != nil {
if err != http.ErrServerClosed {
log.Fatal(err)
}
}
}()
<-quit
log.Println("service shutting down ...")
// gracefully stop server
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(ctx); err != nil {
log.Fatal(err)
}
log.Println("server stopped")
}
And JSON whad I did send is this:
{
"entries":["hello", "world"]
}
And error what I'm getting in postman is this:
json: cannot unmarshal object into Go value of type []*main.Config
I don't know what is a problem, maybe I'm sending wrong json or I just did something wrong in decodeBody, I needed to add [] in decodeBody in var rt []*Config because it wouldn't work otherwise.
Can someone help me to fix this please?
This is an example of how you can define a struct Config that you can parse your sample JSON into.
EDIT: field entries changed to map.
You can play with it on Playground.
package main
import (
"encoding/json"
"fmt"
)
type Config struct {
Id string `json:"id"`
Entries map[string]string `json:"entries"`
}
func main() {
str := `[{"id":"42", "entries":{"hello": "world"}}]`
var tmp []Config
err := json.Unmarshal([]byte(str), &tmp)
if err != nil {
fmt.Printf("error: %v", err)
}
var rt []*Config
for _, c := range tmp {
rt = append(rt, &c)
}
for _, c := range rt {
for k, v := range c.Entries {
fmt.Printf("id=%s key=%s value=%s\n", c.Id, k, v)
}
}
}

Can't generate scalar JSON in gqlgen

I have a GQL scheme:
extend type MyType #key(fields: "id") {
id: ID! #external
properties: JSON #external
myField: String! #requires(fields: "properties")
}
scalar JSON
In graph/model/model.go:
package model
import (
"encoding/json"
"fmt"
"io"
"strconv"
"strings"
)
type JSON map[string]interface{}
// UnmarshalGQL implements the graphql.Unmarshaler interface
func (b *JSON) UnmarshalGQL(v interface{}) error {
*b = make(map[string]interface{})
byteData, err := json.Marshal(v)
if err != nil {
panic("FAIL WHILE MARSHAL SCHEME")
}
tmp := make(map[string]interface{})
err = json.Unmarshal(byteData, &tmp)
if err != nil {
panic("FAIL WHILE UNMARSHAL SCHEME")
//return fmt.Errorf("%v", err)
}
*b = tmp
return nil
}
// MarshalGQL implements the graphql.Marshaler interface
func (b JSON) MarshalGQL(w io.Writer) {
byteData, err := json.Marshal(b)
if err != nil {
panic("FAIL WHILE MARSHAL SCHEME")
}
_, _ = w.Write(byteData)
}
But when I run go run github.com/99designs/gqlgen generate
error:
generating core failed: type.gotpl: template: type.gotpl:52:28: executing "type.gotpl" at <$type.Elem.GO>: nil pointer evaluating *config.TypeReference.
GOexit status 1
I just need to get map[string]interface{} which called JSON. I knew there's scalar Map, but for apollo federation that field must be called JSON.
it's should to replace MarshalGQL to MarshalJSON like:
type JSON map[string]interface{}
func MarshalJSON(b JSON) graphql.Marshaler {
return graphql.WriterFunc(func(w io.Writer) {
byteData, err := json.Marshal(b)
if err != nil {
log.Printf("FAIL WHILE MARSHAL JSON %v\n", string(byteData))
}
_, err = w.Write(byteData)
if err != nil {
log.Printf("FAIL WHILE WRITE DATA %v\n", string(byteData))
}
})
}
func UnmarshalJSON(v interface{}) (JSON, error) {
byteData, err := json.Marshal(v)
if err != nil {
return JSON{}, fmt.Errorf("FAIL WHILE MARSHAL SCHEME")
}
tmp := make(map[string]interface{})
err = json.Unmarshal(byteData, &tmp)
if err != nil {
return JSON{}, fmt.Errorf("FAIL WHILE UNMARSHAL SCHEME")
}
return tmp, nil
}

Golang - Cannot access map in []interface{}

I have used json.Unmarshal and extracted json content. I then managed to get one layer deeper into the []interface{} by using the following code:
response, err := http.Get("http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=2B2A0C37AC20B5DC2234E579A2ABB11C&steamids=76561198132612090")
content, err := ioutil.ReadAll(response.Body)
defer response.Body.Close()
if err != nil {
panic(0)
}
var decoded map[string]interface{}
if err := json.Unmarshal(content, &decoded); err != nil {
panic(0)
}
players := decoded["response"].(map[string]interface{})["players"]
if err != nil {
panic(0)
}
Variable players' type is []interface {} and content is [map[personaname:Acidic]].
How do I access this map? I've tried players["personaname"] but that doesn't seem to work. Any ideas?
Defining a struct type with the expected schema will make your life easier when you want to get the data from it:
package main
import "fmt"
//import "net/http"
//import "io/ioutil"
import "encoding/json"
// you don't need to define everything, only what you need
type Player struct {
Steamid string
Communityvisibilitystate int
Personaname string
Lastlogoff int64 // time.Unix(Lastlogoff, 0)
Profileurl string
Avatar string
Avatarmedium string
Avatarfull string
Personastate int
Realname string
Primaryclanid string
Timecreated int64 // time.Unix(Timecreated, 0)
Personastateflags int
//Loccountrycode string // e.g. if you don't need this
}
func main() {
/*response, err := http.Get("http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=2B2A0C37AC20B5DC2234E579A2ABB11C&steamids=76561198132612090")
if err != nil {
panic(err)
}
content, err := ioutil.ReadAll(response.Body)
defer response.Body.Close()
if err != nil {
panic(0)
}*/
content := []byte(`{
"response": {
"players": [
{
"steamid": "76561198132612090",
"communityvisibilitystate": 3,
"profilestate": 1,
"personaname": "Acidic",
"lastlogoff": 1459489924,
"profileurl": "http://steamcommunity.com/id/ari9/",
"avatar": "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/bc/bc50a4065c31c606e51dfad329341b2d1f1ac4d3.jpg",
"avatarmedium": "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/bc/bc50a4065c31c606e51dfad329341b2d1f1ac4d3_medium.jpg",
"avatarfull": "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/bc/bc50a4065c31c606e51dfad329341b2d1f1ac4d3_full.jpg",
"personastate": 3,
"realname": "Ari Seyhun",
"primaryclanid": "103582791440552060",
"timecreated": 1397199406,
"personastateflags": 0,
"loccountrycode": "TR"
}
]
}
}`)
var decoded struct {
Response struct {
Players []Player
}
}
if err := json.Unmarshal(content, &decoded); err != nil {
panic(err)
}
fmt.Printf("%#v\n", decoded.Response.Players)
}
http://play.golang.org/p/gVPRwLFunF
You can also create a new named type from time.Time for Timecreated and Lastlogoff with its own UnmarshalJSON function, and immediately convert it to time.Time using time.Unix()
Players is a JSON array. Thus you have to convert it to a slice of interface.
Then you can access any element of the slice and casting it to a map[string]interface{} type.
Here's the working example
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
response, err := http.Get("http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=2B2A0C37AC20B5DC2234E579A2ABB11C&steamids=76561198132612090")
content, err := ioutil.ReadAll(response.Body)
defer response.Body.Close()
if err != nil {
panic(0)
}
var decoded map[string]interface{}
if err := json.Unmarshal(content, &decoded); err != nil {
panic(0)
}
players := decoded["response"].(map[string]interface{})["players"]
if err != nil {
panic(0)
}
sliceOfPlayers := players.([]interface{})
fmt.Println((sliceOfPlayers[0].(map[string]interface{}))["personaname"])
}

Marshalling json.RawMessage returns base64 encoded string

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

Golang Converting JSON

map[key:2073933158088]
I need to grab the key out of this data structure as a string, but I can't seem to figure out how!
Help with this overly simple question very much appreciated.
The value above is encapsulated in the variable named data.
I have tried: data.key, data[key], data["key"], data[0] and none of these seem to be appropriate calls.
To define data I sent up a JSON packet to a queue on IronMQ. I then pulled the message from the queue and manipulated it like this:
payloadIndex := 0
for index, arg := range(os.Args) {
if arg == "-payload" {
payloadIndex = index + 1
}
}
if payloadIndex >= len(os.Args) {
panic("No payload value.")
}
payload := os.Args[payloadIndex]
var data interface{}
raw, err := ioutil.ReadFile(payload)
if err != nil {
panic(err.Error())
}
err = json.Unmarshal(raw, &data)
Design your data type to match json structure. This is how can you achieve this:
package main
import (
"fmt"
"encoding/json"
)
type Data struct {
Key string `json:"key"`
}
func main() {
data := new(Data)
text := `{ "key": "2073933158088" }`
raw := []byte(text)
err := json.Unmarshal(raw, data)
if err != nil {
panic(err.Error())
}
fmt.Println(data.Key)
}
Since the number in the json is unquoted, it's not a string, Go will panic if you try to just handle it as a string (playground: http://play.golang.org/p/i-NUwchJc1).
Here's a working alternative:
package main
import (
"fmt"
"encoding/json"
"strconv"
)
type Data struct {
Key string `json:"key"`
}
func (d *Data) UnmarshalJSON(content []byte) error {
var m map[string]interface{}
err := json.Unmarshal(content, &m)
if err != nil {
return err
}
d.Key = strconv.FormatFloat(m["key"].(float64), 'f', -1, 64)
return nil
}
func main() {
data := new(Data)
text := `{"key":2073933158088}`
raw := []byte(text)
err := json.Unmarshal(raw, data)
if err != nil {
panic(err.Error())
}
fmt.Println(data.Key)
}
You can see the result in the playground: http://play.golang.org/p/5hU3hdV3kK