
本文解析 Go 中 json: cannot unmarshal string into Go value of type main.test_struct 错误的根源——前端以 application/x-www-form-urlencoded 方式提交表单数据,而非 JSON 格式,导致 Go 的 json.Decoder 尝试将 URL 编码字符串解析为结构体时失败。
本文解析 go 中 `json: cannot unmarshal string into go value of type main.test_struct` 错误的根源——前端以 `application/x-www-form-urlencoded` 方式提交表单数据,而非 json 格式,导致 go 的 `json.decoder` 尝试将 url 编码字符串解析为结构体时失败。
该错误并非 Go 结构体定义或 JSON 标签问题,而是数据传输格式不匹配所致。从您提供的前端代码可见,jQuery 的 $.ajax() 在 data 传入对象(如 {long: long, lat: lat})且未显式指定 contentType 时,会自动将其序列化为 application/x-www-form-urlencoded 格式的键值对(例如 lat=48.892423&lng=2.215331),而非 JSON 字符串。而您的 Go 后端却使用 json.NewDecoder(r.Body).Decode(&t) 强制按 JSON 协议解析——这相当于用 JSON 解析器去读取一段 URL 编码文本,自然触发 cannot unmarshal string into Go value of type ... 错误(底层实际是尝试把 "lat=48.89..." 这个字符串整体赋给 test_struct 类型变量,类型严重不匹配)。
✅ 正确做法:前后端格式必须一致
方案一(推荐):前端发送标准 JSON,后端保持 JSON 解析
修改 JavaScript,显式设置 contentType: "application/json" 并手动序列化数据:
// 前端:发送 JSON 格式
var params = { lat: lat, lng: lng, acc: 1962 }; // 注意字段名需与 Go struct tag 一致
$.ajax({
type: 'POST',
url: 'http://localhost:9280/post_geo/',
contentType: 'application/json', // 关键:声明内容类型
data: JSON.stringify(params), // 关键:序列化为 JSON 字符串
success: function(data2) {
console.log(data2);
}
});Go 后端无需修改(但建议补充错误处理与 r.Body.Close()):
func postGeo(w http.ResponseWriter, r *http.Request) {
var t struct {
Lat float32 `json:"lat"`
Lng float32 `json:"lng"`
Acc int `json:"acc"`
}
if err := json.NewDecoder(r.Body).Decode(&t); err != nil {
http.Error(w, "Invalid JSON: "+err.Error(), http.StatusBadRequest)
return
}
defer r.Body.Close() // 必须关闭 Body
Info.Printf("Received: %+v", t)
w.Header().Set("Access-Control-Allow-Origin", "*")
fmt.Fprintf(w, "OK")
}方案二:后端适配表单数据(兼容旧逻辑)
若无法修改前端,可改用 r.ParseForm() 解析 x-www-form-urlencoded 数据:
func postGeo(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
http.Error(w, "Parse form failed", http.StatusBadRequest)
return
}
lat, _ := strconv.ParseFloat(r.FormValue("lat"), 32)
lng, _ := strconv.ParseFloat(r.FormValue("lng"), 32)
acc, _ := strconv.Atoi(r.FormValue("acc"))
t := struct {
Lat float32
Lng float32
Acc int
}{
Lat: float32(lat),
Lng: float32(lng),
Acc: acc,
}
Info.Printf("Parsed form: %+v", t)
w.Header().Set("Access-Control-Allow-Origin", "*")
fmt.Fprintf(w, "OK")
}⚠️ 注意事项
-
永远检查
r.Body.Close():即使使用json.Decoder,也应在defer或显式位置关闭r.Body,避免连接泄漏。 -
禁止在解析前多次读取
r.Body:r.Body是单次读取流,ioutil.ReadAll(r.Body)后再调用json.Decode()会读到空内容。 -
跨域请求(CORS)与 JSONP 冲突:您当前前端同时设置了
crossDomain: true和dataType: 'jsonp',但 JSONP 仅支持 GET 请求,无法用于 POST;且后端未提供 JSONP 回调包装,会导致请求失败。建议统一使用 CORS(服务端设置Access-Control-Allow-Origin等头)替代 JSONP。
通过确保“前端发送什么格式,后端就用对应方式解析”,即可彻底解决该类反序列化错误。


















