英文:
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
)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论