Golang解析JSON时遇到无效字符'I':如何解决对象键字符串开头错误

Golang解析JSON时遇到无效字符’I’:如何解决对象键字符串开头错误 我有以下代码:

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

它接收到的JSON类似于:

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

但我得到的输出是:

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

服务器API是:

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

我不认为服务器代码有错误,但我还是把它分享出来,以防需要。


更多关于Golang解析JSON时遇到无效字符'I':如何解决对象键字符串开头错误的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

嵌套JSON中的键必须被引号括起来

我知道,但在上面的代码中如何实现这一点

更多关于Golang解析JSON时遇到无效字符'I':如何解决对象键字符串开头错误的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


hyousef:

"{ID:\"1\", Name:\"bob\"}"

嵌套 JSON 中的键必须用引号括起来。

skillian:

你必须使用 json.Marshalu 序列化为 JSON,这样才能将其反序列化为 JSON。

让我检查一下。

hyousef:

fmt.Printf("%#v\n", u) // =&gt; main.user{ID:"1", Name:"bob"} // 包含字段名和结构体类型
fmt.Printf("%+v\n", u) // =&gt; {ID:1 Name:bob} // 包含字段名,但不包含结构体类型
fmt.Printf("%v\n", u)  // =&gt; {1 bob}

这些字符串表示形式都不是有效的 JSON。你必须使用 json.Marshalu 序列化为 JSON,以便它可以被反序列化为 JSON。

问题出在服务器返回的JSON格式上。服务器在Get方法中使用了不正确的字符串格式化来生成MsgDeatial字段的值,导致返回的JSON中details字段的值是一个字符串,而不是有效的JSON对象。

服务器代码中的这一行有问题:

x := fmt.Sprintf("%v", strings.Trim(fmt.Sprintf("%#v", u), fmt.Sprintf("%+v", reflect.TypeOf(u))))

这会生成类似{ID:"1", Name:"bob"}的字符串,但这不是有效的JSON。有效的JSON要求对象键必须用双引号括起来。

修复服务器代码,正确序列化用户对象:

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

	// 正确序列化用户对象
	userJSON, err := json.Marshal(u)
	if err != nil {
		internalServerError(w, r)
		return
	}

	myJsonString := response{
		MsgType:    "Success",
		MsgDeatial: string(userJSON),
	}
	jsonBytes, err := json.Marshal(myJsonString)
	if err != nil {
		internalServerError(w, r)
		return
	}
	w.Write(jsonBytes)
}

这样服务器返回的JSON会是:

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

然后在客户端代码中,你需要先解析外层的response,再解析内层的user JSON字符串:

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
	}

	var u user
	// 直接解析details字段中的JSON字符串
	err := json.Unmarshal([]byte(r.MsgDeatial), &u)
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	fmt.Println(u.ID)   // 输出: 1
	fmt.Println(u.Name) // 输出: bob
}

或者更好的做法是修改response结构体,让details字段可以存储任意JSON:

type response struct {
	MsgType    string          `json:"type"`
	MsgDetails json.RawMessage `json:"details"`
}

然后在服务器端:

myJsonString := response{
	MsgType:    "Success",
	MsgDetails: userJSON, // 直接使用[]byte
}

客户端:

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

var u user
// 直接使用MsgDetails字段,它已经是JSON格式
err := json.Unmarshal(r.MsgDetails, &u)
if err != nil {
	fmt.Println(err.Error())
	return
}
回到顶部