I'm getting an error of:
json.Unmarshal undefined (type interface {} has no field or method Unmarshal)
trying to convert a json byte slice into the generic interface{} type. I'm reading the docs for encoding/json and they give an example that shows this is valid. What gives?
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
)
func main() {
var json interface{}
data, _ := ioutil.ReadFile("testMusic.json")
json.Unmarshal(data, &json)
m := json.(map[string]interface{})
fmt.Printf("%+v", m)
}
You've defined a local variable json that masks the global symbol json referring to the JSON module. Renaming your local variable should allow your code to work.
Related
Using Go's built-in JSON encoding and RSA certificates, encoding an RSA certificate to JSON and then decoding it again fails. Can anyone tell me why?
Sample code:
package main
import (
"math/big"
"bytes"
"crypto/rsa"
"crypto/x509"
"encoding/json"
"fmt"
)
func main() {
x := x509.Certificate{}
N := big.Int{}
N.SetString("22748727785943691703458259255009028279491017474470365445444638136335844588257736864763884308763893296520197853772438093808627310216929382637891614145169115712146316592081285259872752696564531634381437767246277672628285238806746417464073995314135364153811760424176263991818395775316575483628262670505480241278789196525640591586277923027755459881178666634831466031143685993230414451164180917857690755309066589767418739391025091379117132119476119702352443067398453637494721792111385747721853079909232828234315851102595340792201691113238210441101837352511126798300795766862744529103670862211990583319260851455953450220537", 10)
x.PublicKey = &rsa.PublicKey{N:&N, E:65537}
var y x509.Certificate
b := bytes.Buffer{}
err := json.NewEncoder(&b).Encode(x)
if err != nil { fmt.Println(err); return; }
err = json.NewDecoder(&b).Decode(&y)
if err != nil { fmt.Println(err) }
}
This fails in the Decode step with:
json: cannot unmarshal number 22748727785943691703458259255009028279491017474470365445444638136335844588257736864763884308763893296520197853772438093808627310216929382637891614145169115712146316592081285259872752696564531634381437767246277672628285238806746417464073995314135364153811760424176263991818395775316575483628262670505480241278789196525640591586277923027755459881178666634831466031143685993230414451164180917857690755309066589767418739391025091379117132119476119702352443067398453637494721792111385747721853079909232828234315851102595340792201691113238210441101837352511126798300795766862744529103670862211990583319260851455953450220537 into Go struct field Certificate.PublicKey of type float64
Playground example
The problem here is that the Certificate type declares PublicKey to be an interface{} and so the JSON unmarshaller has no hints about what type it should unmarshal the public key into. It tries to guess and gets it wrong; even if the large integer was correctly unmarshalled into a math/big.Int object, the PublicKey field would still be a map[string]interface{}, not a crypto/rsa.PublicKey object and so almost certainly not what you want.
To make this work, you would need to write a custom JSON marshaller / unmarshaller; probably encoding the whole certificate as PEM text would be correct way to store it in the JSON output. In my case, I can get away with just not encoding the certificate to JSON at all.
Following this tutorial I'm trying to read a json file in Golang. It says there are two ways of doing that:
unmarshal the JSON using a set of predefined structs
or unmarshal the JSON using a map[string]interface{}
Since I'll probably have a lot of different json formats I prefer to interpret it on the fly. So I now have the following code:
package main
import (
"fmt"
"os"
"io/ioutil"
"encoding/json"
)
func main() {
// Open our jsonFile
jsonFile, err := os.Open("users.json")
// if we os.Open returns an error then handle it
if err != nil {
fmt.Println(err)
}
fmt.Println("Successfully Opened users.json")
// defer the closing of our jsonFile so that we can parse it later on
defer jsonFile.Close()
byteValue, _ := ioutil.ReadAll(jsonFile)
var result map[string]interface{}
json.Unmarshal([]byte(byteValue), &result)
fmt.Println(result["users"])
fmt.Printf("%T\n", result["users"])
}
This prints out:
Successfully Opened users.json
[map[type:Reader age:23 social:map[facebook:https://facebook.com twitter:https://twitter.com] name:Elliot] map[name:Fraser type:Author age:17 social:map[facebook:https://facebook.com twitter:https://twitter.com]]]
[]interface {}
At this point I don't understand how I can read the age of the first user (23). I tried some variations:
fmt.Println(result["users"][0])
fmt.Println(result["users"][0].age)
But apparently, type interface {} does not support indexing.
Is there a way that I can access the items in the json without defining the structure?
Probably you want
fmt.Println(result["users"].(map[string]interface{})["age"])
or
fmt.Println(result[0].(map[string]interface{})["age"])
As the JSON is a map of maps the type of the leaf nodes is interface{} and so has to be converted to map[string]interface{} in order to lookup a key
Defining a struct is much easier. My top tip for doing this is to use a website that converts JSON to a Go struct definition, like Json-To-Go
I'm trying to write a configuration package that takes a json filename and a configuration struct. It should unmarshal the json into the passed-in struct and return it. I'm trying to work with interfaces so I can pass any struct I want
The error is:
panic: interface conversion: interface {} is map[string]interface {}, not *main.ConfigurationData
I'm not quite sure how to solve this issue.
Here is my main package
package main
import (
"config"
"commons"
)
type ConfigurationData struct {
S3ARN string `json:"S3ARN"`
SQSQueueUrl string `json:"SQSQueueUrl"`
}
var configData *ConfigurationData
func main(){
configData=config.Load("aws.config.json",configData).(*ConfigurationData)
commons.Dump(configData)
}
Here is my config package
package config
import (
"os"
"encoding/json"
"sync"
"commons"
)
var configLock = new(sync.RWMutex)
func Load(filename string,config interface{})interface{} {
file, err := os.Open(filename)
commons.CheckErrorf(err, "Config Open Error")
defer file.Close()
decoder := json.NewDecoder(file)
configLock.Lock()
err = decoder.Decode(&config)
commons.CheckErrorf(err, "Config Decode Error")
configLock.Unlock()
return config
}
This answer explains well why you get the exception.
What you should do:
When the encoding/json package runs into a type that implements the Marshaler interface, it uses that type’s MarshalJSON() method instead of the default marshaling code to turn the object into JSON. Similarly, when decoding a JSON object it will test to see if the object implements the Unmarshaler interface, and if so it will use the UnmarshalJSON() method instead of the default unmarshaling behavior.
Mine solution for this would be to implement UnmarshalJSON method on *ConfigurationData and method Load should accept Unmarshaler interface instead of interface{}.
You can read more about technics here: https://blog.gopheracademy.com/advent-2016/advanced-encoding-decoding/
Then you simple would do json.Unmarshal(b, &config) inside the Load method where b is []byte read from file.
According to the documentation on json.Unmarshall unmarshalling into an interface value will unmarshall to a new struct of one of a predefined list of types:
To unmarshal JSON into an interface value, Unmarshal stores one of
these in the interface value:
bool, for JSON booleans
float64, for JSON numbers
string, for JSON
strings []interface{}, for JSON arrays
map[string]interface{}, for JSON objects
nil for JSON null
In your case, the type that was chosen was map[string]interface{}. Within your function, a pointer to that newly unmarshalled struct is stored in the config parameter and returned. The panic occurs as the type of the value returned is not the type you're asserting it is.
I am trying to parse a json from a third party software. It returns a json like this
{
"top1/dir1": "10",
"top1/dir2": "20",
"top1/dir3": "30",
"top2/diff_val1": "40"
}
JSONLint says this is a valid json. But I could not figure how I can parse this with golang.
The code I used to parse the json file above (to be clear I took the code from another stackoverflow post).
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
)
type mytype []map[string]string
func main() {
var data mytype
file, err := ioutil.ReadFile("t1.json")
if err != nil {
log.Fatal(err)
}
err = json.Unmarshal(file, &data)
if err != nil {
log.Fatal(err)
}
fmt.Println(data)
}
When I do a go run main.go, I get the below error
$ go run main.go
2016/06/19 22:53:57 json: cannot unmarshal object into Go value of type main.mytype
exit status 1
I did try to parse this format with another library - "github.com/Jeffail/gabs", but was unsuccessful. Since this is a valid json, I am pretty sure this can be parsed, but I am not sure how.
There is a Go package with methods for decoding JSON strings.
https://golang.org/pkg/encoding/json/#Unmarshal
Here is an example of usage:
package main
import (
"encoding/json"
"fmt"
)
func main() {
var jsonBlob = []byte(`[
{"Name": "Platypus", "Order": "Monotremata"},
{"Name": "Quoll", "Order": "Dasyuromorphia"}
]`)
type Animal struct {
Name string
Order string
}
var animals []Animal
err := json.Unmarshal(jsonBlob, &animals)
if err != nil {
fmt.Println("error:", err)
}
fmt.Printf("%+v", animals)
}
EDIT: As pointed out by Malik, the type of the value whose pointer you pass is wrong. In this case, your type should be map[string]interface{} (preferably, because a JSON field might not store a string) or map[string]string instead of []map[string]string. The brackets at the beginning are wrong: such would be an array of JSON objects.
It's just that you have a small typo in your program. You've declared mytype as a slice of maps, rather than just a map.
Just change:
type mytype []map[string]string
To:
type mytype map[string]string
See https://play.golang.org/p/pZQl8jV5TC for an example.
Jonathan's answer provides a good example of decoding JSON, and links the relevant package. You don't provide much detail on what exactly is going wrong with your parsing, but if I had to take a guess I would say you're probably not creating an appropriate struct to contain the JSON once it is unmarshalled. Because Go is statically typed, it expects data to adhere to explicitly defined formats.
If you don't want to go to the trouble of defining structs, you could just use an empty interface object, which is sort of a catch all in Go. Simply declare a variable with the type []interface{}, and then pass it into the JSON unmarshal function. Hope this helps!
For example:
{["NewYork",123]}
For json array is decoded as a go array, and go array is need to explicit define a type,
I don't know How to deal with it.
First that json is invalid, objects has to have keys, so it should be something like {"key":["NewYork",123]} or just ["NewYork",123].
And when you're dealing with multiple random types, you just use interface{}.
const j = `{"NYC": ["NewYork",123]}`
type UntypedJson map[string][]interface{}
func main() {
ut := UntypedJson{}
fmt.Println(json.Unmarshal([]byte(j), &ut))
fmt.Printf("%#v", ut)
}
playground
The json package uses map[string]interface{} and []interface{} values to store arbitrary JSON objects and arrays...
http://blog.golang.org/json-and-go
Each value in an object must have key. So suppose this is your json :
{"key":["NewYork",123]}
Then your code should be like this:
package main
import (
"encoding/json"
"fmt"
)
type Message map[string]interface{}
func main() {
msg := Message{}
s := `{"key":["Newyork",123]}`
err := json.Unmarshal([]byte(s), &msg)
fmt.Println(msg, err)
}
You can run it : http://play.golang.org/p/yihj6BZHBY