如何在 Go 中检测 JSON 输入中的重复键

来源:站长工具作者:缓存小熊猫头衔:程序员
导读:本期聚焦于小伙伴创作的《如何在 Go 中检测 JSON 输入中的重复键》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何在 Go 中检测 JSON 输入中的重复键》有用,将其分享出去将是对创作者最好的鼓励。

在Go语言的标准库中,使用encoding/json包解析JSON时,如果出现重复键,后出现的键值对会覆盖先出现的内容,且不会返回任何错误提示,这在某些需要严格校验输入的场景下会带来隐患。因此我们需要额外实现逻辑来检测JSON中的重复键。

如何在 Go 中检测 JSON 输入中的重复键

基于标准库自定义解析器实现检测

我们可以通过自定义json.Decoder的行为,在解析过程中记录已经出现过的键,从而判断是否存在重复。核心思路是实现io.Reader接口包装原始输入,在解析每个键值对时做校验。

实现步骤

  • 定义一个结构体,内部维护已出现键的集合和原始输入流
  • 重写Read方法,在读取过程中解析JSON的键并校验重复
  • 使用自定义的Reader作为json.Decoder的输入源

代码示例

package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"strings"
)

// 自定义Reader,用于检测JSON重复键
type duplicateKeyDetector struct {
	reader    io.Reader
	scanner   *bufio.Scanner
	keys      map[string]bool
	inString  bool
	escape    bool
	keyBuffer bytes.Buffer
	parsingKey bool
}

func newDuplicateKeyDetector(r io.Reader) *duplicateKeyDetector {
	return &duplicateKeyDetector{
		reader:  r,
		scanner: bufio.NewScanner(r),
		keys:    make(map[string]bool),
	}
}

// 简化版解析逻辑,仅用于演示重复键检测核心思路
func (d *duplicateKeyDetector) detect() (map[string]interface{}, error) {
	// 重置scanner,因为之前可能已经读取过部分内容
	d.scanner = bufio.NewScanner(d.reader)
	d.scanner.Split(bufio.ScanBytes)
	d.keys = make(map[string]bool)
	d.keyBuffer.Reset()
	d.parsingKey = false
	d.inString = false
	d.escape = false

	result := make(map[string]interface{})
	for d.scanner.Scan() {
		b := d.scanner.Bytes()[0]
		// 处理字符串转义
		if d.escape {
			d.escape = false
			if d.parsingKey {
				d.keyBuffer.WriteByte(b)
			}
			continue
		}
		if b == '\' {
			d.escape = true
			if d.parsingKey {
				d.keyBuffer.WriteByte(b)
			}
			continue
		}
		// 处理字符串开始和结束
		if b == '"' {
			d.inString = !d.inString
			if d.parsingKey {
				d.keyBuffer.WriteByte(b)
			}
			// 字符串结束,判断是否是键
			if !d.inString && d.parsingKey {
				key := d.keyBuffer.String()
				// 去除键两端的引号
				key = strings.Trim(key, """)
				if _, exists := d.keys[key]; exists {
					return nil, errors.New("检测到重复键: " + key)
				}
				d.keys[key] = true
				d.parsingKey = false
				d.keyBuffer.Reset()
			}
			continue
		}
		// 处理键的起始
		if !d.inString && b == ':' {
			d.parsingKey = true
			continue
		}
		// 收集键的字符
		if d.parsingKey && d.inString {
			d.keyBuffer.WriteByte(b)
		}
	}
	// 使用标准库解析完整JSON
	d.reader = strings.NewReader(fmt.Sprint(d.scanner.Text()))
	var finalResult map[string]interface{}
	decoder := json.NewDecoder(strings.NewReader(fmt.Sprintf("{%s}", d.keyBuffer.String())))
	// 重新读取原始完整输入解析
	originalData, _ := io.ReadAll(d.reader)
	decoder = json.NewDecoder(bytes.NewReader(originalData))
	if err := decoder.Decode(&finalResult); err != nil {
		return nil, err
	}
	return finalResult, nil
}

func main() {
	// 测试包含重复键的JSON
	jsonInput := `{"name": "test", "age": 18, "name": "repeat"}`
	detector := newDuplicateKeyDetector(strings.NewReader(jsonInput))
	_, err := detector.detect()
	if err != nil {
		fmt.Println("检测结果:", err)
	}
	// 测试无重复键的JSON
	jsonInput2 := `{"name": "test", "age": 18, "gender": "male"}`
	detector2 := newDuplicateKeyDetector(strings.NewReader(jsonInput2))
	result, err := detector2.detect()
	if err != nil {
		fmt.Println("检测结果:", err)
	} else {
		fmt.Println("解析结果:", result)
	}
}

使用第三方库简化实现

如果不想自己实现复杂的解析逻辑,可以使用第三方库来快速实现重复键检测。常用的库如github.com/icza/dyno,它提供了更灵活的JSON解析能力,支持检测重复键。

安装依赖

go get github.com/icza/dyno

代码示例

package main

import (
	"fmt"
	"github.com/icza/dyno"
)

func main() {
	// 包含重复键的JSON
	jsonStr := `{"a": 1, "b": 2, "a": 3}`
	// 解析时检测重复键
	v, err := dyno.NewJSONParser(strings.NewReader(jsonStr)).Parse()
	if err != nil {
		fmt.Println("解析错误:", err)
		return
	}
	// 检查是否存在重复键
	if dupErr := dyno.CheckDuplicateKeys(v); dupErr != nil {
		fmt.Println("检测到重复键:", dupErr)
	} else {
		fmt.Println("无重复键,解析结果:", v)
	}
}

两种方案对比

方案优点缺点
自定义标准库实现无额外依赖,可控性高实现复杂度高,需要处理各种JSON语法边界情况
第三方库实现实现简单,开箱即用需要引入外部依赖,可能存在版本兼容问题

注意事项

  • 自定义解析逻辑时,需要完整处理JSON的各种语法,比如嵌套对象、数组、转义字符、Unicode字符等,否则可能出现漏检或误检
  • 如果JSON输入来自不可信来源,检测重复键的同时也需要做其他安全校验,避免恶意输入导致解析异常
  • 部分场景下重复键可能是允许的,需要根据实际业务需求决定是否开启检测逻辑

GoJSON重复键检测json_Decoder修改时间:2026-07-13 11:12:34

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。