当前位置: 首页 > article >正文

golang自定义MarshalJSON、UnmarshalJSON 原理和技巧

问题出现的原因:在前后端分离的项目中,经常出现的问题是时间戳格式的问题。
后端的日期格式兼容性强,比较完善。前端由于各种原因,日期格式不完善。
就会产生矛盾。

ms int64比较通用,但是unix时间没有可读性,不方便db运维的工作。

以golang为例,讲一下时间戳转换,在内存里面转换时间戳。

golang的时间戳类型叫做:time.Time,标准是RFC 3339。

RFC 3339 是一种日期 - 时间格式的互联网标准,它基于 ISO 8601 格式。其基本格式为YYYY - MM - DDTHH:MM:SS±HH:MM,其中:

  • YYYY表示四位年份,例如2024。
  • MM表示两位月份,范围是01 - 12。
  • DD表示两位日期,范围是01 - 31。
  • T是日期和时间部分的分隔符,它是一个固定的字符,用于区分日期和时间。
  • HH表示两位小时数,采用 24 小时制,范围是00 - 23。
  • MM表示两位分钟数,范围是00 - 59。
  • SS表示两位秒数,范围是00 - 59。
  • ±HH:MM表示时区偏移量,其中+或-表示相对于 UTC(协调世界时)的偏移方向,HH和MM分别表示小时和分钟的偏移量。例如,+08:00表示东八区,比 UTC 快 8 小时;-05:00表示西五区,比 UTC 慢 5 小时。

例如:

  • 2024-06-15T14:30:00+00:00:表示 2024 年 6 月 15 日,下午 2 点 30 分(14:30),时区为 UTC(偏移量为+00:00)。
  • 2024-12-31T23:59:59-05:00:表示 2024 年 12 月 31 日,晚上 11 点 59 分 59 秒(23:59:59),时区为西五区(偏移量为-05:00)。

为方便展示,我绘制了下面的草图,一个https请求从客户端到达数据库,需要经历的最短路径。 nginx作为入口时,但是数据没有格式化,不适合作为拦截点。 往右边继续看,很明显的发现数据流只有在json 序列化/反序列化的时候开始汇聚。 那RFC 3339 标准的time.Time转换为毫秒在这里实现,无疑是工作量最小的修改方式。
(就像小时候,灌酿造的酱油时,在瓶口处放置一个滤网,过滤掉杂质。 也好像查干湖捕鱼时,工人站在冰面出口处将一个一个的鱼勾起来。 又好像,蜀黍办案,在高速路口排兵布阵,是一样的道理。 在数据处理,找到数据的入口 或 出口,然后轻松拿捏。)

(golang语法或代码没有什么好讲的,总可以借鉴或自定义,属于闻道有先后性质的,没有高低之分。 但是一种思维方式,值得推而广之,用在工作和生活的方方面面,减少你前行的阻力。)

在这里插入图片描述

自定义MarshalJSON, UnmarshalJSON。当应用调用json.Marshal(), json.UnMarshal()时就会调用自定义解析函数。

type DevData struct {
        QrCodeStr   string  `json:"qrCodeStr"`
        StartTime      time.Time `json:"StartTime,omitempty" swaggerignore:"false"`
        StartTimeStamp int64     `json:"startTimeStamp"`
}


func (c *DevData) MarshalJSON() ([]byte, error) { 
        type Alias DevData

        aux := &Alias{}
        *aux = Alias(*c)
        aux.StartTime = time.Unix(aux.StartTimeStamp, 0)
        aux.EndTime = time.Unix(aux.EndTimeStamp, 0)

        if data, err := json.Marshal(aux); err == nil { 
                return data, nil                
        } else {
                return nil, err
        }
}

func (c *DevData) UnmarshalJSON(data []byte) error {
        type Alias DevData
        aux := &Alias{}
        if err := json.Unmarshal(data, aux); err != nil {
                return err
        }
        aux.StartTime = time.Unix(aux.StartTimeStamp, 0)
        aux.EndTime = time.Unix(aux.EndTimeStamp, 0)

        *c = DevData(*aux)

        return nil
}

在哪里产生关联:
当一个类型实现了encoding/json包中的json.Marshaler接口的MarshalJSON方法时,json.Marshal函数就会调用这个自定义的MarshalJSON方法来进行 JSON 序列化。json.Marshaler接口定义如下:

type Marshaler interface {
    MarshalJSON() ([]byte, error)
}

type Unmarshaler interface {
    UnmarshalJSON([]byte) error
} 

在这里插入图片描述
在这里插入图片描述

其中的type Alias DevData,是为了避免套娃无线递归问题。
但是引入了新的问题: 对象多拷贝一次,当数据量比较大,api调用比较频繁的时候,浪费cpu时间。

可以用解析之后,用map遍历,我最开始写golang代码时就用过这个笨方法。

好的解决办法是:限定拷贝的数据范围,将需要特殊处理的时间戳,单独封装在一个结构体里面。这样只有特殊字段会多拷贝一次,其他字段不会出现拷贝。

type TimeInfo struct {
        StartTime      time.Time `json:"StartTime,omitempty"`    // 设置标签:omitempty,客户端传入空值时,忽略该字段解析不会报错
        StartTimeStamp int64     `json:"startTimeStamp,omitempty" bson:"-,omitempty"` // 应用传入空值时,解析不报错。 bson注解表示,StartTimeStamp 是一个内存临时变量,不会写入mongodb数据库。
}

func (c *TimeInfo) MarshalJSON() ([]byte, error) { 
        type Alias TimeInfo

        aux := &Alias{}
        *aux = Alias(*c)
        aux.StartTime = time.Unix(aux.StartTimeStamp, 0)
        aux.EndTime = time.Unix(aux.EndTimeStamp, 0)

        if data, err := json.Marshal(aux); err == nil { 
                return data, nil                
        } else {
                return nil, err
        }
}

func (c *TimeInfo) UnmarshalJSON(data []byte) error {
        type Alias TimeInfo
        aux := &Alias{}
        if err := json.Unmarshal(data, aux); err != nil {
                return err
        }
        aux.StartTime = time.Unix(aux.StartTimeStamp, 0)
        aux.EndTime = time.Unix(aux.EndTimeStamp, 0)

        *c = TimeInfo(*aux)

        return nil
}

type DevData struct {
        QrCodeStr   string  `json:"qrCodeStr"`
		TimeInfo
}

http://www.kler.cn/a/449103.html

相关文章:

  • Python入门:4.Python中的运算符
  • sh cmake-linux.sh -- --skip-license --prefix = $MY_INSTALL_DIR
  • 汽车IVI中控开发入门及进阶(46):FFmpeg
  • 写作词汇积累:纰漏、坎肩、颠三倒四、隔阂
  • Halcon例程代码解读:安全环检测(附源码|图像下载链接)
  • 【看海的算法日记✨优选篇✨】第二回:流动之窗,探索算法的优雅之道
  • 【蓝桥杯每日一题】 蜗牛——动态规划
  • Redisson分布式锁的源码解读
  • panddleocr-文本检测+文本方向分类+文本识别整体流程
  • JavaAgent技术应用和原理:JVM持久化监控
  • ubuntu18.04连接不上网络问题
  • Spring Boot与Django对比:哪个更适合做为Web服务器框架?
  • 32岁前端干了8年,是继续做前端开发,还是转其它工作
  • 图像处理中的图像配准方法
  • 详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
  • 【PyQt5 02】基本功能(示例)
  • 作业:循环比赛日程表 与 取余运算(mod)
  • TensorFlow和Keras的区别和关系
  • GitCode 光引计划投稿|智能制造一体化低代码平台 Skyeye云
  • /etc/fstab 文件学习systemd与该文件关系
  • 开发整合笔记
  • 华为IPD流程6大阶段370个流程活动详解_第二阶段:计划阶段 — 86个活动
  • 基于Springboot的数字科技风险报告管理系统
  • 百度热力图数据处理,可直接用于论文
  • 层次聚类算法的研究
  • 江苏计算机专转本 技能Mysql知识点总结(三)