在Golang中检测JSON输入中的重复键。

huangapple go评论91阅读模式
英文:

Golang - detecting duplicate keys in JSON input

问题

我最近完成了一个项目,其中我使用了Python中的“对象钩子”来检测JSON键是否与另一个键重复。普通的JSON解码器似乎只会给出最后遇到的值,但我希望能够检测并返回错误。我在新公司的新项目是用golang来实现这个功能,所以想知道是否有类似于Python中的对象钩子的方法。我之前还使用了另一个对象钩子来获取Python中的“有序字典”;它本质上是JSON输入的列表形式,保留了原始JSON的顺序。虽然我还没有在golang中被分配这样的任务,但我敢打赌它会出现...无论如何,对于与golang相关的这些JSON功能的意见将不胜感激!

英文:

I recently completed a project where I used the "object hook" in Python to detect whether a JSON key was a duplicate of another key. Normal JSON decoders seem to just give the last value encountered, but I would like to be able to detect and return an error. My new project (at new company) is to write this in golang, so wondering if there is a similar method to Python's object hook. I had also used a different object hook to get an "ordered dict" in Python; essentially a list form of the JSON input, with the ordering of the original JSON preserved. Haven't been tasked with that in golang yet, but I bet it's coming.... anyway, input on either of these JSON capabilities as relate to golang appreciated!

答案1

得分: 1

你需要一个类似于bufio.Scanner或SAX的JSON库。这里有一个实现:github.com/garyburd/json。它会在扫描过程中生成事件,你可以利用这些事件来发现重复的键。

以下是如何使用它的示例代码:

package main

import (
	"fmt"
	"github.com/garyburd/json"
	"io"
	"strings"
)

type Nothing struct{}
type Context struct {
	Kind json.Kind
	Keys map[string]Nothing
}

func Validate(rdr io.Reader) error {
	scanner := json.NewScanner(rdr)
	stack := []Context{}
	for scanner.Scan() {
		if scanner.Kind() == json.Object || scanner.Kind() == json.Array {
			stack = append(stack, Context{
				Kind: scanner.Kind(),
				Keys: map[string]Nothing{},
			})
		} else if scanner.Kind() == json.End {
			if len(stack) == 0 {
				return fmt.Errorf("expected start object or array")
			}
			stack = stack[:len(stack)-1]
		} else if len(stack) > 0 {
			current := stack[len(stack)-1]
			if current.Kind == json.Object {
				key := string(scanner.Name())
				_, exists := current.Keys[key]
				if exists {
					return fmt.Errorf("found duplicate key: %v", key)
				}
				current.Keys[key] = Nothing{}
			}
		}
	}
	return nil
}

func main() {
	rdr := strings.NewReader(`
		{
			"x": 10,
			"y": {
				"z": 1,
				"z": 2
			},
			"z": [1,2,3,4,5]
		}
	`)
	err := Validate(rdr)
	if err == nil {
		fmt.Println("valid json!")
	} else {
		fmt.Println("invalid json:", err)
	}
}

它在遍历JSON对象时构建了一个哈希表的堆栈(用于嵌套对象/数组)。在这些哈希表中,任何重复的键都会导致错误。如果你需要更多详细信息,你可以轻松地给Context添加一个Name属性,并向后遍历堆栈以生成JSON路径(例如a.b.c.d是一个重复的键)。

英文:

What you need is a json library which works like a bufio.Scanner or SAX. One is implemented here: github.com/garyburd/json. It will generate events as it scans which you can use to discover duplicate keys.

Here's an example of how to use it:

package main
import (
"fmt"
"github.com/garyburd/json"
"io"
"strings"
)
type Nothing struct{}
type Context struct {
Kind json.Kind
Keys map[string]Nothing
}
func Validate(rdr io.Reader) error {
scanner := json.NewScanner(rdr)
stack := []Context{}
for scanner.Scan() {
if scanner.Kind() == json.Object || scanner.Kind() == json.Array {
stack = append(stack, Context{
Kind: scanner.Kind(),
Keys: map[string]Nothing{},
})
} else if scanner.Kind() == json.End {
if len(stack) == 0 {
return fmt.Errorf("expected start object or array")
}
stack = stack[:len(stack)-1]
} else if len(stack) > 0 {
current := stack[len(stack)-1]
if current.Kind == json.Object {
key := string(scanner.Name())
_, exists := current.Keys[key]
if exists {
return fmt.Errorf("found duplicate key: %v", key)
}
current.Keys[key] = Nothing{}
}
}
}
return nil
}
func main() {
rdr := strings.NewReader(`
{
"x": 10,
"y": {
"z": 1,
"z": 2
},
"z": [1,2,3,4,5]
}
`)
err := Validate(rdr)
if err == nil {
fmt.Println("valid json!")
} else {
fmt.Println("invalid json:", err)
}
}

As it walks through the JSON object it builds a stack of hash tables. (for nested objects / arrays) Any duplicate keys in one of those hash tables results in an error. If you need more detail you could easily add a Name property to the Context and walk the stack backwards to generate a json path. (like a.b.c.d is a duplicate key)

huangapple
  • 本文由 发表于 2015年1月14日 00:37:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/27927035.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定