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

Go新手求教:如何简化嵌套结构体的JSON反序列化

优化Go语言JSON到嵌套结构体的反序列化实现

问题背景

现有如下JSON负载:

{
    "name": "foo-bar",
    "profileName": "my-profile-name"
}

尝试用以下嵌套结构体进行反序列化:

type Project struct {
    Name    string   `json:"name"`
    Profile *Profile `json:"profileName,omitempty"`
}

type Profile struct {
    // Name is the name of the namespace created for the project
    Name string `json:"profileName"`
}

直接反序列化会失败,因为JSON中profileName是字符串类型,而结构体中Profile是结构体指针类型。自己实现了一个UnmarshalJSON方法可以解决问题,但代码过于繁琐,希望找到更简洁的实现方式。

现有解决方案

package main

import (
    "encoding/json"
    "fmt"
)

type Project struct {
    Name    string   `json:"name"`
    Profile *Profile `json:"profileName,omitempty"`
}

type Profile struct {
    // Name is the name of the namespace created for the project
    Name string `json:"profileName"`
}

func (p *Profile) UnmarshalJSON(data []byte) error {
    // try to unmarshal as a string first
    var s string
    err := json.Unmarshal(data, &s)
    if err == nil {
        p.Name = s
        return nil
    }

    // otherwise unmarshal as a struct
    type Alias Profile
    aux := &struct {
        *Alias
    }{
        Alias: (*Alias)(p),
    }
    if err := json.Unmarshal(data, &aux); err != nil {
        return err
    }
    *p = *(*Profile)(aux.Alias)
    return nil
}

func main() {
    payload := []byte(`{
        "name": "foo-bar",
        "profileName": "my-profile-name"
    }`)

    project := Project{
        Profile: &Profile{},
    }
    err := json.Unmarshal(payload, &project)
    if err != nil {
        panic(err)
    }

    fmt.Printf("Name: %s\nProfile Name: %s", project.Name, project.Profile.Name)
}

更优实现方式

方式一:简化自定义UnmarshalJSON方法

如果只需要兼容字符串类型的profileName,可以直接简化Profile的反序列化方法:

func (p *Profile) UnmarshalJSON(data []byte) error {
    var s string
    if err := json.Unmarshal(data, &s); err != nil {
        return err
    }
    p.Name = s
    return nil
}

如果需要同时兼容字符串和结构体形式的profileName(比如JSON中可能出现"profileName": {"profileName": "xxx"}),可以优化为:

func (p *Profile) UnmarshalJSON(data []byte) error {
    // 先尝试解析为字符串
    var s string
    if err := json.Unmarshal(data, &s); err == nil {
        p.Name = s
        return nil
    }
    // 解析为结构体形式
    var aux struct {
        Name string `json:"profileName"`
    }
    if err := json.Unmarshal(data, &aux); err != nil {
        return err
    }
    p.Name = aux.Name
    return nil
}

方式二:使用中间字段转换

不需要自定义UnmarshalJSON,而是在Project结构体中添加一个中间字段接收字符串,再手动赋值:

type Project struct {
    Name         string   `json:"name"`
    ProfileStr   string   `json:"profileName"`
    Profile      *Profile `json:"-"` // 忽略JSON序列化/反序列化
}

func main() {
    payload := []byte(`{
        "name": "foo-bar",
        "profileName": "my-profile-name"
    }`)

    var project Project
    err := json.Unmarshal(payload, &project)
    if err != nil {
        panic(err)
    }
    // 手动赋值给Profile
    project.Profile = &Profile{Name: project.ProfileStr}

    fmt.Printf("Name: %s\nProfile Name: %s", project.Name, project.Profile.Name)
}

方式三:使用自定义字符串类型

如果Profile本质上只需要存储一个字符串,可以直接将Profile定义为自定义字符串类型,简化实现:

type Profile string

func (p *Profile) UnmarshalJSON(data []byte) error {
    var s string
    if err := json.Unmarshal(data, &s); err != nil {
        return err
    }
    *p = Profile(s)
    return nil
}

type Project struct {
    Name    string   `json:"name"`
    Profile *Profile `json:"profileName,omitempty"`
}

func main() {
    payload := []byte(`{
        "name": "foo-bar",
        "profileName": "my-profile-name"
    }`)

    var project Project
    err := json.Unmarshal(payload, &project)
    if err != nil {
        panic(err)
    }

    fmt.Printf("Name: %s\nProfile Name: %s", project.Name, *project.Profile)
}

内容的提问来源于stack exchange,提问作者G.M

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 22:29:59