
Go 中 json: cannot unmarshal string into Go value of type main.test_struct 错误,通常是因为 HTTP 请求体实际是 URL 编码表单(application/x-www-form-urlencoded),而非 JSON 字符串,导致 json.Decoder 尝试将形如 lat=48.89&lng=2.21 的字符串解析为结构体而失败。
go 中 `json: cannot unmarshal string into go value of type main.test_struct` 错误,通常是因为 http 请求体实际是 url 编码表单(`application/x-www-form-urlencoded`),而非 json 字符串,导致 `json.decoder` 尝试将形如 `lat=48.89&lng=2.21` 的字符串解析为结构体而失败。
该错误的根本原因并非结构体定义或 JSON 格式本身有问题,而是客户端发送的数据格式与服务端期望的解析方式不匹配。
从你提供的前端代码可见:
var params = {long: long, lat: lat};
$.ajax({
type: 'POST',
url: url_bis,
data: params, // ← 关键:jQuery 默认将对象转为 x-www-form-urlencoded
dataType: 'jsonp',
// ...
});jQuery 的 data: {lat: ..., long: ...} 会自动编码为 lat=48.892423&long=2.215331(注意字段名是 long 而非 lng,且无 acc),并以 Content-Type: application/x-www-form-urlencoded 发送。而你的 Go 服务端却直接用 json.NewDecoder(r.Body).Decode(&t) 尝试解析——此时 r.Body 是表单数据,不是合法 JSON,因此解码器报错:“无法把字符串(如 "lat=48.89...")反序列化为 test_struct”。
✅ 正确做法取决于你希望采用的数据协议:
方案一:前端改为发送标准 JSON(推荐)
修改 JavaScript,显式构造 JSON 并设置请求头:
const payload = JSON.stringify({
lat: data.location.lat,
lng: data.location.lng,
acc: 1962 // 示例值,按需补充
});
$.ajax({
type: 'POST',
url: 'http://localhost:9280/post_geo/',
contentType: 'application/json', // ← 必须声明
data: payload,
success: function(data2) {
console.log('Success:', data2);
}
});对应 Go 服务端保持原逻辑即可(但需修复字段名一致性):
type TestStruct struct { // 建议 PascalCase 首字母大写
Lat float32 `json:"lat"`
Lng float32 `json:"lng"` // 注意:前端传的是 "lng",不是 "long"
Acc int `json:"acc"`
}
func postGeo(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var t TestStruct
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&t); err != nil {
http.Error(w, "Invalid JSON: "+err.Error(), http.StatusBadRequest)
return
}
defer r.Body.Close()
Info.Printf("Parsed: %+v", t) // ✅ 此时可正常打印 {Lat:48.892423 Lng:2.215331 Acc:1962}
w.Header().Set("Access-Control-Allow-Origin", "*")
fmt.Fprintf(w, "OK")
}方案二:服务端兼容表单提交(适配现有前端)
若暂无法修改前端,可在 Go 中使用 r.ParseForm() 解析表单:
func postGeo(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
http.Error(w, "Failed to parse form", http.StatusBadRequest)
return
}
lat, _ := strconv.ParseFloat(r.FormValue("lat"), 32)
lng, _ := strconv.ParseFloat(r.FormValue("long"), 32) // 注意字段名为 "long"
acc, _ := strconv.Atoi(r.FormValue("acc"))
t := TestStruct{
Lat: float32(lat),
Lng: float32(lng),
Acc: acc,
}
Info.Printf("From form: %+v", t)
// ... 响应逻辑
}⚠️ 重要注意事项:
- 字段名必须严格一致:前端传
long,结构体就不可用Lng+json:"lng";若坚持用lng,前端必须传lng。 -
defer r.Body.Close()应在r.Body使用之后、且仅一次调用;当前代码中若Decode失败,defer仍会执行,但r.Body可能已关闭或为空——建议移至成功解析后,或改用io.ReadAll+ 字符串调试(开发期)。 - 生产环境避免
panic(err),应返回 HTTP 错误响应,保障服务健壮性。
总结:该错误是典型的「协议错配」问题。确认数据传输格式(JSON vs 表单)并统一前后端约定,是解决此类问题的关键。优先推荐方案一——标准化使用 JSON,语义清晰、扩展性强、生态支持完善。


















