Go json解码和编码uint8 数组的问题

问题

最近在做iris框架切换fiber框架时,碰到一个坑,fiber看注释说是默认使用官方标准库的json解析库,iris默认也是使用官方标准库的json解析库,所以切换后没有关注这方面,后面业务研发人员反馈,框架切换后,解析json有问题,以前能解析出来,现在解析不出来了

// 这个结构体,注意看是[]uint8
type Param struct {
    InputIdList []uint8 `json:"inputIdList"`
}

// fiber里解析也很容易
app.Post("/aa", func(ctx *fiber.Ctx) error {
    var param Param
    err := ctx.BodyParser(&param)
    if err != nil {
        //实际代码跑到这里,提示json: cannot unmarshal  into Go value of type uint8
        return err
    }
    // 其他业务逻辑
    // xxxx
    return nil
}

后面跟踪进去发现fiber里使用的是go-json

最后解决方案也很简单,在配置里配置使用官方标准库


app := fiber.New(fiber.Config{
        // json 编码库
        JSONEncoder: json.Marshal,
        // json 解码库
        JSONDecoder: json.Unmarshal,
    })

然后解析就正常了,然后再看编码,发现结构体里含有[]uint8,输出会转成[]byte,在base64编码,如果用刚才param的结构体,输出就是


{
"inputIdList": "AQI="
}

我们期望的结果是


{
"inputIdList": [1,2]
}

后面上网搜索了一下,发现大概都是这样的解决方案


import "fmt"
import "encoding/json"
import "strings"

type Test struct {
    Name  string
    Array []uint8
}

func (t *Test) MarshalJSON() ([]byte, error) {
    var array string
    if t.Array == nil {
        array = "null"
    } else {
        array = strings.Join(strings.Fields(fmt.Sprintf("%d", t.Array)), ",")
    }
    jsonResult := fmt.Sprintf(`{"Name":%q,"Array":%s}`, t.Name, array)
    return []byte(jsonResult), nil
}


func main() {
    t := &Test{"Go", []uint8{'h', 'e', 'l', 'l', 'o'}}

    m, err := json.Marshal(t)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Printf("%s", m) // {"Name":"Go","Array":[104,101,108,108,111]}
}

其实就是结构体序列化的时候可以自定义序列化的方法,不使用官方的,那按这么说,我是不是可以对某个结构按我定义的方法去编码和解码呢?


type Param struct {
    InputIdList []uint8 `json:"inputIdList"`
}


// json编码
func (p *Param) MarshalJSON() ([]byte, error) {
    var array string
    if p.InputIdList == nil {
        array = "null"
    } else {
        array = strings.Join(strings.Fields(fmt.Sprintf("%d", p.InputIdList)), ",")
    }
    jsonResult := fmt.Sprintf(`{"inputIdList":%s}`, array)
    return []byte(jsonResult), nil
}

// json解码
func (p *Param) UnmarshalJSON(data []byte) error {
    validID := regexp.MustCompile(`\{"inputIdList":\[(.*)\]\}`)
    decoderData := validID.FindAllStringSubmatch(string(data), -1)
    if len(decoderData) < 1 {
        return errors.New("json decoder error")
    }
    inputIdList := strings.Split(decoderData[0][1], ",")
    for _, v := range inputIdList {
        id, _ := strconv.Atoi(v)
        p.InputIdList = append(p.InputIdList, uint8(id))
    }
    return nil
}

再回到刚才配置那里,我将官方标准库注释,让他使用回go-json库,发现现在解码和编码都按预期输出了

总结

  1. 框架迁移时,尽量使用之前的编解码库,比如json库,虽然说go-json是和encoding/json是兼容的,但是可能部分场景下解析不同,如果你之前业务已经上线了,那切换时测试覆盖率能否达到100%?如果不能,请不要轻易切换
  2. json解析某个结构时,可以自定义编解码方法
添加新评论