You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go中jsonpatch.Patch反序列化对象失败问题及解决方法

JSON Patch解码错误:无法将对象反序列化为jsonpatch.Patch类型

问题场景

使用Go 1.21.1结合Gin框架编写Issue资源的HTTP PATCH接口,引入github.com/evanphx/json-patch库处理JSON Patch操作。执行patch, err := jsonpatch.DecodePatch(patchJSON)时触发错误:

json: cannot unmarshal object into Go value of type jsonpatch.Patch

发送请求的curl命令:

curl -d '{"op": "replace", "path": "/summarybrief", "value": "Second Test Issue"}' -X PATCH http://localhost:8080/issue/22 -H "Content-Type: application/json"

完整接口代码:

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "io"
    "net/http"

    jsonpatch "github.com/evanphx/json-patch"
    "github.com/gin-gonic/gin"
    "github.com/jasonalansmith/maelstrom-platform-api/database"
)

type Issue struct {
    SysId        uint   `json:"sysid,omitempty"`
    Identifier   string `json:"identifier,omitempty"`
    SummaryBrief string `json:"summarybrief,omitempty"`
    SummaryLong  string `json:"summarylong,omitempty"`
}

func patchIssue(ctx *gin.Context) {
    id := ctx.Param("sysid")

    iss := Issue{}
    sqls := "SELECT * FROM issue WHERE sysid = $1"
    res := database.Db.QueryRow(sqls, id)
    err := res.Scan(&iss.SysId, &iss.Identifier, &iss.SummaryBrief,
        &iss.SummaryLong)
    if err == sql.ErrNoRows {
        ctx.AbortWithStatusJSON(400, "Issue does not exist.")
        return
    }

    issueBytes, err := json.Marshal(iss)
    if err != nil {
        fmt.Println("Error creating patch json ", err.Error())
        return
    }

    patchJSON, err := io.ReadAll(ctx.Request.Body)
    fmt.Println(string(patchJSON))
    if err != nil {
        fmt.Println(err)
    }

    patch, err := jsonpatch.DecodePatch(patchJSON)
    if err != nil {
        fmt.Println("Error decoding patch json ", err.Error())
        return
    }

    patchedIssue, err := patch.Apply(issueBytes)
    if err != nil {
        fmt.Println("Error applying patch json ", err.Error())
        return
    }

    fmt.Println(patchedIssue)
}

问题原因

github.com/evanphx/json-patch库严格遵循RFC 6902 JSON Patch规范,该规范要求请求体必须是包含一个或多个操作对象的数组,即使只有单个操作也需要用数组包裹。而当前curl发送的是单个JSON对象,不符合库的输入要求,因此触发反序列化错误。

解决方案

方案1:修改请求格式(推荐)

将curl请求中的单个操作对象包裹成数组:

curl -d '[{"op": "replace", "path": "/summarybrief", "value": "Second Test Issue"}]' -X PATCH http://localhost:8080/issue/22 -H "Content-Type: application/json"

方案2:代码兼容单个对象输入(可选)

如果需要兼容客户端发送的单个操作对象,可以在代码中先判断输入格式,将单个对象转换为数组后再解码:

// 在读取patchJSON之后添加以下处理逻辑
var raw interface{}
if err := json.Unmarshal(patchJSON, &raw); err != nil {
    fmt.Println("Invalid JSON:", err.Error())
    ctx.AbortWithStatusJSON(http.StatusBadRequest, "Invalid patch format")
    return
}

var patchArray []byte
switch v := raw.(type) {
case map[string]interface{}:
    // 单个操作对象,包装为数组
    arr := []interface{}{v}
    var err error
    patchArray, err = json.Marshal(arr)
    if err != nil {
        fmt.Println("Failed to wrap patch:", err.Error())
        ctx.AbortWithStatusJSON(http.StatusInternalServerError, "Failed to process patch")
        return
    }
case []interface{}:
    // 已经是数组格式,直接序列化
    var err error
    patchArray, err = json.Marshal(v)
    if err != nil {
        fmt.Println("Failed to marshal patch array:", err.Error())
        ctx.AbortWithStatusJSON(http.StatusInternalServerError, "Failed to process patch")
        return
    }
default:
    ctx.AbortWithStatusJSON(http.StatusBadRequest, "Invalid patch format: must be object or array")
    return
}

// 使用处理后的数组数据解码patch
patch, err := jsonpatch.DecodePatch(patchArray)

内容的提问来源于stack exchange,提问作者Jason Alan Smith

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 19:12:29