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

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

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。它会在扫描过程中生成事件,你可以利用这些事件来发现重复的键。

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

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/garyburd/json"
  5. "io"
  6. "strings"
  7. )
  8. type Nothing struct{}
  9. type Context struct {
  10. Kind json.Kind
  11. Keys map[string]Nothing
  12. }
  13. func Validate(rdr io.Reader) error {
  14. scanner := json.NewScanner(rdr)
  15. stack := []Context{}
  16. for scanner.Scan() {
  17. if scanner.Kind() == json.Object || scanner.Kind() == json.Array {
  18. stack = append(stack, Context{
  19. Kind: scanner.Kind(),
  20. Keys: map[string]Nothing{},
  21. })
  22. } else if scanner.Kind() == json.End {
  23. if len(stack) == 0 {
  24. return fmt.Errorf("expected start object or array")
  25. }
  26. stack = stack[:len(stack)-1]
  27. } else if len(stack) > 0 {
  28. current := stack[len(stack)-1]
  29. if current.Kind == json.Object {
  30. key := string(scanner.Name())
  31. _, exists := current.Keys[key]
  32. if exists {
  33. return fmt.Errorf("found duplicate key: %v", key)
  34. }
  35. current.Keys[key] = Nothing{}
  36. }
  37. }
  38. }
  39. return nil
  40. }
  41. func main() {
  42. rdr := strings.NewReader(`
  43. {
  44. "x": 10,
  45. "y": {
  46. "z": 1,
  47. "z": 2
  48. },
  49. "z": [1,2,3,4,5]
  50. }
  51. `)
  52. err := Validate(rdr)
  53. if err == nil {
  54. fmt.Println("valid json!")
  55. } else {
  56. fmt.Println("invalid json:", err)
  57. }
  58. }

它在遍历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:

  1. package main
  2. import (
  3. "fmt"
  4. "github.com/garyburd/json"
  5. "io"
  6. "strings"
  7. )
  8. type Nothing struct{}
  9. type Context struct {
  10. Kind json.Kind
  11. Keys map[string]Nothing
  12. }
  13. func Validate(rdr io.Reader) error {
  14. scanner := json.NewScanner(rdr)
  15. stack := []Context{}
  16. for scanner.Scan() {
  17. if scanner.Kind() == json.Object || scanner.Kind() == json.Array {
  18. stack = append(stack, Context{
  19. Kind: scanner.Kind(),
  20. Keys: map[string]Nothing{},
  21. })
  22. } else if scanner.Kind() == json.End {
  23. if len(stack) == 0 {
  24. return fmt.Errorf("expected start object or array")
  25. }
  26. stack = stack[:len(stack)-1]
  27. } else if len(stack) > 0 {
  28. current := stack[len(stack)-1]
  29. if current.Kind == json.Object {
  30. key := string(scanner.Name())
  31. _, exists := current.Keys[key]
  32. if exists {
  33. return fmt.Errorf("found duplicate key: %v", key)
  34. }
  35. current.Keys[key] = Nothing{}
  36. }
  37. }
  38. }
  39. return nil
  40. }
  41. func main() {
  42. rdr := strings.NewReader(`
  43. {
  44. "x": 10,
  45. "y": {
  46. "z": 1,
  47. "z": 2
  48. },
  49. "z": [1,2,3,4,5]
  50. }
  51. `)
  52. err := Validate(rdr)
  53. if err == nil {
  54. fmt.Println("valid json!")
  55. } else {
  56. fmt.Println("invalid json:", err)
  57. }
  58. }

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:

确定