I am receiving this structure and do not know how to create the go structure to marshal it into:
[
{
"searchinfo": [
{
"PrefProv": "68",
"Language": "Uzbek"
}
]
}
]
How do I define the go structure to unMarshal it into?
Here an example:
package main
import (
"encoding/json"
"fmt"
)
type RawStruct []struct {
Searchinfo []struct {
PrefProv string `json:"PrefProv"`
Language string `json:"Language"`
} `json:"searchinfo"`
}
func main() {
raw_data := `[{"searchinfo":[{"PrefProv":"68","Language":"Uzbek"}]}]`
var rawStruct RawStruct
if err := json.Unmarshal([]byte(raw_data), &rawStruct); err != nil {
panic(err.Error())
}
fmt.Printf("%+v\n", rawStruct)
fmt.Println("---------")
for indexStruct, itemStruct := range rawStruct {
fmt.Printf("Iterating element [%d] of itemStruct: [%+v]\n", indexStruct, itemStruct)
for indexInfo, itemInfo := range itemStruct.Searchinfo {
fmt.Printf("[%d] PrefProv: %s\n", indexInfo, itemInfo.PrefProv)
fmt.Printf("[%d] Language: %s\n", indexInfo, itemInfo.Language)
}
}
}
Result:
[{Searchinfo:[{PrefProv:68 Language:Uzbek}]}]
---------
Iterating element [0] of itemStruct: [{Searchinfo:[{PrefProv:68 Language:Uzbek}]}]
[0] PrefProv: 68
[0] Language: Uzbek
Related
I'm using the fsnotify packet to wait for changes in a json file.
I have two problems with this code. The first one is regarding the info returned by ReadFile function. Looks like when I print something returned by the function is empty.
Second issue is regarding the fsnotify that is not reading the file the first time unless i do some modification on the content. I must read the file from the beggining as well.
type Information struct {
Info []Info `json:"info"`
}
type Info struct {
Type string `json:"type"`
News []New `json:"news"`
}
type New struct {
Name string `json:"name"`
Read bool `json:"read"`
}
func ReadFile(file_name string) *Information {
jsonFile, err := os.Open(file_name)
if err != nil {
fmt.Println(err)
}
fmt.Println("Successfully Opened file_name.json")
defer jsonFile.Close()
byteValue, _ := ioutil.ReadAll(jsonFile)
var infor Information
json.Unmarshal(byteValue, &infor)
return &infor
}
// main function
func main() {
// read json file using fsnotify to wait for changes
watcher, err := fsnotify.NewWatcher()
if err != nil {
panic(err)
}
err = watcher.Add(file_json)
if err != nil {
panic(err)
}
for {
select {
case ev, ok := <-watcher.Events:
log.Println("event:", ev)
if !ok {
return
}
if ev.Op&fsnotify.Write == fsnotify.Write {
data := ReadFile(file_name)
fmt.Print("INFORMATION ABOUT FILE:\n")
for _, info := range data.Info {
fmt.Printf("Info type: %s\n", info.Type) // Here is not printing the result of info.Type
for _, news := range info.News {
fmt.Printf("News Name: %s\n", news.Name) // Here is not printing even "News Name:" or News Read:"
fmt.Printf("News Read: %s\n", strconv.FormatBool(news.Read))
}
}
}
case err := <-watcher.Errors:
log.Println("error:", err)
}
}
}
This is the json file:
{
"info": [
{
"type": "general",
"news": [
{ "name": "abc", "read": true },
{ "name": "def", "read": true }
]
},
{
"type": "confidential",
"news": [
{ "name": "xxx", "read": false },
{ "name": "yyy", "read": false }
]
},
]
}
type Information struct {
Info []Info `json:"info"`
}
type Info struct {
Type string `json:"type"`
News []New `json:"news"` // Here should be a slice define.
}
type New struct {
Name string `json:"name"`
Read bool `json:"read"`
}
func ReadFile(file_name string) *Information {
jsonFile, err := os.Open(file_name)
if err != nil {
fmt.Println(err)
}
fmt.Println("Successfully Opened file_name.json")
defer jsonFile.Close()
byteValue, _ := ioutil.ReadAll(jsonFile)
var infor Information
json.Unmarshal(byteValue, &infor)
return &infor
}
func main() {
data := ReadFile("./data.json")
for _, news := range data.Info {
for _, v := range news.News {
name := v.Name
// Add you want to do
fmt.Println(name)
}
}
}
You can not get like this:
getInfo = [general][abc, true, def, true]
[confidential][xxx, false, yyy, false]
I'm trying to take an array of strings that I receive from a Go API, and write them to a file in a weird json list format. There are not brackets [], so i have to create a "dimension" for each of the string values in the array. I'm trying to do this using types/structs, but i keep getting stuck (new at Go). Should i try and just use maps, or is there a way to accomplish this?
This is the code I am using right now:
package main
import (
"fmt"
"io/ioutil"
)
type Dimension struct {
SQL, Type string
}
type Measure struct {
Type string
DrillMembers []string
}
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
a := []string{"country", "year", "gdpPercap", "lifeExp", "pop", "continent"}
cubeSchema := "measures: {\n count: {\n type: `count`,\n drillMembers: "
for i, s := range a {
cubeSchema += s
fmt.Println(cubeSchema)
fmt.Println(i)
}
fileText := []byte(cubeSchema)
fmt.Println(cubeSchema)
err := ioutil.WriteFile("test.js", fileText, 0644)
check(err)
}
This is how I need the output to look:
measures: {
count: {
type: `count`,
drillMembers: [country]
}
},
dimensions: {
country: {
sql: `country`,
type: `string`
},
year: {
sql: `year`,
type: `string`
},
gdppercap: {
sql: `gdppercap`,
type: `string`
},
lifeexp: {
sql: `lifeexp`,
type: `string`
},
pop: {
sql: `pop`,
type: `string`
},
continent: {
sql: `continent`,
type: `string`
}
}
Right now I keep getting stuck with the following output:
measures: {
count: {
type: `count`,
drillMembers: countryyeargdpPercaplifeExppopcontinent
package main
import (
"fmt"
"io/ioutil"
)
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
schema := `measures: {
count: {
type: 'count',
drillMembers: [country]
}
},
dimensions: {
`
a := []string{"country", "year", "gdpPercap", "lifeExp", "pop", "continent"}
var cubeSchema string
for _, s := range a {
cubeSchema += s + ": {\n\tsql: " + s + ",\n\ttype: `string`\n},\n"
}
fileText := []byte(schema + cubeSchema + "}\n}")
fmt.Println(cubeSchema)
err := ioutil.WriteFile("test.js", fileText, 0644)
check(err)
}
check this code.
I tried to do the second part:
package main
import (
"encoding/json"
"fmt"
)
func main() {
a := []string{"country", "year", "gdpPercap", "lifeExp", "pop", "continent"}
var items map[string]sqlType
items = make(map[string]sqlType)
for _, v := range a {
items[v] = sqlType{SQL: v, Type: "string"}
}
dimensions := dimensions{Dimensions: items}
bytes, err := json.Marshal(dimensions)
if err != nil {
panic(err)
}
c := string(bytes)
fmt.Println(c)
}
type sqlType struct {
SQL string `json:"sql"`
Type string `json:"type"`
}
type dimensions struct {
Dimensions map[string]sqlType `json:"dimensions"`
}
What I'm trying to do:
Transform all arrays of length 1 in a JSON file to non arrays.
e.g.
Input: {"path": [{"secret/foo": [{"capabilities": ["read"]}]}]}
Output: {"path": {"secret/foo": {"capabilities": "read"}}}
I can't use Structs as the JSON format will vary...
Right now I've managed to at least detect the 1 length slices:
package main
import (
"encoding/json"
"fmt"
)
func findSingletons(value interface{}) {
switch value.(type) {
case []interface{}:
if len(value.([]interface{})) == 1 {
fmt.Println("1 length array found!", value)
}
for _, v := range value.([]interface{}) {
findSingletons(v)
}
case map[string]interface{}:
for _, v := range value.(map[string]interface{}) {
findSingletons(v)
}
}
}
func removeSingletonsFromJSON(input string) {
jsonFromInput := json.RawMessage(input)
jsonMap := make(map[string]interface{})
err := json.Unmarshal([]byte(jsonFromInput), &jsonMap)
if err != nil {
panic(err)
}
findSingletons(jsonMap)
fmt.Printf("JSON value of without singletons:%s\n", jsonMap)
}
func main() {
jsonParsed := []byte(`{"path": [{"secret/foo": [{"capabilities": ["read"]}]}]}`)
removeSingletonsFromJSON(string(jsonParsed))
fmt.Println(`Should have output {"path": {"secret/foo": {"capabilities": "read"}}}`)
}
Which outputs
1 length array found! [map[secret/foo:[map[capabilities:[read]]]]]
1 length array found! [map[capabilities:[read]]]
1 length array found! [read]
JSON value of without singletons:map[path:[map[secret/foo:[map[capabilities:[read]]]]]]
Should have output {"path": {"secret/foo": {"capabilities": "read"}}}
But I'm not sure how I can change them into non-arrays...
The type switch is your friend:
switch t := v.(type) {
case []interface{}:
if len(t) == 1 {
data[k] = t[0]
And you may use recursion to remove inside elements, like so:
func removeOneElementSlice(data map[string]interface{}) {
for k, v := range data {
switch t := v.(type) {
case []interface{}:
if len(t) == 1 {
data[k] = t[0]
if v, ok := data[k].(map[string]interface{}); ok {
removeOneElementSlice(v)
}
}
}
}
}
I would do this to convert
{"path":[{"secret/foo":[{"capabilities":["read"]}]}]}
to
{"path":{"secret/foo":{"capabilities":"read"}}}:
package main
import (
"encoding/json"
"fmt"
"log"
)
func main() {
s := `{"path":[{"secret/foo":[{"capabilities":["read"]}]}]}`
fmt.Println(s)
var data map[string]interface{}
if err := json.Unmarshal([]byte(s), &data); err != nil {
panic(err)
}
removeOneElementSlice(data)
buf, err := json.Marshal(data)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(buf)) //{"a":"a","n":7}
}
func removeOneElementSlice(data map[string]interface{}) {
for k, v := range data {
switch t := v.(type) {
case []interface{}:
if len(t) == 1 {
data[k] = t[0]
if v, ok := data[k].(map[string]interface{}); ok {
removeOneElementSlice(v)
}
}
}
}
}
I try to read a directory and make a JSON string out of the file entries. But the json.encoder.Encode() function returns only empty objects. For test I have two files in a tmp dir:
test1.js test2.js
The go program is this:
package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
)
type File struct {
name string
timeStamp int64
}
func main() {
files := make([]File, 0, 20)
filepath.Walk("/home/michael/tmp/", func(path string, f os.FileInfo, err error) error {
if f == nil {
return nil
}
name := f.Name()
if len(name) > 3 {
files = append(files, File{
name: name,
timeStamp: f.ModTime().UnixNano() / int64(time.Millisecond),
})
// grow array if needed
if cap(files) == len(files) {
newFiles := make([]File, len(files), cap(files)*2)
for i := range files {
newFiles[i] = files[i]
}
files = newFiles
}
}
return nil
})
fmt.Println(files)
encoder := json.NewEncoder(os.Stdout)
encoder.Encode(&files)
}
And the out that it produces is:
[{test1.js 1444549471481} {test2.js 1444549481017}]
[{},{}]
Why is the JSON string empty?
It doesn't work because none of the fields in the File struct are exported.
The following works just fine:
package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"time"
)
type File struct {
Name string
TimeStamp int64
}
func main() {
files := make([]File, 0, 20)
filepath.Walk("/tmp/", func(path string, f os.FileInfo, err error) error {
if f == nil {
return nil
}
name := f.Name()
if len(name) > 3 {
files = append(files, File{
Name: name,
TimeStamp: f.ModTime().UnixNano() / int64(time.Millisecond),
})
// grow array if needed
if cap(files) == len(files) {
newFiles := make([]File, len(files), cap(files)*2)
for i := range files {
newFiles[i] = files[i]
}
files = newFiles
}
}
return nil
})
fmt.Println(files)
encoder := json.NewEncoder(os.Stdout)
encoder.Encode(&files)
}
I found some posts on how to decoding json nested objects in go, I tried to apply the answers to my problem, but I only managed to find a partial solution.
My json file look like this:
{
"user":{
"gender":"male",
"age":"21-30",
"id":"80b1ea88-19d7-24e8-52cc-65cf6fb9b380"
},
"trials":{
"0":{"index":0,"word":"WORD 1","Time":3000,"keyboard":true,"train":true,"type":"A"},
"1":{"index":1,"word":"WORD 2","Time":3000,"keyboard":true,"train":true,"type":"A"},
},
"answers":{
"training":[
{"ans":0,"RT":null,"gtAns":"WORD 1","correct":0},
{"ans":0,"RT":null,"gtAns":"WORD 2","correct":0}
],
"test":[
{"ans":0,"RT":null,"gtAns":true,"correct":0},
{"ans":0,"RT":null,"gtAns":true,"correct":0}
]
}
}
Basically I need to parse the information inside it and save them into go structure. With the code below I managed to extract the user information, but it looks too complicated to me and it won't be easy to apply the same thing to the "answers" fields which contains 2 arrays with more than 100 entries each. Here the code I'm using now:
type userDetails struct {
Id string `json:"id"`
Age string `json:"age"`
Gender string `json:"gender"`
}
type jsonRawData map[string]interface {
}
func getJsonContent(r *http.Request) ( userDetails) {
defer r.Body.Close()
jsonBody, err := ioutil.ReadAll(r.Body)
var userDataCurr userDetails
if err != nil {
log.Printf("Couldn't read request body: %s", err)
} else {
var f jsonRawData
err := json.Unmarshal(jsonBody, &f)
if err != nil {
log.Printf("Error unmashalling: %s", err)
} else {
user := f["user"].(map[string]interface{})
userDataCurr.Id = user["id"].(string)
userDataCurr.Gender = user["gender"].(string)
userDataCurr.Age = user["age"].(string)
}
}
return userDataCurr
}
Any suggestions? Thanks a lot!
You're doing it the hard way by using interface{} and not taking advantage of what encoding/json gives you.
I'd do it something like this (note I assumed there was an error with the type of the "gtAns" field and I made it a boolean, you don't give enough information to know what to do with the "RT" field):
package main
import (
"encoding/json"
"fmt"
"io"
"log"
"strconv"
"strings"
)
const input = `{
"user":{
"gender":"male",
"age":"21-30",
"id":"80b1ea88-19d7-24e8-52cc-65cf6fb9b380"
},
"trials":{
"0":{"index":0,"word":"WORD 1","Time":3000,"keyboard":true,"train":true,"type":"A"},
"1":{"index":1,"word":"WORD 2","Time":3000,"keyboard":true,"train":true,"type":"A"}
},
"answers":{
"training":[
{"ans":0,"RT":null,"gtAns":true,"correct":0},
{"ans":0,"RT":null,"gtAns":true,"correct":0}
],
"test":[
{"ans":0,"RT":null,"gtAns":true,"correct":0},
{"ans":0,"RT":null,"gtAns":true,"correct":0}
]
}
}`
type Whatever struct {
User struct {
Gender Gender `json:"gender"`
Age Range `json:"age"`
ID IDString `json:"id"`
} `json:"user"`
Trials map[string]struct {
Index int `json:"index"`
Word string `json:"word"`
Time int // should this be a time.Duration?
Train bool `json:"train"`
Type string `json:"type"`
} `json:"trials"`
Answers map[string][]struct {
Answer int `json:"ans"`
RT json.RawMessage // ??? what type is this
GotAnswer bool `json:"gtAns"`
Correct int `json:"correct"`
} `json:"answers"`
}
// Using some custom types to show custom marshalling:
type IDString string // TODO custom unmarshal and format/error checking
type Gender int
const (
Male Gender = iota
Female
)
func (g *Gender) UnmarshalJSON(b []byte) error {
var s string
err := json.Unmarshal(b, &s)
if err != nil {
return err
}
switch strings.ToLower(s) {
case "male":
*g = Male
case "female":
*g = Female
default:
return fmt.Errorf("invalid gender %q", s)
}
return nil
}
func (g Gender) MarshalJSON() ([]byte, error) {
switch g {
case Male:
return []byte(`"male"`), nil
case Female:
return []byte(`"female"`), nil
default:
return nil, fmt.Errorf("invalid gender %v", g)
}
}
type Range struct{ Min, Max int }
func (r *Range) UnmarshalJSON(b []byte) error {
// XXX could be improved
_, err := fmt.Sscanf(string(b), `"%d-%d"`, &r.Min, &r.Max)
return err
}
func (r Range) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf(`"%d-%d"`, r.Min, r.Max)), nil
// Or:
b := make([]byte, 0, 8)
b = append(b, '"')
b = strconv.AppendInt(b, int64(r.Min), 10)
b = append(b, '-')
b = strconv.AppendInt(b, int64(r.Max), 10)
b = append(b, '"')
return b, nil
}
func fromJSON(r io.Reader) (Whatever, error) {
var x Whatever
dec := json.NewDecoder(r)
err := dec.Decode(&x)
return x, err
}
func main() {
// Use http.Get or whatever to get an io.Reader,
// (e.g. response.Body).
// For playground, substitute a fixed string
r := strings.NewReader(input)
// If you actually had a string or []byte:
// var x Whatever
// err := json.Unmarshal([]byte(input), &x)
x, err := fromJSON(r)
if err != nil {
log.Fatal(err)
}
fmt.Println(x)
fmt.Printf("%+v\n", x)
b, err := json.MarshalIndent(x, "", " ")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Re-marshalled: %s\n", b)
}
Playground
Of course if you want to reuse those sub-types you could pull them out of the "Whatever" type into their own named types.
Also, note the use of a json.Decoder rather than reading in all the data ahead of time. Usually try and avoid any use of ioutil.ReadAll unless you really need all the data at once.