Extract all JSON keys and values as map - go

i want to get all keys and values of json fields as type map[string][]string. The objects with same keys, should be in same map field.
I can convert JSON to map[string]interface{} now with this function, but it doesn't meet my needs.
func jsonToMap(data []byte) map[string]interface{} {
x := map[string]interface{}
json.Unmarshal(data, &x)
return x
}
Sample json:
{
"data": {
"name": "John",
"age": 30
},
"items": [
1,
2,
3
],
"data2": {
"name": "Johns",
"age": {
"test": [
1,
2,
3,
{
"test2": "123"
}
]
}
}
}
Expected result:
map[string][]string
map["data"][]string{""}
map["name"][]string{"John", "Johns"}
map["age"][]string{"30",""}
map["items"][]string{"1","2","3"}
map["test"][]string{"1","2","3"}
map["test2"][]string{"123"}

The solution is using recursive function.
https://en.wikipedia.org/wiki/Recursion_(computer_science)
Here is a simple example:
https://gobyexample.com/recursion
The following code can do what you want:
package main
import (
"encoding/json"
"fmt"
)
func main() {
JSONStr := `{
"data": {"name": "John", "age": 30},
"items": [1, 2, 3],
"data2": {
"name": "Johns",
"age": {"test": [1, 2, 3, {"test2": "123"}]}
}
}`
var JSON map[string]interface{}
json.Unmarshal([]byte(JSONStr), &JSON)
neededOutput := jsonToMap(JSON)
fmt.Println(neededOutput)
}
func jsonToMap(data map[string]interface{}) map[string][]string {
// final output
out := make(map[string][]string)
// check all keys in data
for key, value := range data {
// check if key not exist in out variable, add it
if _, ok := out[key]; !ok {
out[key] = []string{}
}
if valueA, ok := value.(map[string]interface{}); ok { // if value is map
out[key] = append(out[key], "")
for keyB, valueB := range jsonToMap(valueA) {
if _, ok := out[keyB]; !ok {
out[keyB] = []string{}
}
out[keyB] = append(out[keyB], valueB...)
}
} else if valueA, ok := value.([]interface{}); ok { // if value is array
for _, valueB := range valueA {
if valueC, ok := valueB.(map[string]interface{}); ok {
for keyD, valueD := range jsonToMap(valueC) {
if _, ok := out[keyD]; !ok {
out[keyD] = []string{}
}
out[keyD] = append(out[keyD], valueD...)
}
} else {
out[key] = append(out[key], fmt.Sprintf("%v", valueB))
}
}
} else { // if string and numbers and other ...
out[key] = append(out[key], fmt.Sprintf("%v", value))
}
}
return out
}
Output:
map[age:[30 ] data:[] data2:[] items:[1 2 3] name:[John Johns] test:[1 2 3] test2:[123]]

Related

Creating a new slice from an array of objects golang and group

I have an array of objects that I would like to use to create a new slice while grouping a field for objects with the same id(the id, in this case, the id is pay_method_id) into an array of objects. i.e I want to group all users with a particular payment method
sample input
[{
"user_id": 1,
"pay_method_id": 1
}
{
"user_id": 2,
"pay_method_id": 2
}
{
"user_id": 4,
"pay_method_id": 1
}
{
"user_id": 3,
"pay_method_id": 2
}]
expected output
[
{"pay_method_id" : "2",
"users": [{"user_id": 2}, {"user_id": 3}]
}
{
"pay_method_id" : "1",
"users": [{"user_id": 4}, {"user_id": 1}]
}
]
Struct representing input
type paymenthodUsers struct{
PayMethodID int
UserID int
}
Struct for the output
type paymentMethodGrouped struct{
PayMethodID int
Users []user
}
type user struct{
UserID int
}
How do I generate the expected output above in golang?
package main
import (
"encoding/json"
"fmt"
"log"
)
type paymenthodUsers struct {
PayMethodID int `json:"pay_method_id"`
UserID int `json:"user_id"`
}
type paymentMethodGrouped struct {
PayMethodID int `json:"pay_method_id"`
Users []user `json:"users"`
}
type user struct {
UserID int `json:"user_id"`
}
func main() {
_json := `[{
"user_id": 1,
"pay_method_id": 1
},
{
"user_id": 2,
"pay_method_id": 2
},
{
"user_id": 4,
"pay_method_id": 1
},
{
"user_id": 3,
"pay_method_id": 2
}]`
var paymentmethods []paymenthodUsers
err := json.Unmarshal([]byte(_json), &paymentmethods)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Original input : %+v\n", paymentmethods)
groups := make(map[int][]user)
for _, pm := range paymentmethods {
if _, found := groups[pm.PayMethodID]; !found {
groups[pm.PayMethodID] = []user{}
}
groups[pm.PayMethodID] = append(groups[pm.PayMethodID], user{pm.UserID})
}
paymentGroups := []paymentMethodGrouped{}
for k, v := range groups {
paymentGroups = append(paymentGroups, paymentMethodGrouped{k, v})
}
data, err := json.Marshal(paymentGroups)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Grouped: %s", string(data))
}
Go Playground Demo

Convert JSON key value pair into single field value in Go

I have a json like following, where value can be int or string
{
"data": [
{
"Name": "a_name",
"value": 1
},
{
"Name": "b_name",
"value": "val"
},
{
"Name": "c_name",
"value": 2
}
]
}
Now I want to convert that json into following struct, like only extract a_name and b_name value.
type Data struct {
AName int `json: "a_name"`
BName string `json: "b_name"`
}
I can do it by following way
import (
"encoding/json"
"fmt"
)
type TmpData struct {
Data []struct {
Name string `json:"Name"`
Value interface{} `json:"value"`
} `json:"data"`
}
type ExpectedData struct {
AName int `json: "a_name"`
BName string `json: "b_name"`
}
func main() {
data := `{
"data": [
{
"Name": "a_name",
"value": 1
},
{
"Name": "b_name",
"value": "val"
},
{
"Name": "c_name",
"value": 2
}
]
}`
tmpData := &TmpData{}
json.Unmarshal([]byte(data), tmpData)
ans := &ExpectedData{}
for _, d := range tmpData.Data {
if d.Name == "a_name" {
ans.AName = int(d.Value.(float64))
} else if d.Name == "b_name" {
ans.BName = d.Value.(string)
}
}
fmt.Println(ans)
}
Is there any better solution for this?
Not possible with the standard JSON un-marshalling unless you write a custom un-marshaller for your Data type.
The key here is to define the type for value to be an interface{}, so that multiple types could be stored in your b_name record.
func (d *Data) UnmarshalJSON(data []byte) error {
var result Details
if err := json.Unmarshal(data, &result); err != nil {
return err
}
for _, value := range result.Data {
switch value.Name {
// The json package will assume float64 when Unmarshalling with an interface{}
case "a_name":
v, ok := (value.Value).(float64)
if !ok {
return fmt.Errorf("a_name got data of type %T but wanted float64", value.Value)
}
d.AName = int(v)
case "b_name":
v, ok := (value.Value).(string)
if !ok {
return fmt.Errorf("b_name got data of type %T but wanted string", value.Value)
}
d.BName = v
}
}
return nil
}
Playground - https://go.dev/play/p/GrXKAE87d1F

golang unmarshal map[string]interface{} to a struct containing an array with meta

I have the following json data coming through an API. I want to unmarshal this data into a different way of structure as it is defined below. How can I do it in an elegant way?
{
"_meta": {
"count": 2,
"total": 2
},
"0": {
"key": "key0",
"name": "name0"
},
"1": {
"key": "key1",
"name": "name1"
},
"2": {
"key": "key2",
"name": "name2"
}
// It goes on..
}
type Data struct {
Meta Meta `json:"_meta,omitempty"`
Elements []Element
}
type Element struct {
Key string
Name string
}
type Meta struct{
Count int
Total int
}
This can be quite tricky because you have a json object that holds everything. So i went with the approach of unmarshalling to map of string to *json.RawMessage and then fixing the struct from there.
To do that you will be using a custom Unmarshaler and the benefit of it is that you delay the actual parsing of the inner messages until you need them.
So for example if your meta field was wrong or the numbers it said didn't match the length of the map-1 you could exit prematurely.
package main
import (
"encoding/json"
"fmt"
)
type jdata map[string]*json.RawMessage
type data struct {
Meta Meta
Elements []Element
}
//Element is a key val assoc
type Element struct {
Key string
Name string
}
//Meta holds counts and total of elems
type Meta struct {
Count int
Total int
}
var datain = []byte(`
{
"_meta": {
"count": 2,
"total": 2
},
"0": {
"key": "key0",
"name": "name0"
},
"1": {
"key": "key1",
"name": "name1"
},
"2": {
"key": "key2",
"name": "name2"
}
}`)
func (d *data) UnmarshalJSON(buf []byte) (err error) {
var (
meta *json.RawMessage
ok bool
)
jdata := new(jdata)
if err = json.Unmarshal(buf, jdata); err != nil {
return
}
if meta, ok = (*jdata)["_meta"]; !ok {
return fmt.Errorf("_meta field not found in JSON")
}
if err = json.Unmarshal(*meta, &d.Meta); err != nil {
return
}
for k, v := range *jdata {
if k == "_meta" {
continue
}
elem := &Element{}
if err = json.Unmarshal(*v, elem); err != nil {
return err
}
d.Elements = append(d.Elements, *elem)
}
return nil
}
func main() {
data := &data{}
if err := data.UnmarshalJSON(datain); err != nil {
panic(err)
}
fmt.Printf("decoded:%v\n", data)
}

How to iterate over an []interface{} in Go

I'm struggling to get the keys and values of the following interface, which is the result of JSON marshaling the result returned by Execute as demonstrated in this example:
[
[
{
"id": 36,
"label": "TestThing",
"properties": {
"schema__testBoolean": [
{
"id": 40,
"value": true
}
],
"schema__testInt": [
{
"id": 39,
"value": 1
}
],
"schema__testNumber": [
{
"id": 38,
"value": 1.0879834
}
],
"schema__testString": [
{
"id": 37,
"value": "foobar"
}
],
"uuid": [
{
"id": 41,
"value": "7f14bf92-341f-408b-be00-5a0a430852ee"
}
]
},
"type": "vertex"
}
]
]
A reflect.TypeOf(result) results in: []interface{}.
I've used this to loop over the array:
s := reflect.ValueOf(result)
for i := 0; i < s.Len(); i++ {
singleVertex := s.Index(i).Elem() // What to do here?
}
But I'm getting stuck with errors like:
reflect.Value.Interface: cannot return value obtained from unexported
field or method
If you know that's your data structure, there's no reason to use reflection at all. Just use a type assertion:
for key, value := range result.([]interface{})[0].([]interface{})[0].(map[string]interface{}) {
// key == id, label, properties, etc
}
For getting the underlying value of an interface use type assertion. Read more about Type assertion and how it works.
package main
import (
"fmt"
)
func main() {
res, err := g.Execute( // Sends a query to Gremlin Server with bindings
"g.V(x)",
map[string]string{"x": "1234"},
map[string]string{},
)
if err != nil {
fmt.Println(err)
return
}
fetchValue(res)
}
func fetchValue(value interface{}) {
switch value.(type) {
case string:
fmt.Printf("%v is an interface \n ", value)
case bool:
fmt.Printf("%v is bool \n ", value)
case float64:
fmt.Printf("%v is float64 \n ", value)
case []interface{}:
fmt.Printf("%v is a slice of interface \n ", value)
for _, v := range value.([]interface{}) { // use type assertion to loop over []interface{}
fetchValue(v)
}
case map[string]interface{}:
fmt.Printf("%v is a map \n ", value)
for _, v := range value.(map[string]interface{}) { // use type assertion to loop over map[string]interface{}
fetchValue(v)
}
default:
fmt.Printf("%v is unknown \n ", value)
}
}

How can i put an object in object in golang

What i am trying to do is to load an whole object coming from my database into an object.
type RatingPK struct {
ratingsPK Rating
}
func (r *RatingPK) Init(rg Rating) {
r.ratingsPK = rg
}
func searchUserRatings(id string)(ratings []Rating) {
rows, err := db.Query("SELECT * FROM ratings WHERE userId = ?", id)
if err != nil {
return
}
for rows.Next() {
rate := Rating{}
err = rows.Scan(&rate.MovieId, &rate.UserId, &rate.RatingId, &rate.Rating, &rate.Timestamp)
if err != nil {
return
}
r := RatingPK{}
r.Init(rate)
ratings = append(ratings, r)
}
defer rows.Close()
return
}
this code gives me this result:
[{
"rating_id": 593,
"userId": 2,
"movieId": 93,
"rating": 3,
"timestamp": "0000-00-00"
},
{
"rating_id": 616,
"userId": 2,
"movieId": 94,
"rating": 3,
"timestamp": "0000-00-00"
}]
Now what i want to do is to get each object of the array and put in to another object and the desired result will be this:
[{"ratingsPK": {
"rating_id": 593,
"userId": 2,
"movieId": 93,
"rating": 3,
"timestamp": "0000-00-00"
}},
{"ratingsPK": {
"rating_id": 616,
"userId": 2,
"movieId": 94,
"rating": 3,
"timestamp": "0000-00-00"
}}]
Is there any way to achieve that?
You may loop and do as follows
type RatingPK struct {
ratingsPK Rating
}
func (r *RatingPK) Init(rg Rating) {
r.ratingsPK = rg
}
func searchUserRatings(id string) (ratings []RatingPK) {
rows, err := db.Query("SELECT * FROM ratings WHERE userId = ?", id)
if err != nil {
return
}
var myarray []string
for rows.Next() {
rate := Rating{}
err = rows.Scan(&rate.MovieId, &rate.UserId, &rate.RatingId, &rate.Rating, &rate.Timestamp)
if err != nil {
return
}
r := RatingPK{}
r.Init(rate)
ratings = append(ratings, r)
}
defer rows.Close()
return
}
Here's a short example that will help you understand how to use nested structs ;)
package main
import (
"fmt"
)
type nameType struct {
firstName string
lastName string
}
type student struct {
name nameType
age int
}
func main() {
bob := student{nameType{"bob", "dope"}, 18}
fmt.Println(bob.name.firstName)
}
You can test it up here ;)
https://play.golang.org/p/ft9PGrlrrx

Resources