doujishan2247
doujishan2247
2016-09-29 04:21

使用多层嵌套地图取消编组JSON

已采纳

I realize that this question is very similar to others on stackoverflow but I have not been able to model the other questions to my use case.

I have JSON that looks like this (simplified for this post)

{
  "somekey": "string",
  "state": {
    "groups": {
      "host:host1": {
        "status": "OK",
        "morethings": "blah"
      },
      "host:host2": {
        "status": "Alert",
        "morethings": "blah"
      }
    }
  }
}

I am trying to get the hashes under groups into an array so I can iterate through and check the status of the hosts.
Based on some of the other posts here I felt I was on the correct track with this example:

package main

import (
    "encoding/json"
    "fmt"
)

const jsonStream = `
{
  "state": {
    "groups": {
      "host:i-b3a6cea5": {
        "status": "OK",
        "last_triggered_ts": null,
        "last_nodata_ts": null,
        "name": "host:i-b3a6cea5",
        "last_notified_ts": null,
        "last_resolved_ts": null
      },
      "host:i-4d81ca7c": {
        "status": "OK",
        "last_triggered_ts": null,
        "last_nodata_ts": null,
        "name": "host:i-4d81ca7c",
        "last_notified_ts": null,
        "last_resolved_ts": null
      },
      "host:i-a03a7758": {
        "status": "Alert",
        "triggering_value": {
          "to_ts": 1475092440,
          "value": 2,
          "from_ts": 1475092380
        },
        "last_triggered_ts": 1475092440,
        "last_nodata_ts": null,
        "name": "host:i-a03a7758",
        "last_notified_ts": 1475092440,
        "last_resolved_ts": null
      }
    }
  }
}`

type hostDetails struct {
    Status string `json:"status"`
    Name   string `json:"name"`
}

type GroupsData struct {
    Groups map[string]hostDetails `json:"groups"`
}

type Data struct {
    State map[string]GroupsData `json:"state"`
}

func main() {
    var data Data

    err := json.Unmarshal([]byte(jsonStream), &data)
    if err != nil {
        fmt.Println(err)
    }

    fmt.Println(data)

}

but I only end up with an empty data structure:

{map[groups:{map[]}]}

To see if I was even on the correct track I modified my JSON and took out the state key so that groups was the at the top level.
When I do that it populates the data structure as seen here

I'm struggling to understand why I can deal with the 1 level of nesting but not the second level?
My caveman brain thinks I should be able to reuse the pattern for as many levels of nesting I have.
At this point I've been fiddling most of the day and feel like I'm missing something that's right in front of me but can't see it.
Any pointers would be appreciated on how to handle the additional layer of nesting.

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

2条回答

  • dotxxh0998 dotxxh0998 5年前

    1 - You may remove one extra level by using:

    var data map[string]GroupsData
    

    Try it on The Go Playground:

    package main
    
    import (
        "encoding/json"
        "fmt"
    )
    
    func main() {
        var data map[string]GroupsData
        err := json.Unmarshal([]byte(jsonStream), &data)
        if err != nil {
            fmt.Println(err)
        }    
        fmt.Println(data)    
    }
    
    type GroupsData struct {
        Groups map[string]HostDetails `json:"groups"`
    }
    
    type HostDetails struct {
        Status string `json:"status"`
        Name   string `json:"name"`
    }
    
    const jsonStream = `
    {
        "state": {
            "groups": {
                "host:i-b3a6cea5": {
                    "status": "OK",
                    "last_triggered_ts": null,
                    "last_nodata_ts": null,
                    "name": "host:i-b3a6cea5",
                    "last_notified_ts": null,
                    "last_resolved_ts": null
                },
                "host:i-4d81ca7c": {
                    "status": "OK",
                    "last_triggered_ts": null,
                    "last_nodata_ts": null,
                    "name": "host:i-4d81ca7c",
                    "last_notified_ts": null,
                    "last_resolved_ts": null
                },
                "host:i-a03a7758": {
                    "status": "Alert",
                    "triggering_value": {
                        "to_ts": 1475092440,
                        "value": 2,
                        "from_ts": 1475092380
                    },
                    "last_triggered_ts": 1475092440,
                    "last_nodata_ts": null,
                    "name": "host:i-a03a7758",
                    "last_notified_ts": 1475092440,
                    "last_resolved_ts": null
                }
            }
        }
    }`
    

    output:

    map[state:{map[host:i-b3a6cea5:{OK host:i-b3a6cea5} host:i-4d81ca7c:{OK host:i-4d81ca7c} host:i-a03a7758:{Alert host:i-a03a7758}]}]
    

    2 - You may use:

    type Data struct {
        State GroupsData `json:"state"`
    }   
    

    Try it on The Go Playground:

    package main
    
    import (
        "encoding/json"
        "fmt"
    )
    
    func main() {
        var data Data    
        err := json.Unmarshal([]byte(jsonStream), &data)
        if err != nil {
            fmt.Println(err)
        }    
        fmt.Println(data)    
    }
    
    type Data struct {
        State GroupsData `json:"state"`
    }
    type GroupsData struct {
        Groups map[string]hostDetails `json:"groups"`
    }
    
    type hostDetails struct {
        Status string `json:"status"`
        Name   string `json:"name"`
    }
    
    const jsonStream = `
    {
      "state": {
        "groups": {
          "host:i-b3a6cea5": {
            "status": "OK",
            "last_triggered_ts": null,
            "last_nodata_ts": null,
            "name": "host:i-b3a6cea5",
            "last_notified_ts": null,
            "last_resolved_ts": null
          },
          "host:i-4d81ca7c": {
            "status": "OK",
            "last_triggered_ts": null,
            "last_nodata_ts": null,
            "name": "host:i-4d81ca7c",
            "last_notified_ts": null,
            "last_resolved_ts": null
          },
          "host:i-a03a7758": {
            "status": "Alert",
            "triggering_value": {
              "to_ts": 1475092440,
              "value": 2,
              "from_ts": 1475092380
            },
            "last_triggered_ts": 1475092440,
            "last_nodata_ts": null,
            "name": "host:i-a03a7758",
            "last_notified_ts": 1475092440,
            "last_resolved_ts": null
          }
        }
      }
    }`
    

    output:

    {{map[host:i-b3a6cea5:{OK host:i-b3a6cea5} host:i-4d81ca7c:{OK host:i-4d81ca7c} host:i-a03a7758:{Alert host:i-a03a7758}]}}
    

    Your code works with one extra level "state": {} in JSON data: The Go Playground

    点赞 评论 复制链接分享
  • dousuitang5239 dousuitang5239 5年前

    Your mistake is that Data.State shouldn't be a map. If you change the definition to

    type Data struct {
        State GroupsData `json:"state"`
    }
    

    it works: https://play.golang.org/p/oRjSJMDzU8.

    点赞 评论 复制链接分享