如何将包含转义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
相关产品推荐
相关产品推荐

