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
嵌套JSON中的键必须被引号括起来
我知道,但在上面的代码中如何实现这一点
更多关于Golang解析JSON时遇到无效字符'I':如何解决对象键字符串开头错误的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
hyousef:
"{ID:\"1\", Name:\"bob\"}"
嵌套 JSON 中的键必须用引号括起来。
skillian:
你必须使用
json.Marshal将u序列化为 JSON,这样才能将其反序列化为 JSON。
让我检查一下。
hyousef:
fmt.Printf("%#v\n", u) // => main.user{ID:"1", Name:"bob"} // 包含字段名和结构体类型 fmt.Printf("%+v\n", u) // => {ID:1 Name:bob} // 包含字段名,但不包含结构体类型 fmt.Printf("%v\n", u) // => {1 bob}
这些字符串表示形式都不是有效的 JSON。你必须使用 json.Marshal 将 u 序列化为 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
}

