I have JSON structure of this type
{
"<server>": {
"<guest>": {
"<service>": {
"<part>": {
"num": "<number>",
"type": "<type>",
"timestamp": "2016-02-01T12:53:12Z"
}
}
}
}
}
During unmarshaling above JSON object I need to preserve keys e.g. server
,guest
etc. I thought of below struct but I am unable to unmarshal above JSON object as keys are lost during unmarshalling.
type Section struct {
Bytes int
Files int
Timestamp time.Time
}
type Report struct {
Server string
Guest string
Service string
Part string
Details Section
}
Is there any way to unmarshal above JSON object into struct keeping keys intact.
Edit:
Text inside <server>
is changing. That is why I need to preserve it after unmarshlling so that I can work further on it.
1- Try it on The Go Playground:
package main
import (
"encoding/json"
"fmt"
"time"
)
func main() {
s := `{
"<server1>": {
"<guest2>": {
"<service3>": {
"<part4>": {
"num": 12,
"type": 14,
"timestamp": "2016-02-01T12:53:12Z"
}
}
}
}
}`
var data map[string]map[string]map[string]map[string]Section
err := json.Unmarshal([]byte(s), &data)
if err != nil {
fmt.Println(err)
}
fmt.Println(data)
}
type Section struct {
Bytes int `json:"num"`
Files int `json:"type"`
Timestamp time.Time `json:"timestamp"`
}
type Report struct {
Server string
Guest string
Service string
Part string
Details Section
}
output:
map[<server1>:map[<guest2>:map[<service3>:map[<part4>:{12 14 2016-02-01 12:53:12 +0000 UTC}]]]]
2- Converting to Report struct
, try it on The Go Playground:
package main
import (
"encoding/json"
"fmt"
"time"
)
func main() {
s := `{
"<server1>": {
"<guest2>": {
"<service3>": {
"<part4>": {
"num": 12,
"type": 14,
"timestamp": "2016-02-01T12:53:12Z"
}
}
}
}
}`
var data map[string]map[string]map[string]map[string]Section
err := json.Unmarshal([]byte(s), &data)
if err != nil {
fmt.Println(err)
}
r := Report{}
for k, v := range data {
r.Server = k
for k2, v2 := range v {
r.Guest = k2
for k3, v3 := range v2 {
r.Service = k3
for k4, v4 := range v3 {
r.Part = k4
r.Details = v4
}
}
}
}
fmt.Println(r)
}
type Section struct {
Bytes int `json:"num"`
Files int `json:"type"`
Timestamp time.Time `json:"timestamp"`
}
type Report struct {
Server string
Guest string
Service string
Part string
Details Section
}
output:
{<server1> <guest2> <service3> <part4> {12 14 2016-02-01 12:53:12 +0000 UTC}}
For above JSON this structure will work fine.
type Report struct {
Server struct {
Guest struct {
Service struct {
Part struct {
Num string `json:"num"`
Type string `json:"type"`
Timestamp time.Time `json:"timestamp"`
} `json:"<part>"`
} `json:"<service>"`
} `json:"<guest>"`
} `json:"<server>"`
}
I've written a library that could make this easier to work with. The Reports
can be unmarshaled against your JSON format:
package main
import (
"time"
"github.com/go-restit/lzjson"
)
type Section struct {
Bytes int `json:"num"`
Files int `json:"type"`
Timestamp time.Time `json:"timestamp"`
}
type Report struct {
Server string
Guest string
Service string
Part string
Details Section
}
type Reports []Report
func (reports *Reports) UnmarshalJSON(content []byte) (err error) {
root := lzjson.NewNode()
if err = json.Unmarshal(content, &root); err != nil {
return
}
// reset the slice of reports
*reports = make([]Report, 0, 10)
for _, serverName := range root.GetKeys() {
server := root.Get(serverName)
if err = server.Error(); err != nil {
return
}
for _, guestName := range server.GetKeys() {
guest := server.Get(guestName)
if err = guest.Error(); err != nil {
return
}
for _, serviceName := range guest.GetKeys() {
service := guest.Get(serviceName)
if err = service.Error(); err != nil {
return
}
for _, partName := range service.GetKeys() {
part := service.Get(partName)
if err = part.Error(); err != nil {
return
}
var report Report
timestamp, _ := time.Parse("2006-01-02T15:04:05Z", part.Get("timestamp").String())
report.Server = serverName
report.Guest = guestName
report.Service = serviceName
report.Part = partName
report.Details = Section{
Bytes: part.Get("num").Int(),
Files: part.Get("type").Int(),
Timestamp: timestamp,
}
*reports = append(*reports, report)
}
}
}
}
return
}
You can now do this:
package main
import (
"encoding/json"
"log"
)
func main() {
raw := `
{
"<server 1>": {
"<guest>": {
"<service>": {
"<part>": {
"num": "<number>",
"type": "<type>",
"timestamp": "2016-02-01T12:53:12Z"
}
}
}
},
"<server 2>": {
"<guest>": {
"<service>": {
"<part>": {
"num": "<number>",
"type": "<type>",
"timestamp": "2016-02-01T12:53:12Z"
}
}
}
}
}`
var reports Reports
err := json.Unmarshal([]byte(raw), &reports)
if err != nil {
log.Fatalf("error reading reports: %s", err.Error())
}
log.Printf("Reports: %#v", reports)
}