You need to enable JavaScript to run this app. 如何将包含转义JSON字段的数据解析为Go struct并实现键值查询
优惠活动
大模型
产品
解决方案
定价
更多

如何将包含转义JSON字段的数据解析为Go struct并实现键值查询

Go解析嵌套转义JSON字符串方案

1 定义适配的结构体类型

最优雅的实现是自定义SourceMap类型,实现json.Unmarshaler接口,自动完成嵌套转义JSON的解析:

package main

import "encoding/json"

// SourceMap 对应sources字段解析后的map结构
type SourceMap map[string]string

// UnmarshalJSON 实现自定义JSON解析逻辑
func (m *SourceMap) UnmarshalJSON(data []byte) error {
    // 先解析外层的字符串值(去掉JSON字符串的外层引号)
    var rawJsonStr string
    if err := json.Unmarshal(data, &rawJsonStr); err != nil {
        return err
    }
    // 再解析内部的转义JSON字符串为map
    var tmpMap map[string]string
    if err := json.Unmarshal([]byte(rawJsonStr), &tmpMap); err != nil {
        return err
    }
    *m = tmpMap
    return nil
}

type Config struct {
    Key     string    `json:"key" validate:"required"`
    Sources SourceMap `json:"sources" validate:"required"`
}

2 实现查询逻辑

给SourceMap添加查询方法,满足存在则返回对应值、不存在返回默认值的需求:

// Get 查询指定key,不存在则返回default对应的值
func (m SourceMap) Get(key string) string {
    if val, ok := m[key]; ok {
        return val
    }
    return m["default"]
}

使用示例:

func main() {
    rawData := []byte(`{
        "key": "123",
        "sources": "{\"1a\":\"source1a\",\"2b\":\"source2b\",\"3c\":\"source3c\",\"default\":\"sourcex\"}"
    }`)
    var cfg Config
    if err := json.Unmarshal(rawData, &cfg); err != nil {
        panic(err)
    }
    println(cfg.Sources.Get("1a")) // 输出 source1a
    println(cfg.Sources.Get("4d")) // 输出 sourcex
}

3 单元测试编写

以下测试用例覆盖解析、查询的核心场景,示例使用testify/assert简化断言,无该依赖可替换为原生testing判断逻辑:

package main

import (
	"encoding/json"
	"testing"
	"github.com/stretchr/testify/assert"
)

func TestConfig_Unmarshal(t *testing.T) {
	t.Run("合法JSON正常解析", func(t *testing.T) {
		rawData := []byte(`{
			"key": "test_key",
			"sources": "{\"1a\":\"source1a\",\"2b\":\"source2b\",\"default\":\"def\"}"
		}`)
		var cfg Config
		err := json.Unmarshal(rawData, &cfg)
		assert.NoError(t, err)
		assert.Equal(t, "test_key", cfg.Key)
		assert.Equal(t, "source1a", cfg.Sources["1a"])
		assert.Equal(t, "def", cfg.Sources["default"])
	})

	t.Run("非法sources格式返回错误", func(t *testing.T) {
		rawData := []byte(`{
			"key": "test_key",
			"sources": "{invalid_json}"
		}`)
		var cfg Config
		err := json.Unmarshal(rawData, &cfg)
		assert.Error(t, err)
	})
}

func TestSourceMap_Get(t *testing.T) {
	sm := SourceMap{
		"1a": "val1a",
		"default": "def_val",
	}
	assert.Equal(t, "val1a", sm.Get("1a"))
	assert.Equal(t, "def_val", sm.Get("not_exist_key"))
}

备选简化方案

如果不想自定义解析逻辑,也可以分两步解析:

type Config struct {
	Key        string            `json:"key" validate:"required"`
	SourcesStr string            `json:"sources" validate:"required"`
	Sources    map[string]string `json:"-"` // 忽略自动JSON解析,手动赋值
}

// 解析逻辑
var cfg Config
// 第一步解析外层结构
if err := json.Unmarshal(rawData, &cfg); err != nil {
	// 处理错误
}
// 第二步单独解析sources字符串
if err := json.Unmarshal([]byte(cfg.SourcesStr), &cfg.Sources); err != nil {
	// 处理错误
}

内容的提问来源于stack exchange,提问作者alakin_11

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.12 09:42:01