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

Go语言中解析含动态键的JSON:如何提取Sources的键名并映射到结构体

Go语言中解析含动态键的JSON:如何提取Sources的键名并映射到结构体

嘿,这个问题我熟!你遇到的是Go里解析带动态键JSON的典型场景——因为source1、source2这种键是动态生成的,没法直接用JSON tag绑定到结构体的字段上,咱们一步步来解决它~

首先,先调整一下你的结构体定义,给Source加上Name字段,并且用json:"-"标记它,告诉Go的JSON库这个字段不参与序列化/反序列化(毕竟原JSON里没有这个字段):

type Source struct {
    Name string `json:"-"` // 忽略JSON解析/生成,后续手动赋值
    Type string `json:"type"`
    Url  string `json:"url"`
    Min  int    `json:"min"`
    Max  int    `json:"max"`
}

然后你的顶层结构体可以这样定义,把sources解析成map[string]Source(和你之前的思路一致):

type DataItem struct {
    Id   string `json:"id"`
    Type string `json:"type"`
}

type Config struct {
    Version int                `json:"version"`
    Sources map[string]Source  `json:"sources"`
    Data    []DataItem         `json:"data"`
}

接下来就是关键步骤:解析JSON之后,遍历这个Sources map,把每个键名(也就是source1、source2)手动赋值给对应Source的Name字段。这里有两种常见的处理方式:

方式1:转换为带名称的Source列表

如果你的业务更需要一个包含所有Source的切片,就可以这么做:

var config Config
err := json.Unmarshal(jsonBytes, &config)
if err != nil {
    // 处理解析错误,比如log.Fatal(err)
}

// 遍历map,给每个Source添加上Name字段
var sourcesWithName []Source
for name, source := range config.Sources {
    source.Name = name
    sourcesWithName = append(sourcesWithName, source)
}

// 之后就可以用sourcesWithName来做业务逻辑了

方式2:直接修改map中的Source(用指针)

如果你想保留原来的map结构,同时让每个Source都有Name,那可以把结构体改成指针类型,这样遍历的时候直接修改原对象:

// 调整Config的Sources为指针类型
type Config struct {
    Version int                      `json:"version"`
    Sources map[string]*Source       `json:"sources"`
    Data    []DataItem               `json:"data"`
}

// 解析后遍历赋值
var config Config
err := json.Unmarshal(jsonBytes, &config)
if err != nil {
    // 处理错误
}

for name, source := range config.Sources {
    source.Name = name // 因为是指针,直接修改原对象
}

// 现在config.Sources里的每个Source指针都有Name字段了

完整示例代码

为了让你更直观理解,这里给一个可运行的完整示例:

package main

import (
    "encoding/json"
    "fmt"
)

type Source struct {
    Name string `json:"-"`
    Type string `json:"type"`
    Url  string `json:"url"`
    Min  int    `json:"min"`
    Max  int    `json:"max"`
}

type DataItem struct {
    Id   string `json:"id"`
    Type string `json:"type"`
}

type Config struct {
    Version int                `json:"version"`
    Sources map[string]Source  `json:"sources"`
    Data    []DataItem         `json:"data"`
}

func main() {
    // 模拟你的JSON数据
    jsonData := []byte(`{
        "version": 1,
        "sources": {
            "source1": {
                "type": "number",
                "url": "<path>",
                "min": 0,
                "max": 10
            },
            "source2": {
                "type": "number",
                "url": "<path>",
                "min": 1,
                "max": 15
            }
        },
        "data": [
            {
                "id": "data1",
                "type": "point"
            },
            {
                "id": "data2",
                "type": "line"
            }
        ]
    }`)

    var config Config
    err := json.Unmarshal(jsonData, &config)
    if err != nil {
        fmt.Printf("JSON解析失败:%v\n", err)
        return
    }

    // 转换为带Name的Source列表
    var sources []Source
    for name, src := range config.Sources {
        src.Name = name
        sources = append(sources, src)
    }

    // 输出验证结果
    fmt.Println("解析后的Sources(含名称):")
    for _, s := range sources {
        fmt.Printf("Name: %s, Type: %s, URL: %s, Min: %d, Max: %d\n",
            s.Name, s.Type, s.Url, s.Min, s.Max)
    }
}

本质上,这种方式是Go处理动态键JSON的标准做法——因为动态键无法通过静态结构体的tag直接映射,所以必须先借助map[string]T解析,再手动把键名赋值到结构体的对应字段中。

备注:内容来源于stack exchange,提问作者Robbert

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 12:48:00