简体   繁体   中英

Golang unmarshal json map & array

After reading JSON and Go , I do understand the basics of decoding json in Go are. However, there this problem where that input json can be sometimes a map & sometimes an array of maps.

consider the following problem:

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    b := []byte(`[{"email":"example@test.com"}]`)
    c := []byte(`{"email":"example@test.com"}`)

    var m interface{}

    json.Unmarshal(b, &m)
    switch v := m.(type) {
    case []interface{}:
        fmt.Println("this is b", v)
    default:
        fmt.Println("No type found")
    }

    json.Unmarshal(c, &m)
    switch v := m.(type) {
    case map[string]interface{}:
        fmt.Println("this is c", v)
    default:
        fmt.Println("No type found")
    }

}

Now, how to I get to the value email : example@test.com in both cases( b & c )

Question:

  1. If json is an array, I want to loop over each & print email.
  2. if json is an map, I want to print email directly

Play: http://play.golang.org/p/UPoFxorqWl

In my experience, using interface{} to handle json decoding may cause some strange problem, which I tend to avoid it. Although there are ways to achieve it using the reflect package.

Here is a solution for you problem base on your origin solution, hope it helps.

package main

import (
    "encoding/json"
    "fmt"
)

type Item struct {
    Email string `json:email`
}

func main() {
    b := []byte(`[{"email":"example_in_array@test.com"}]`)
    //b := []byte(`{"email":"example@test.com"}`)

    var m = &Item{}
    var ms = []*Item{}
    err := json.Unmarshal(b, &m)
    if err != nil {
        err = json.Unmarshal(b, &ms)
        if err != nil {
            panic(err)
        }
        for _, m := range ms {
            fmt.Println(m.Email)
        }
    } else {
        fmt.Println(m.Email)
    }

}

Is this what you wanted? http://play.golang.org/p/8yrirlUAnp

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    b := []byte(`[{"email":"example@test.com"}]`)
    c := []byte(`{"email":"example@test.com"}`)

    var m interface{}

    json.Unmarshal(b, &m)
    switch v := m.(type) {
    case []interface{}:
        for _, x := range v {
            fmt.Println("this is b", x.(map[string]interface{})["email"])
        }
    default:
        fmt.Println("No type found")
    }

    json.Unmarshal(c, &m)
    switch v := m.(type) {
    case map[string]interface{}:
        fmt.Println("this is c", v["email"])
    default:
        fmt.Println("No type found")
    }

}

Edit: missed the loop part, added it.

This is not the most idiomatic, but another way to do it is to try and marshal into the type that you want. The advantage here is there is no extra reflection beyond what is needed.

http://play.golang.org/p/dV5qCu3tKk

package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    fmt.Println(extract([]byte(`[{"email":"example@test.com"}]`)))
    fmt.Println(extract([]byte(`{"email":"example@test.com"}`)))
}

func extract(b []byte) string {
    var m map[string]string

    err := json.Unmarshal(b, &m)
    if err == nil {
        return m["email"]
    }

    var nested []map[string]string

    err = json.Unmarshal(b, &nested)
    if err == nil {
        for _, m := range nested {
            return m["email"]
        }
    }
    return ""
}

You can try this package

b := []byte(`[{"email":"example@test.com"}]`)
c := []byte(`{"email":"example@test.com"}`)

var m interface{}
var mm interface{}
json.Unmarshal(b, &m)
json.Unmarshal(c, &mm)

x := map[string]interface{}{
   "wrapped": m,
}
xx := map[string]interface{}{
   "wrapped": mm,
} 

var email string
if email_interface, err := GetProperty(x, "wrapped[0].email"); err == nil {
  email, _ = email_interface.(string)
}
if email_interface, err := GetProperty(xx, "wrapped.email"); err == nil {
  email, _ = email_interface.(string)
}

The Idiomatic way: implement Unmarshaler interface:

type Email struct {
    Val string `json:"email"`
}

func (this *Email) UnmarshalJSON(jsonBytes []byte) error {
    var err error
    type EmailStruct Email
    bytesBuffer := bytes.Buffer{}
    if jsonBytes[0] == '[' {
        emails := []EmailStruct{}
        err = json.Unmarshal(jsonBytes, &emails)
        if err != nil {
            return err
        }

        encoder := gob.NewEncoder(&bytesBuffer)
        err = encoder.Encode(&emails[0])
        if err != nil {
            return err
        }

        decoder := gob.NewDecoder(&bytesBuffer)
        err = decoder.Decode(this)
        if err != nil {
            return err
        }

        return err
    }

    email := EmailStruct{}
    err = json.Unmarshal(jsonBytes, &email)
    if err != nil {
        return err
    }

    encoder := gob.NewEncoder(&bytesBuffer)
    err = encoder.Encode(&email)
    if err != nil {
        return err
    }

    decoder := gob.NewDecoder(&bytesBuffer)
    err = decoder.Decode(this)
    if err != nil {
        return err
    }

    return err
}

Use " json.Unmarshal(jsonBytes, location) " to decode json bytes.

email := Email{}
json.Unmarshal(jsonBytes, &email)

OR

emails := []Email{}
json.Unmarshal(jsonBytes, &emails)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM