Golang Unmarshal JSON - json

My input JSON, has a list of different elements.
I have problems with the number of the first element of response.
Simplified example:
package main
import (
"fmt"
"log"
"encoding/json"
)
var j = ` {
"response": [
777, // problem here !!!
{
"id": 888,
"from_id": 999,
"to_id": 888,
"text": "hello..."
},
{
"id": 999,
"from_id": 888,
"to_id": 999,
"text": "goodbye..."
}
]
}`
type D struct {
Id int `json:"id"`
FromId int `json:"from_id"`
ToId int `json:"to_id"`
Text string `json:"text"`
}
type R struct {
Count int
Response []D `json:"response"`
}
func main() {
var data = new(R)
err := json.Unmarshal([]byte(j), &data)
if err != nil {
log.Fatal(err)
}
fmt.Println(data.Response)
}
Error on output. I do not understand where the error. Help me please.

1- Here is the working code,try it on The Go Playground:
package main
import (
"encoding/json"
"fmt"
)
func main() {
var d *R
err := json.Unmarshal([]byte(str), &d)
if err != nil {
panic(err)
}
var data R2
data.Count = int(d.Response[0].(float64))
for _, v := range d.Response[1:] {
bs, err := json.Marshal(v)
if err != nil {
panic(err)
}
var d1 *D
err = json.Unmarshal(bs, &d1)
if err != nil {
panic(err)
}
data.Response = append(data.Response, *d1)
}
fmt.Println(data)
}
type R struct {
Response []interface{} `json:"response"`
}
var str = ` {
"response": [
777,
{
"id": 888,
"from_id": 999,
"to_id": 888,
"text": "hello"
},
{
"id": 999,
"from_id": 888,
"to_id": 999,
"text": "goodbye"
}
]
}`
type D struct {
Id int `json:"id"`
FromId int `json:"from_id"`
ToId int `json:"to_id"`
Text string `json:"text"`
}
type R2 struct {
Count int
Response []D
}
output:
{777 [{888 999 888 hello} {999 888 999 goodbye}]}

Your response json is invalid. You use array instead of structure with fields specified in type D.

I have a different approach: https://play.golang.org/p/9Xnxk7tVxE
All you have to do is implement the Unmarshaler interface and add the logic inside the method UnmarshalJSON. It's a similar solution to the other one, but it's more portable since you don't have to do that in the main or external method and it's little more robust :)
package main
import (
"encoding/json"
"fmt"
"log"
)
var j = `{
"response": [
777,
{
"id": 888,
"from_id": 999,
"to_id": 888,
"text": "hello..."
},
{
"id": 999,
"from_id": 888,
"to_id": 999,
"text": "goodbye..."
}
]
}`
type D struct {
Id int `json:"id"`
FromId int `json:"from_id"`
ToId int `json:"to_id"`
Text string `json:"text"`
}
type R struct {
Count int
Response []D
}
func (r *R) UnmarshalJSON(data []byte) error {
var values struct {
Resp []interface{} `json:"response"`
}
if err := json.Unmarshal(data, &values); err != nil {
return err
}
if len(values.Resp) < 1 {
return fmt.Errorf("empty response %v", values.Resp)
}
count, isNumber := values.Resp[0].(float64)
if !isNumber {
return fmt.Errorf("first element has to be a number, we got '%T'", values.Resp[0])
}
r.Count = int(count)
for _, elem := range values.Resp[1:] {
de, err := json.Marshal(elem)
if err != nil {
return err
}
var d D
if err := json.Unmarshal(de, &d); err != nil {
return err
}
r.Response = append(r.Response, d)
}
return nil
}
func main() {
var data = new(R)
if err := json.Unmarshal([]byte(j), &data); err != nil {
log.Fatal(err)
}
fmt.Println(*data)
}

Related

json.Unmarshal not work even though there is an export field

json file:
{
"student_class": [
{
"student_id": 1,
"class_id": 2
},
{
"student_id": 1,
"class_id": 1
},
Struct:
package studentClass
type StudentClasses struct {
StudentClasses []StudentClass
}
type StudentClass struct {
StudentId int `json:"student_id"`
ClassId int `json:"class_id"`
}
my function:
func Read() {
var studentClasses studentClass.StudentClasses
jsonFile, err := os.Open("db/student_class.json")
if err != nil {
fmt.Println(err)
}
defer jsonFile.Close()
byteValue, _ := io.ReadAll(jsonFile)
json.Unmarshal(byteValue, &studentClasses)
for i := 0; i < len(studentClasses.StudentClasses); i++ {
fmt.Println(studentClasses.StudentClasses[i])
}
}
It return nothing
When i add fmt.Println(studentClasses) after json.Unmarshall... then it return {[]}
Error off json.Unmarshal is nil
I have researched about this problem but people with the same problem as me are saying that struct's field is not exported. Example: go json.Unmarshal do not working
I do not know where the error is and what am I doing wrong
Please help me to solve this problem. thanks everyone!
You didn't specify the json name for StudentClasses.
type StudentClasses struct {
StudentClasses []StudentClass `json:"student_class"`
}
Sample:
package main
import (
"encoding/json"
"fmt"
)
type StudentClasses struct {
StudentClasses []StudentClass `json:"student_class,omitempty"`
}
type StudentClass struct {
StudentId int `json:"student_id"`
ClassId int `json:"class_id"`
}
func main() {
_json := `{
"student_class": [
{
"student_id": 1,
"class_id": 2
},
{
"student_id": 1,
"class_id": 1
}
]
}`
var studentClasses StudentClasses
json.Unmarshal([]byte(_json), &studentClasses)
fmt.Printf("%+v", studentClasses)
}

Transform Prometheus Metrics to Json with Golang

I have prometgeus metrics and I want to convert it to json format using golang. I wrote some code but without success.
For example: Prometheus Metric:
# TYPE http_requests_total counter
http_requests_total{code="200",method="GET"} 28
http_requests_total{code="200",method="POST"} 3
The JSON I want to convert:
{
"http_requests_total": [
{
"http_requests_total": {
"code": "200",
"method": "GET",
"value": 28
}
},
{
"http_requests_total": {
"code": "200",
"method": "POST",
"value": 3
}
}
]
}
I'm assuming you're looking for this to be flexible, i.e. not just handling those specific metrics? If so, the following code should do the trick.
package main
import (
"encoding/json"
"fmt"
"log"
"os"
"strings"
dto "github.com/prometheus/client_model/go"
"github.com/prometheus/common/expfmt"
)
func main() {
if err := run(); err != nil {
log.Fatal(err)
}
}
func run() error {
str := `# TYPE http_requests_total counter
http_requests_total{code="200",method="GET"} 28
http_requests_total{code="200",method="POST"} 3
`
parser := &expfmt.TextParser{}
families, err := parser.TextToMetricFamilies(strings.NewReader(str))
if err != nil {
return fmt.Errorf("failed to parse input: %w", err)
}
out := make(map[string][]map[string]map[string]any)
for key, val := range families {
family := out[key]
for _, m := range val.GetMetric() {
metric := make(map[string]any)
for _, label := range m.GetLabel() {
metric[label.GetName()] = label.GetValue()
}
switch val.GetType() {
case dto.MetricType_COUNTER:
metric["value"] = m.GetCounter().GetValue()
case dto.MetricType_GAUGE:
metric["value"] = m.GetGauge().GetValue()
default:
return fmt.Errorf("unsupported type: %v", val.GetType())
}
family = append(family, map[string]map[string]any{
val.GetName(): metric,
})
}
out[key] = family
}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
if err = enc.Encode(out); err != nil {
return fmt.Errorf("failed to encode json: %w", err)
}
return nil
}
Output:
{
"http_requests_total": [
{
"http_requests_total": {
"code": "200",
"method": "GET",
"value": 28
}
},
{
"http_requests_total": {
"code": "200",
"method": "POST",
"value": 3
}
}
]
}

Parse JSON File in Golang

I have JSON File
{
"info": {
"_postman_id": "ac691afd-f987-47ca-82d3-dae2a367e3df",
"name": "ParseGo",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "Gogo",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "https://www.google.com/",
"protocol": "https",
"host": [
"www",
"google",
"com"
],
"path": [
""
]
}
},
"response": []
},
{
"name": "Golang",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": ""
}
},
"response": []
},
{
"name": "Hide Pool!",
"request": {
"method": "GET",
"header": [],
"url": {
"raw": ""
}
},
"response": []
}
],
"protocolProfileBehavior": {}
}
and i want parse this, i want see in terminal name and method. How i can this?
i try this but not work( help pls. I try use this code
package main
{
import (
"encoding/json"
"fmt"
"log"
"os"
)
type Student struct {
Name string
Standard int `json:"Standard"`
}
func main() {
// open the file pointer
studentFile, err := os.Open("data.json")
if err != nil {
log.Fatal(err)
}
defer studentFile.Close()
var studentDecoder *json.Decoder = json.NewDecoder(studentFile)
if err != nil {
log.Fatal(err)
}
var studentList []Student
err = studentDecoder.Decode(&studentList)
if err != nil {
log.Fatal(err)
}
for i, student := range studentList {
fmt.Println("Student", i+1)
fmt.Println("Student name:", student.Name)
fmt.Println("Student standard:", student.Standard)
}
}
I'm not strong in Go, how can I modify the code for my task and is it possible? if i try this code i have this error
2020/11/05 13:29:54 json: cannot unmarshal object into Go value of type []main.Student
exit status 1
You can try to do something like this:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
)
type Data struct {
Item []Item `json:"item"`
}
type Item struct {
Name string `json:"name"`
Request Request `json:"request"`
}
type Request struct {
Method string `json:"method"`
}
func main() {
filename := "/path/to/your_file.json"
jsonFile, err := os.Open(filename)
if err != nil {
fmt.Printf("failed to open json file: %s, error: %v", filename, err)
return
}
defer jsonFile.Close()
jsonData, err := ioutil.ReadAll(jsonFile)
if err != nil {
fmt.Printf("failed to read json file, error: %v", err)
return
}
data := Data{}
if err := json.Unmarshal(jsonData, &data); err != nil {
fmt.Printf("failed to unmarshal json file, error: %v", err)
return
}
// Print
for _, item := range data.Item {
fmt.Printf("Name: %s, Method: %s \n", item.Name, item.Request.Method)
}
}
Result must look like this:
Name: Gogo, Method: GET
Name: Golang, Method: GET
Name: Hide Pool!, Method: GET

Go json.Unmarshall() works with single entity but not with slice

I'm using Go for a simple http client. Here's the entity I'm unmarshalling:
type Message struct {
Id int64
Timestamp int64
Text string
Author User
LastEdited int64
}
type User struct {
Id int64
Name string
}
A single entity looks like this in JSON:
{
"text": "hello, can you hear me?",
"timestamp": 1512964818565,
"author": {
"name": "andrea",
"id": 3
},
"lastEdited": null,
"id": 8
}
Go/json has no problem unmarshalling the single entity:
var m Message
err = json.Unmarshal(body, &m)
if err != nil {
printerr(err.Error())
}
println(m.Text)
However, if the return of the endpoint is multiple entities:
[
{
"text": "hello, can you hear me?",
"timestamp": 1512964800981,
"author": {
"name": "eleven",
"id": 4
},
"lastEdited": null,
"id": 7
}
]
And I change my corresponding Unmarshall to work on a slice of structs, Go throws an error:
var m []Message
err = json.Unmarshal(body, &m)
if err != nil {
printerr(err.Error()) // unexpected end of JSON input
}
for i := 0; i < len(m); i++ {
println(m[i].Text)
}
What gives?
Works fine for me (try it on playground), where are you getting the payload data from? sounds like that's truncating it.
package main
import (
"encoding/json"
"fmt"
)
type Message struct {
Id int64
Timestamp int64
Text string
Author User
LastEdited int64
}
type User struct {
Id int64
Name string
}
func main() {
body := []byte(`[
{
"text": "hello, can you hear me?",
"timestamp": 1512964800981,
"author": {
"name": "eleven",
"id": 4
},
"lastEdited": null,
"id": 7
}
]`)
var m []Message
err := json.Unmarshal(body, &m)
if err != nil {
fmt.Printf("error: %v") // unexpected end of JSON input
}
for i := 0; i < len(m); i++ {
fmt.Println(m[i].Text)
}
}
running it gives this output
hello, can you hear me?

Error unmarshalling JSON in Go that starts with an Array

This is my code
package main
import (
"encoding/json"
"log"
)
type Data struct {
Page int
Pages int
PerPage string
Total int
CountriesList []Country
}
type Country struct {
Id string
Iso string
}
func main() {
body := []byte(`[
{
"page": 1,
"pages": 6,
"per_page": "50",
"total": 256
},
[
{
"id": "ABW",
"iso2Code": "AW"}]]`)
items := make([]Data, 10)
if err := json.Unmarshal(body, &items); err != nil {
log.Fatalf("error %v", err)
}
}
I'm try to unmarshall some JSON and getting the following error:
error json: cannot unmarshal array into Go value of type main.Data
When reading the question, I assumed that there could be multiple Data + list of Country pairs. Here's the solution I ended up with:
package main
import (
"encoding/json"
"fmt"
"log"
)
type Data struct {
Page int
Pages int
PerPage string `json:"per_page, string"`
Total int
}
type Country struct {
Id string
Iso2Code string
}
type DataCountry struct {
Data Data
CountryList []Country
}
func main() {
body := []byte(`[
{
"page": 1,
"pages": 6,
"per_page": "50",
"total": 256
},
[
{
"id": "ABW",
"iso2Code": "AW"}]
]`)
raw := make([]json.RawMessage, 10)
if err := json.Unmarshal(body, &raw); err != nil {
log.Fatalf("error %v", err)
}
sdc := make([]DataCountry, 0)
for i := 0; i < len(raw); i += 2 {
dc := DataCountry{}
data := Data{}
if err := json.Unmarshal(raw[i], &data); err != nil {
fmt.Println("error %v", err)
} else {
dc.Data = data
}
var countries []Country
if err := json.Unmarshal(raw[i+1], &countries); err != nil {
fmt.Println("error %v", err)
} else {
dc.CountryList = countries
}
sdc = append(sdc, dc)
}
fmt.Printf("%v\n", sdc)
}
I found the blog post "Using go to unmarshal json lists with multiple types" very useful for understanding several different options for dealing parsing JSON lists.
package main
import (
"encoding/json"
"fmt"
"log"
)
type Data struct {
Page int
Pages int
Per_Page string
Total int
CountriesList []Country
}
type Country struct {
Id string
Iso2Code string
}
func main() {
body := []byte(`
[
{
"page": 1,
"pages": 6,
"per_page": "50",
"total": 256,
"countrieslist": [
{
"id": "ABW",
"iso2Code": "AW"
}
]
}
]
`)
items := make([]Data, 10)
if err := json.Unmarshal(body, &items); err != nil {
log.Fatalf("error %v", err)
}
fmt.Printf("%#v\n", items)
}
Playground
Output:
[]main.Data{main.Data{Page:1, Pages:6, Per_Page:"50", Total:256, CountriesList:[]main.Country{main.Country{Id:"ABW", Iso2Code:"AW"}}}}
I got some help from #go-nuts on IRC:
package main
import (
"encoding/json"
"fmt"
"log"
)
type T1 struct {
Page, Pages, Total int
PerPage int `json:"per_page,string"`
}
type T2 struct {
ID string
ISO2Code string
}
func main() {
body := []byte(`
[
{
"page": 1,
"pages": 6,
"per_page": "50",
"total": 256
},
[
{
"id": "ABW",
"iso2Code": "AW"
},
{
"id": "AFG",
"iso2Code": "AF"
}
]
]
`)
t1 := T1{}
t2 := []T2{}
if err := json.Unmarshal(body, &[]interface{}{&t1, &t2}); err != nil {
log.Fatalf("error %v", err)
}
fmt.Printf("%#v %#v", t1, t2)
for k, v := range t2 {
fmt.Printf("%v %v\n",k, v.ID)
}
}