Invalid character 'I' looking for beginning of object key string

I’ve the below code:

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
)

type user struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

type response struct {
	MsgType    string `json:"type"`
	MsgDeatial string `json:"details"`
}

func main() {

	url := "http://localhost/users/1"

	req, _ := http.NewRequest("GET", url, nil)

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()

	var r response
	if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
		fmt.Println(err)
		return
	}
        fmt.Println(r.MsgDeatial)

	var u user // u := user{}

	err := json.Unmarshal([]byte(r.MsgDeatial), &u)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	reader := strings.NewReader(r.MsgDeatial)

	if err := json.NewDecoder(reader).Decode(&u); err != nil {
		fmt.Println(err.Error())
		return
	}

	fmt.Println(u.ID)
	fmt.Println(u.Name)
}

That is recieving a json like:

{
  "type": "Success",
  "details": "{ID:\"1\", Name:\"bob\"}"
}

But the output I’m getting is:

{ID:"1", Name:"bob"}
invalid character 'I' looking for beginning of object key string
invalid character 'I' looking for beginning of object key string

The server API is:

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"reflect"
	"regexp"
	"strings"
	"sync"
)

var (
	listUserRe   = regexp.MustCompile(`^\/users[\/]*$`)
	getUserRe    = regexp.MustCompile(`^\/users\/(\d+)$`)
	createUserRe = regexp.MustCompile(`^\/users[\/]*$`)
)

type user struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

type response struct {
	MsgType    string `json:"type"`
	MsgDeatial string `json:"details"`
}

type datastore struct {
	m map[string]user
	*sync.RWMutex
}

type userHandler struct {
	store *datastore
}

func (h *userHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("content-type", "application/json")
	switch {
	case r.Method == http.MethodGet && listUserRe.MatchString(r.URL.Path):
		h.List(w, r)
		return
	case r.Method == http.MethodGet && getUserRe.MatchString(r.URL.Path):
		h.Get(w, r)
		return
	case r.Method == http.MethodPost && createUserRe.MatchString(r.URL.Path):
		h.Create(w, r)
		return
	default:
		notFound(w, r)
		return
	}
}

func (h *userHandler) List(w http.ResponseWriter, r *http.Request) {
	h.store.RLock()
	users := make([]user, 0, len(h.store.m))
	for _, v := range h.store.m {
		users = append(users, v)
	}
	h.store.RUnlock()
	jsonBytes, err := json.Marshal(users)
	if err != nil {
		internalServerError(w, r)
		return
	}
	w.WriteHeader(http.StatusOK)
	w.Write(jsonBytes)
}

func (h *userHandler) Get(w http.ResponseWriter, r *http.Request) {
	matches := getUserRe.FindStringSubmatch(r.URL.Path)
	if len(matches) < 2 {
		notFound(w, r)
		return
	}
	h.store.RLock()
	u, ok := h.store.m[matches[1]]
	h.store.RUnlock()
	if !ok {
		w.WriteHeader(http.StatusNotFound)
		//w.Write([]byte("user not found"))
		myJsonString := response{
			MsgType:    "Error",
			MsgDeatial: "user not found",
		}
		jsonBytes, err := json.Marshal(myJsonString)
		if err != nil {
			internalServerError(w, r)
			return
		}
		w.Write(jsonBytes)
		return
	}

	w.WriteHeader(http.StatusOK)

	fmt.Printf("%#v\n", u) // => main.user{ID:"1", Name:"bob"} // include the field names, and the struct type
	fmt.Printf("%+v\n", u) // => {ID:1 Name:bob} // include the field names, but not the struct type
	fmt.Printf("%v\n", u)  // => {1 bob}

	x := fmt.Sprintf("%v", strings.Trim(fmt.Sprintf("%#v", u), fmt.Sprintf("%+v", reflect.TypeOf(u))))
	myJsonString := response{
		MsgType:    "Success",
		MsgDeatial: x,
	}
	jsonBytes, err := json.Marshal(myJsonString)
	if err != nil {
		internalServerError(w, r)
		return
	}
	w.Write(jsonBytes)
}

func (h *userHandler) Create(w http.ResponseWriter, r *http.Request) {
	var u user
	if err := json.NewDecoder(r.Body).Decode(&u); err != nil {
		internalServerError(w, r)
		return
	}
	h.store.Lock()
	h.store.m[u.ID] = u
	h.store.Unlock()

	myJsonString := response{
		MsgType:    "Success",
		MsgDeatial: "User " + u.Name + " had been added",
	}

	jsonBytes, err := json.Marshal(myJsonString)
	if err != nil {
		internalServerError(w, r)
		return
	}

	w.Write(jsonBytes)

}

func internalServerError(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusInternalServerError)
	w.Write([]byte("internal server error"))
}

func notFound(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusNotFound)
	w.Write([]byte("not found"))
}

func main() {
	mux := http.NewServeMux()
	userH := &userHandler{
		store: &datastore{
			m: map[string]user{
				"1": {ID: "1", Name: "bob"},
			},
			RWMutex: &sync.RWMutex{},
		},
	}
	mux.Handle("/users", userH)
	mux.Handle("/users/", userH)

	http.ListenAndServe("localhost:80", mux)
}

I do not think there is an error at the server code but I’m sharing it in case it was required.

None of these string representations are valid JSON. You have to use json.Marshal to marshal u as JSON so that it can be deserialized as JSON.

The keys in the nested JSON must be quoted

I know, but how to get it done in the code above

let me check it

Thanks for the replies recieved, I solved it using:

  1. any
  2. unmarshal

Now the server code is:

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"regexp"
	"sync"
)

var (
	listUserRe   = regexp.MustCompile(`^\/users[\/]*$`)
	getUserRe    = regexp.MustCompile(`^\/users\/(\d+)$`)
	createUserRe = regexp.MustCompile(`^\/users[\/]*$`)
)

type user struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

type response struct {
	MsgType    string `json:"type"`
	MsgDeatial any    `json:"details"`
}

type datastore struct {
	m map[string]user
	*sync.RWMutex
}

type userHandler struct {
	store *datastore
}

func (h *userHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("content-type", "application/json")
	switch {
	case r.Method == http.MethodGet && listUserRe.MatchString(r.URL.Path):
		h.List(w, r)
		return
	case r.Method == http.MethodGet && getUserRe.MatchString(r.URL.Path):
		h.Get(w, r)
		return
	case r.Method == http.MethodPost && createUserRe.MatchString(r.URL.Path):
		h.Create(w, r)
		return
	default:
		notFound(w, r)
		return
	}
}

func (h *userHandler) List(w http.ResponseWriter, r *http.Request) {
	h.store.RLock()
	users := make([]user, 0, len(h.store.m))
	for _, v := range h.store.m {
		users = append(users, v)
	}
	h.store.RUnlock()
	jsonBytes, err := json.Marshal(users)
	if err != nil {
		internalServerError(w, r)
		return
	}
	w.WriteHeader(http.StatusOK)
	w.Write(jsonBytes)
}

func (h *userHandler) Get(w http.ResponseWriter, r *http.Request) {
	matches := getUserRe.FindStringSubmatch(r.URL.Path)
	if len(matches) < 2 {
		notFound(w, r)
		return
	}
	h.store.RLock()
	u, ok := h.store.m[matches[1]]
	h.store.RUnlock()
	if !ok {
		w.WriteHeader(http.StatusNotFound)
		//w.Write([]byte("user not found"))
		myJsonString := response{
			MsgType:    "Error",
			MsgDeatial: "user not found",
		}
		jsonBytes, err := json.Marshal(myJsonString)
		if err != nil {
			internalServerError(w, r)
			return
		}
		w.Write(jsonBytes)
		return
	}
	/*	jsonBytes, err := json.Marshal(u)
		if err != nil {
			internalServerError(w, r)
			return
		} */
	w.WriteHeader(http.StatusOK)
	//w.Write(jsonBytes)
	//fmt.Printf("%#v\n", u) // => main.user{ID:"1", Name:"bob"} // include the field names, and the struct type
	//x := fmt.Sprintf("%+v\n", u) // => {ID:1 Name:bob} // include the field names, but not the struct type
	//x := fmt.Sprintf("%v\n", u) // => {1 bob}

	//x := fmt.Sprintf("%v", strings.Trim(fmt.Sprintf("%#v", u), fmt.Sprintf("%+v", reflect.TypeOf(u))))
	//println(x)
	myJsonString := response{
		MsgType:    "Success",
		MsgDeatial: u,
	}
	jsonBytes, err := json.Marshal(myJsonString)
	if err != nil {
		internalServerError(w, r)
		return
	}
	fmt.Printf("%#v\n", myJsonString)
	w.Write(jsonBytes)
}

func (h *userHandler) Create(w http.ResponseWriter, r *http.Request) {
	var u user
	if err := json.NewDecoder(r.Body).Decode(&u); err != nil {
		internalServerError(w, r)
		return
	}
	h.store.Lock()
	h.store.m[u.ID] = u
	h.store.Unlock()

	myJsonString := response{
		MsgType:    "Success",
		MsgDeatial: "User " + u.Name + " had been added",
	}

	jsonBytes, err := json.Marshal(myJsonString)
	if err != nil {
		internalServerError(w, r)
		return
	}

	w.Write(jsonBytes)

}

func internalServerError(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusInternalServerError)
	w.Write([]byte("internal server error"))
}

func notFound(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusNotFound)
	w.Write([]byte("not found"))
}

func main() {
	mux := http.NewServeMux()
	userH := &userHandler{
		store: &datastore{
			m: map[string]user{
				"1": {ID: "1", Name: "bob"},
			},
			RWMutex: &sync.RWMutex{},
		},
	}
	mux.Handle("/users", userH)
	mux.Handle("/users/", userH)

	http.ListenAndServe("localhost:80", mux)
}

The returned response is:

{
  "type": "Success",
  "details": {
    "id": "1",
    "name": "bob"
  }
}

And the client side that is recieving the above JSON is:

package main

import (
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
)

// type response map[string]any
type user struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

type response struct {
	MsgType    string `json:"type"`
	MsgDeatial any    `json:"details"`
}

func main() {

	url := "http://localhost/users/1"

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		log.Fatalln(err)
	}

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatalln(err)
	}

	defer res.Body.Close()

	var r response
	b, err := io.ReadAll(res.Body)
	// b, err := ioutil.ReadAll(resp.Body)  Go.1.15 and earlier
	if err != nil {
		log.Fatalln(err)
	}

	fmt.Println(string(b))
	if err := json.Unmarshal([]byte(string(b)), &r); err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(r.MsgType)
	fmt.Println(r.MsgDeatial)

	var u user // u := user{}
	t, err := json.Marshal(r.MsgDeatial)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(t))
	if err := json.Unmarshal([]byte(string(t)), &u); err != nil {
		fmt.Println(err)
		return
	}

	fmt.Println(u.ID)
	fmt.Println(u.Name)
}

And the output at the console is:

{"type":"Success","details":{"id":"1","name":"bob"}}
Success
map[id:1 name:bob]
{"id":"1","name":"bob"}
1
bob