V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
x97bgt
V2EX  ›  Go 编程语言

不懂就问: go 里面如何自定义外部类型的 json 序列化方式?

  •  1
     
  •   x97bgt · 2022-06-17 14:52:32 +08:00 · 2489 次点击
    这是一个创建于 876 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我要 json 序列化一个外部类型的变量,把所有 int 类型变量都序列化成"010101"这种表示。

    类似 Java 里的 Gson ,想把自定义的 int 序列化逻辑注册进去。

    这要怎么做到?

    第 1 条附言  ·  2022-06-17 15:38:41 +08:00

    举个例子。下面的类型都是所引用的库里吗的

    type Person struct {
    	name string
    	age int
    	home Home
    }
    
    type Home struct {
    	number int
    	district string
    }
    

    现在有一个变量

    var p Person
    

    我怎么用一种比较优雅的方式来序列化p,把它里面所有的int都序列化成"010101"这种表示?

    第 2 条附言  ·  2022-06-17 15:45:35 +08:00
    问题的根源是,`Person`和`Home`都不是我定义的,我不能为它们增加序列化方法。
    25 条回复    2022-06-20 14:51:34 +08:00
    dreasky
        1
    dreasky  
       2022-06-17 15:00:14 +08:00
    rimutuyuan
        2
    rimutuyuan  
       2022-06-17 15:04:10 +08:00
    自定义类型,实现 MarshalJSON 方法

    type MyInt int

    func (v MyInt) MarshalJSON() ([]byte, error) {

    return big.NewInt(int64(int(v) + 1)).Bytes(), nil
    }

    func main() {
    m := MyInt(1)
    json.Marshal(m)
    }
    rimutuyuan
        3
    rimutuyuan  
       2022-06-17 15:08:26 +08:00
    #2 代码有点问题,但流程是对的

    return big.NewInt(int64(int(v) + 1)).Bytes(), nil
    x97bgt
        4
    x97bgt  
    OP
       2022-06-17 15:10:37 +08:00
    @dreasky
    @rimutuyuan

    外部类型里的变量是 int ,不是自定义类型。

    这样的话,我还需要对这个外部变量再创建一个自定义类型,然后把 int 字段替换成 MyInt 。

    如果这个外部变量里面又嵌套了其他外部类型,那我都要对他们每个都创建一个自定义类型。这样写太反人类了。。。
    rimutuyuan
        5
    rimutuyuan  
       2022-06-17 15:14:29 +08:00
    @x97bgt 只修改最外层即可

    如 type Person struct {name string, age int},只需要实现 Person 的 MarshalJSON ,然后拼接
    x97bgt
        6
    x97bgt  
    OP
       2022-06-17 15:19:41 +08:00
    @rimutuyuan

    有点不大明白。这个 Person 是外部类型,我怎么能实现它的 MarshalJSON ?
    rimutuyuan
        7
    rimutuyuan  
       2022-06-17 15:32:50 +08:00
    @x97bgt

    func (p *Person) MarshalJSON() ([]byte, error) {}
    x97bgt
        8
    x97bgt  
    OP
       2022-06-17 15:36:24 +08:00
    @rimutuyuan

    还是有点搞不明白。

    这个 Person 是我所引用的库里的类型。可以给这种外部类型实现新的方法?
    rimutuyuan
        10
    rimutuyuan  
       2022-06-17 15:50:21 +08:00
    @x97bgt 那就不能了
    icexin
        11
    icexin  
       2022-06-17 17:16:38 +08:00
    Egfly
        12
    Egfly  
       2022-06-17 17:32:22 +08:00
    土办法,引用第三方库的 struct ,你改变不了。那就在你的代码定义一个相同字段的 struct ,然后实现 MarshalJSON()方法

    多了一步遍历转换的过程,看 OP 能不能接受
    join
        13
    join  
       2022-06-17 17:35:21 +08:00
    随便找段 json 复制进去就能生成对应的 struct 类型了,这个工具我写 go 时经常用。
    https://mholt.github.io/json-to-go/
    zoharSoul
        14
    zoharSoul  
       2022-06-17 17:36:09 +08:00   ❤️ 1
    在 golang 里面应该不行, 大道至简, 别琢磨巧妙的方法了.

    大道至简的要义就是, 啰嗦就啰嗦, 多点代码也没啥, 省脑子.
    icexin
        15
    icexin  
       2022-06-17 18:24:15 +08:00   ❤️ 2
    Kisesy
        16
    Kisesy  
       2022-06-17 18:44:38 +08:00
    可以用 github.com/json-iterator/go 的扩展功能给 int 类型注册一个自定义编码
    hxtheone
        17
    hxtheone  
       2022-06-17 19:54:03 +08:00 via iPhone
    原生做法感觉只能定义一个一样的 struct 然后自定义字段序列化方式,加一步数据的转换
    wheeler
        18
    wheeler  
       2022-06-17 21:26:37 +08:00 via iPhone
    go 里方法定义得和类型定义在一个包里,这样应该搞不定。
    x97bgt
        19
    x97bgt  
    OP
       2022-06-17 21:28:48 +08:00
    @cloverstd
    @Kisesy

    这个库貌似可以实现,但实在有点抗拒为了这功能又引入一个新东西。。。多谢老歌
    x97bgt
        20
    x97bgt  
    OP
       2022-06-17 21:34:16 +08:00
    @Egfly
    @icexin
    @hxtheone
    @wheeler

    这也是我想到的:先写一些数据作为中间层,把全部的外部类型都继承一遍。。。。

    没想到只能这么写。。。对 go 这语言改观了。。

    多谢大家
    x97bgt
        21
    x97bgt  
    OP
       2022-06-17 21:34:45 +08:00
    @icexin 代码很贴心,投币感谢了!
    joesonw
        22
    joesonw  
       2022-06-18 00:20:43 +08:00 via iPhone
    你想要的是类似 swift 的 extension ,任意地方可以扩展一个类的方法。这样的坏处是 implicitity ,你用了一堆库,某个库把另一个库的 MashalJSON 给改了,你得到的结果就是一头雾水,明明自己没改呀。
    x97bgt
        23
    x97bgt  
    OP
       2022-06-19 11:42:09 +08:00
    @joesonw
    这个看设计,因为 go 里面的序列化器都是全局的,所以改一个地方就会影响好多个地方。

    像 java 的 Gson 就不会,用的时候新建一个 instance 然后注册进逻辑就可以,只有使用它的地方才带这个逻辑。
    lysS
        24
    lysS  
       2022-06-19 15:06:09 +08:00
    你说的自定义 json 序列化方式是加 tag 吗?

    你可以自定义一个自己的完全一样的 struct
    type MyPerson struct {
    name string `我的自定义 tag`
    age int
    home Home
    }

    然后把 p 强制类型转换后再序列化

    json.Marshal(MyPerson(p))
    x97bgt
        25
    x97bgt  
    OP
       2022-06-20 14:51:34 +08:00
    @lysS 不是。我说的是把'{"name": "Alice", "age": 10, "home": {"district": "XX", "number": 1}}',变成'{"name": "Alice", "age": "1010", "home": {"district": "XX", "number": "01"}}'这样,就是把里面所有的 int 数值类型全部换一个序列化方式。

    是需要引入中间变量
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2037 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 00:22 · PVG 08:22 · LAX 16:22 · JFK 19:22
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.