Gin中浮点数 NaN JSON序列化问题

作者:matrix 发布时间:2024-12-24 分类:Golang

无意把除数为0的计算放了进来,既然没有发现panic,页面却显示空白。看起来像是没有被全局recover捕获~

gin 控制台显示

Error #01: json: unsupported value: NaN

排查

断点调试下发现json处理的问题,error被push到gin的c.Error里面,gin被判定为私有类型错误,所以没有panic

~/go/pkg/mod/github.com/gin-gonic/gin@v1.10.0/context.go

func (c *Context) Render(code int, r render.Render) {
    c.Status(code)

    if !bodyAllowedForStatus(code) {
        r.WriteContentType(c.Writer)
        c.Writer.WriteHeaderNow()
        return
    }

    if err := r.Render(c.Writer); err != nil {
        // Pushing error to c.Errors
        _ = c.Error(err)
        c.Abort()
    }
}

r.Render(c.Writer)内部会json序列化,error被返回到r.Render。这个错误信息就直接给了上下文的c.Error(err)

原因

Json序列化时存在math.Nan特殊浮点型数据会导response失败,
c.JSON没有任何返回也就是空白页面显示了。

测试

func main() {

    data := map[string]interface{}{
        "name":     "123",
        "name_nan": math.NaN(), // json: unsupported value: NaN
        // "name_inf": math.Inf(1), // json: unsupported value: +Inf
        // "name_inf": math.Inf(-1), // json: unsupported value: -Inf
    }

    a, err := json.Marshal(data)
    fmt.Println(a, err)

}

NaN这种其他特殊意义值 Inf 都回导致 JSON 序列化异常

解决办法

gin中添加错误处理中间件

func ErrorHandler() gin.HandlerFunc {
  return func(c *gin.Context) {
    c.Next() // 继续执行请求处理链

    //中间件处理完成后进行错误收集
    if len(c.Errors) > 0 {
      // 迭代错误并处理
      for _, err := range c.Errors {
        c.JSON(http.StatusInternalServerError, gin.H{
          "code":    http.StatusInternalServerError,
          "message": err.Error(),
          "data":    nil,
        })
        c.Abort()
        return
      }
    }
  }
}

其他办法就是自行实现JSON序列化操作或者判断NaNInf数据

参考:

https://blog.axiaoxin.com/post/2021-11-21-Golang-%E8%BF%90%E8%A1%8C%E6%97%B6%E9%99%A4%E6%95%B0%E4%B8%BA-0-%E8%BF%94%E5%9B%9E-inf/

https://blog.csdn.net/qq_36268452/article/details/124809417

https://blog.csdn.net/qq_40227117/article/details/122186916

JSON Merge Patch 合并结构体字段数据

作者:matrix 发布时间:2023-10-18 分类:Golang

图片5697-JSON Merge Patch 合并结构体字段数据

json Merge Patch,是一个Internet Engineering Task Force(IETF)标准。基本思想是,你有一个原始的json对象,然后根据提供的“补丁”JSON对象,最终生成原始JSON对象需要修改的结果。这种机制适用于部分更新(也称为PATCH更新)的场景。

例子

原始对象:

{
  "Account": "old_account",
  "Name": "old_name",
  "Avatar": "old_avatar"
}

补丁对象(patch object):

{
  "Account": "new_account",
  "Name": null
}

应用补丁对象后的待更新数据(PATCH更新):

{
  "Account": "new_account",
  "Avatar": "old_avatar"
}

简单来说,补丁对象(patch object)描述了以下几种修改:

  • 添加或更新字段:如果补丁中的一个字段在原始对象中不存在,它会被添加;如果存在,它会被更新。

  • 删除字段:如果补丁中的一个字段设置为null,并且该字段在原始对象中存在,那么该字段会被删除。

golang使用

使用实现IETF标准的JSON Merge Patch依赖库 json-patch

go get -u github.com/evanphx/json-patch
// JOSN PATCH
// dst 原始对象
// patch 补丁对象
// return 将补丁应用到原始对象
func MergePatch(dst, patch interface{}) error {
    // 序列化目标(原始)结构体到JSON
    dstJSON, err := json.Marshal(dst)
    if err != nil {
        return err
    }

    // 序列化补丁结构体到JSON,这个补丁描述了如何修改目标(原始)对象
    patchJSON, err := json.Marshal(patch)
    if err != nil {
        return err
    }

    // 使用补丁合并目标(原始)对象
    mergedJSON, err := jsonpatch.MergePatch(dstJSON, patchJSON)
    if err != nil {
        return err
    }

    // 反序列化合并后的JSON回到目标(原始)结构体
    return json.Unmarshal(mergedJSON, dst)
}


调用:


if err := MergePatch(&originJSON, &patchJSON); err != nil { u.JSONResponseError(ctx, err) return } // originJSON 就是应用过补丁的最新原始结构数据

参考:
https://datatracker.ietf.org/doc/html/rfc7396

php的json_decode函数无法解析json

作者:matrix 发布时间:2014-09-04 分类:零零星星

phpjson_decode函数用来解析json数据很方便,但是有时候却解析不了。

究其原因找到如下可能性:

1.键名没有用双引号括起来

['name':n,'age',a]
[name:n,age,a]

这两个都不能解析

2.出现多余逗号

['name':n,'age',a,]

###3.有些转义不支持
数据中出现\x26这样的会失败,有时候\'都无法解析。
stripslashes()去掉转义即可!

4.json不支持gbk编码

iconv('GBK', 'UTF-8', $json_data);//使用iconv()函数将GBK转到UTF-8编码

json数据解析前用检测工具测试一下较好:http://www.bejson.com/

150515添加

/* 
 格式化错误的json数据,使其能被json_decode()解析 
 不支持健名有中文、引号、花括号、冒号 
 不支持健指有冒号 
*/  
function format_ErrorJson($data,$quotes_key=false)  
{  
    $con = str_replace('\'','"',$data);//替换单引号为双引号 
    $con = str_replace(array('\\"'),array('<|YH|>'),$con);//替换 
    $con = preg_replace('/(\w+):[ {]?((?<YinHao>"?).*?\k<YinHao>[,}]?)/is', '"$1": $2',$con );//若键名没有双引号则添加  
    if($quotes_key)  
    {  
        $con = preg_replace('/("\w+"): ?([^"\s]+)([,}])[\s]?/is', '$1: "$2"$3',$con );//给键值添加双引号 
    } 
    $con = str_replace(array('<|YH|>'),array('\\"'),$con);//还原替换  
    return $con;  
}  

参考:http://bbs.csdn.net/topics/390496037

http://chenwei.me/p/59.html

ajax跨域请求json数据

作者:matrix 发布时间:2014-03-13 分类:零零星星

 

刚开始仅仅想获取一个他域的json数据,没想到牵扯到很多的问题。
每次都请求失败:
chrome面板的status为(canceled)

在Request Header这里显示CAUTION Provisional headers are shown

 

后来才知道是ajax跨域问题导致:

也就是ajax同源策略(同源是指域名,协议,端口相同)。
跨域可以实现在自己的网站之间传递数据。但是如果你想用“跨域”盗取其它网站的数据,那还是放弃吧。除非目标网站有给你提供JSONP的接口,或者有某些可以利用的漏洞,要不然真没什么办法实现。
跨域问题的产生,最主要原因是COOKIE的安全问题。因为COOKIE是属于一个域的,如果允许跨域,客户端浏览器上储存的COOKIE就可以被它的所有者之外的程序访问到。举个例子吧,假如没有跨域问题,我现在就可以给百度发送个HTTP请求,获取你在百度上登录的用户名。或者获取SessionID,直接冒充你的帐号登录。为了避免这些问题,所以跨域访问的限制是非常有必要的。

利用jsonp跨域

跨域必须要有回调函数的接口,这里用jsonp试试 阅读剩余部分 »