Parse JSON File in Golang - json

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

Related

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
}
}
]
}

I cannot print data side by side in JSON array in Golang Colly

I'm taking pictures with golang colly from Amazon and I want to throw these pictures in JSON into a single array (only 1 array for each product images). I scraped the pictures I need, I'm just having a problem with the JSON file. Thank you very much in advance.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"time"
"github.com/gocolly/colly"
)
type Info struct {
ID int `json:"id"`
Images []string `json:"images"`
}
func main() {
AllInfos := make([]Info, 0)
start := time.Now()
co := colly.NewCollector(
colly.AllowedDomains("www.amazon.com", "amazon.com"),
)
// GET Images
Counter := 0
var info Info
var theArray [10]string
co.OnHTML("img[src]", func(e *colly.HTMLElement) {
imgsrc := e.Attr("src")
imgclass := e.Attr("class")
if imgsrc[0:49] == "https://images-na.ssl-images-amazon.com/images/I/" && imgclass == "" {
theArray[Counter] = imgsrc
fmt.Printf("The Array %d %v", Counter, theArray[Counter]+"\n")
Counter = Counter + 1
co.Visit(e.Request.AbsoluteURL(imgsrc))
info = Info{
Images: []string{
theArray[0],
theArray[1],
theArray[2],
theArray[3],
theArray[4],
theArray[5],
theArray[6],
},
}
AllInfos = append(AllInfos, info)
}
})
co.Visit("https://www.amazon.com/Bluetooth-FM-Transmitter-Compatible-Smartphones/dp/B088TCSH8T/ref=sr_1_1_sspa?dchild=1&keywords=transmitter&qid=1623860482&sr=8-1-spons&psc=1&smid=A2XMGHKVCX40WB&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUFGVEgxTjJLRFdLSkUmZW5jcnlwdGVkSWQ9QTAyNDE0ODkyRlNDWlAzUktPQzJSJmVuY3J5cHRlZEFkSWQ9QTA5MTkwNjgyWVUzQ0RUMTBCTEFFJndpZGdldE5hbWU9c3BfYXRmJmFjdGlvbj1jbGlja1JlZGlyZWN0JmRvTm90TG9nQ2xpY2s9dHJ1ZQ==WDY4VktWQlImd2lkZ2V0TmFtZT1zcF9kZXRhaWwmYWN0aW9uPWNsaWNrUmVkaXJlY3QmZG9Ob3RMb2dDbGljaz10cnVl")
co.OnRequest(func(r *colly.Request) {
fmt.Println("Visiting: ", r.URL.String())
})
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
enc.Encode(AllInfos)
writeJson(AllInfos)
duration := time.Since(start)
fmt.Println(duration.Seconds())
}
func writeJson(data []Info) {
dataFile, err := json.MarshalIndent(data, "", " ")
if err != nil {
log.Println("Could not create JSON", err)
}
ioutil.WriteFile("stocky.json", dataFile, 0666)
}
My code's JSON result;
[
{
"id": 0,
"images": [
"https://images-na.ssl-images-amazon.com/images/I/41EKkSQCU-L._AC_US40_.jpg",
"",
"",
"",
"",
"",
""
]
},
{
"id": 0,
"images": [
"https://images-na.ssl-images-amazon.com/images/I/41EKkSQCU-L._AC_US40_.jpg",
"https://images-na.ssl-images-amazon.com/images/I/511L3hYCClL._AC_US40_.jpg",
"",
"",
"",
"",
""
]
},
{
"id": 0,
"images": [
"https://images-na.ssl-images-amazon.com/images/I/41EKkSQCU-L._AC_US40_.jpg",
"https://images-na.ssl-images-amazon.com/images/I/511L3hYCClL._AC_US40_.jpg",
"https://images-na.ssl-images-amazon.com/images/I/51kSO5K364L._AC_US40_.jpg",
"",
"",
"",
""
]
},
{
"id": 0,
"images": [
"https://images-na.ssl-images-amazon.com/images/I/41EKkSQCU-L._AC_US40_.jpg",
"https://images-na.ssl-images-amazon.com/images/I/511L3hYCClL._AC_US40_.jpg",
"https://images-na.ssl-images-amazon.com/images/I/51kSO5K364L._AC_US40_.jpg",
"https://images-na.ssl-images-amazon.com/images/I/61NvwkbuXUL._AC_US40_.jpg",
"",
"",
""
]
},
{
"id": 0,
"images": [
"https://images-na.ssl-images-amazon.com/images/I/41EKkSQCU-L._AC_US40_.jpg",
"https://images-na.ssl-images-amazon.com/images/I/511L3hYCClL._AC_US40_.jpg",
"https://images-na.ssl-images-amazon.com/images/I/51kSO5K364L._AC_US40_.jpg",
"https://images-na.ssl-images-amazon.com/images/I/61NvwkbuXUL._AC_US40_.jpg",
"https://images-na.ssl-images-amazon.com/images/I/51hwJpj4OgL._AC_US40_.jpg",
"",
""
]
},
{
"id": 0,
"images": [
"https://images-na.ssl-images-amazon.com/images/I/41EKkSQCU-L._AC_US40_.jpg",
"https://images-na.ssl-images-amazon.com/images/I/511L3hYCClL._AC_US40_.jpg",
"https://images-na.ssl-images-amazon.com/images/I/51kSO5K364L._AC_US40_.jpg",
"https://images-na.ssl-images-amazon.com/images/I/61NvwkbuXUL._AC_US40_.jpg",
"https://images-na.ssl-images-amazon.com/images/I/51hwJpj4OgL._AC_US40_.jpg",
"https://images-na.ssl-images-amazon.com/images/I/51dz9PNWVrL._AC_US40_.jpg",
""
]
}
]
I need JSON result like this;
[
{
"id": 0,
"images": [
"https://images-na.ssl-images-amazon.com/images/I/41EKkSQCU-L._AC_US40_.jpg",
"https://images-na.ssl-images-amazon.com/images/I/511L3hYCClL._AC_US40_.jpg",
"https://images-na.ssl-images-amazon.com/images/I/51kSO5K364L._AC_US40_.jpg",
"https://images-na.ssl-images-amazon.com/images/I/61NvwkbuXUL._AC_US40_.jpg",
"https://images-na.ssl-images-amazon.com/images/I/51hwJpj4OgL._AC_US40_.jpg",
"https://images-na.ssl-images-amazon.com/images/I/51dz9PNWVrL._AC_US40_.jpg",
]
}
]
I had a hard time understanding what you wanted to do but here's what I came up with:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"strings"
"time"
"github.com/gocolly/colly"
)
type Info struct {
ID int `json:"id"`
Images []string `json:"images"`
}
func main() {
allInfos := new(Info)
start := time.Now()
co := colly.NewCollector(
colly.AllowedDomains("www.amazon.com", "amazon.com"),
)
// GET Images
co.OnHTML("img[src]", func(e *colly.HTMLElement) {
imgsrc := e.Attr("src")
imgclass := e.Attr("class")
if strings.HasPrefix(imgsrc, "https://images-na.ssl-images-amazon.com/images/I/") && imgclass == "" {
fmt.Printf("The Array %d %v", len(allInfos.Images), imgsrc+"\n")
allInfos.Images = append(allInfos.Images, imgsrc)
co.Visit(e.Request.AbsoluteURL(imgsrc))
}
})
co.Visit("https://www.amazon.com/Bluetooth-FM-Transmitter-Compatible-Smartphones/dp/B088TCSH8T/ref=sr_1_1_sspa?dchild=1&keywords=transmitter&qid=1623860482&sr=8-1-spons&psc=1&smid=A2XMGHKVCX40WB&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUFGVEgxTjJLRFdLSkUmZW5jcnlwdGVkSWQ9QTAyNDE0ODkyRlNDWlAzUktPQzJSJmVuY3J5cHRlZEFkSWQ9QTA5MTkwNjgyWVUzQ0RUMTBCTEFFJndpZGdldE5hbWU9c3BfYXRmJmFjdGlvbj1jbGlja1JlZGlyZWN0JmRvTm90TG9nQ2xpY2s9dHJ1ZQ==WDY4VktWQlImd2lkZ2V0TmFtZT1zcF9kZXRhaWwmYWN0aW9uPWNsaWNrUmVkaXJlY3QmZG9Ob3RMb2dDbGljaz10cnVl")
co.OnRequest(func(r *colly.Request) {
fmt.Println("Visiting: ", r.URL.String())
})
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
enc.Encode(allInfos)
writeJson(allInfos)
duration := time.Since(start)
fmt.Println(duration.Seconds())
}
func writeJson(data *Info) {
dataFile, err := json.MarshalIndent(data, "", " ")
if err != nil {
log.Println("Could not create JSON", err)
}
ioutil.WriteFile("stocky.json", dataFile, 0666)
}
As #Flimzy suggested, I appended only the new imagesrc to a since instance of an info struct. Seeing as it seemed like your expected output was a slice of length 1 with all the images.
I also used a strings.HasPrefix to clean up your slice check. This is equivalent but I think it's cleaner, so that's an optional change.
Let me know if you have any more questions.
EDIT, if you need to keep using a slice something like the following might work. From your question it's hard to tell when you need to clear a new Info:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"strings"
"time"
"github.com/gocolly/colly"
)
type Info struct {
ID int `json:"id"`
Images []string `json:"images"`
}
func main() {
allInfos := make([]Info, 1)
start := time.Now()
co := colly.NewCollector(
colly.AllowedDomains("www.amazon.com", "amazon.com"),
)
// GET Images
co.OnHTML("img[src]", func(e *colly.HTMLElement) {
imgsrc := e.Attr("src")
imgclass := e.Attr("class")
if strings.HasPrefix(imgsrc, "https://images-na.ssl-images-amazon.com/images/I/") && imgclass == "" {
fmt.Printf("The Array %d %v", len(allInfos[0].Images), imgsrc+"\n")
allInfos[0].Images = append(allInfos[0].Images, imgsrc)
co.Visit(e.Request.AbsoluteURL(imgsrc))
}
})
co.Visit("https://www.amazon.com/Bluetooth-FM-Transmitter-Compatible-Smartphones/dp/B088TCSH8T/ref=sr_1_1_sspa?dchild=1&keywords=transmitter&qid=1623860482&sr=8-1-spons&psc=1&smid=A2XMGHKVCX40WB&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUFGVEgxTjJLRFdLSkUmZW5jcnlwdGVkSWQ9QTAyNDE0ODkyRlNDWlAzUktPQzJSJmVuY3J5cHRlZEFkSWQ9QTA5MTkwNjgyWVUzQ0RUMTBCTEFFJndpZGdldE5hbWU9c3BfYXRmJmFjdGlvbj1jbGlja1JlZGlyZWN0JmRvTm90TG9nQ2xpY2s9dHJ1ZQ==WDY4VktWQlImd2lkZ2V0TmFtZT1zcF9kZXRhaWwmYWN0aW9uPWNsaWNrUmVkaXJlY3QmZG9Ob3RMb2dDbGljaz10cnVl")
co.OnRequest(func(r *colly.Request) {
fmt.Println("Visiting: ", r.URL.String())
})
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
enc.Encode(allInfos)
writeJson(allInfos)
duration := time.Since(start)
fmt.Println(duration.Seconds())
}
func writeJson(data []Info) {
dataFile, err := json.MarshalIndent(data, "", " ")
if err != nil {
log.Println("Could not create JSON", err)
}
ioutil.WriteFile("stocky.json", dataFile, 0666)
}

Golang unmarshaling JSON to protobuf generated structs

I would like to reqeive a JSON response withing a client application and unmarshal this response into a struct. To ensure that the struct stays the same accross all client apps using this package, I would like to define the JSON responses as protobuf messages. I am having difficulties unmarshaling the JSON to the protobuf generated structs.
I have the following JSON data:
[
{
"name": "C1",
"type": "docker"
},
{
"name": "C2",
"type": "docker"
}
]
I have modeled my protobuf definitions like this:
syntax = "proto3";
package main;
message Container {
string name = 1;
string type = 2;
}
message Containers {
repeated Container containers = 1;
}
Using this pattern with structs normaly works, but for some reason using these proto definitions causes issues. The below code demonstrates a working and a non-working example. Although one of the versions work, I am unable to use this solution, since []*Container does not satisfy the proto.Message interface.
package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/gogo/protobuf/jsonpb"
)
func working(data string) ([]*Container, error) {
var cs []*Container
return cs, json.Unmarshal([]byte(data), &cs)
}
func notWorking(data string) (*Containers, error) {
c := &Containers{}
jsm := jsonpb.Unmarshaler{}
if err := jsm.Unmarshal(strings.NewReader(data), c); err != nil {
return nil, err
}
return c, nil
}
func main() {
data := `
[
{
"name": "C1",
"type": "docker"
},
{
"name": "C2",
"type": "docker"
}
]`
w, err := working(data)
if err != nil {
panic(err)
}
fmt.Print(w)
nw, err := notWorking(data)
if err != nil {
panic(err)
}
fmt.Print(nw.Containers)
}
Running this gives the following output:
[name:"C1" type:"docker" name:"C2" type:"docker" ]
panic: json: cannot unmarshal array into Go value of type map[string]json.RawMessage
goroutine 1 [running]:
main.main()
/Users/example/go/src/github.com/example/example/main.go:46 +0x1ee
Process finished with exit code 2
Is there a way to unmarshal this JSON to Containers? Or alternatively, make []*Container to satisfy the proto.Message interface?
For the message Containers, i.e.
message Containers {
repeated Container containers = 1;
}
The correct JSON should look like:
{
"containers" : [
{
"name": "C1",
"type": "docker"
},
{
"name": "C2",
"type": "docker"
}
]
}
If you cannot change the JSON then you can utilize the func that you've created
func working(data string) ([]*Container, error) {
var cs []*Container
err := json.Unmarshal([]byte(data), &cs)
// handle the error here
return &Containers{
containers: cs,
}, nil
}
You should use NewDecoder to transfer the data to jsonDecoder and then traverse
the array.The code is this
func main() {
data := `
[
{
"name": "C1",
"type": "docker"
},
{
"name": "C2",
"type": "docker"
}
]`
jsonDecoder := json.NewDecoder(strings.NewReader(data))
_, err := jsonDecoder.Token()
if err != nil {
log.Fatal(err)
}
var protoMessages []*pb.Container
for jsonDecoder.More() {
protoMessage := pb.Container{}
err := jsonpb.UnmarshalNext(jsonDecoder, &protoMessage)
if err != nil {
log.Fatal(err)
}
protoMessages = append(protoMessages, &protoMessage)
}
fmt.Println("%s", protoMessages)
}

How to connect JSON that is unmarshaled to an HTML page using golang?

I've reached the point where I have unmarshaled my JSON and I can print the data directly from the registry.go file. What I don't understand (and I'm new to golang and backend dev) is how to connect that data to my html page. I would very much appreciate if someone could explain to me how to do this.
I have seen some examples, but they are all in one file, I want to keep the JSON, HTML, and Go pages separate. The JSON is being pulled directly from an API, and there will be multiple HTML pages using that data.
Here is an example, very close to the JSON I'm using, it is being pulled from an API:
{
"count": 4,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"description": "some text",
"displayName": "some more text",
"status": {
"status": "UP",
"lastUpdate": "2020-07-21T12:42:24.968647Z"
},
"type": "test"
},
{
"id": 2,
"description": "some text",
"displayName": "some more text",
"status": {
"status": "UP",
"lastUpdate": "2020-07-21T12:42:24.968647Z"
},
"type": "test"
},
{
"id": 3,
"description": "some text",
"displayName": "some more text",
"status": {
"status": "UP",
"lastUpdate": "2020-07-21T12:42:24.968647Z"
},
"type": "test"
},
{
"id": 4,
"description": "some text",
"displayName": "some more text",
"status": {
"status": "UP",
"lastUpdate": "2020-07-21T12:42:24.968647Z"
},
"type": "test"
}
]
}
Here is the registry.go page I am using. I have a separate main.go page as I have other pages that have been created. I am trying to keep this piece separate, but if I cannot please tell me.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
)
// Results struct which contains
// an array of results
type Results struct {
Count int `json:"count"`
Results []Result `json:"results"`
}
// Results struct which contains the ID and description
type Result struct {
ID int `json:"id"`
Description string `json:"description"`
DisplayName string `json:"displayName"`
Status Status `json:"status"`
Type string `json:"type"`
}
// Status struct
type Result struct {
Status string `json:"status"`
LastUpdated string `json:"lastUpdated"`
}
func main() {
// Open the jsonFile
jsonFile, err := os.Open("test.json")
// if os.Open returns an error then handle it
if err != nil {
fmt.Println(err)
}
fmt.Println("Successfully Opened test.json")
// defer the closing of the jsonFile in order to parse it later on
defer jsonFile.Close()
// read the opened xmlFile as a byte array.
byteValue, _ := ioutil.ReadAll(jsonFile)
// initialize the results array
var results Results
// unmarshal the byteArray
json.Unmarshal(byteValue, &results)
// Print the count
fmt.Println(results.count)
// iterate through the results and print
for i := 0; i < len(users.Users); i++ {
fmt.Println("ID: " + results.Results[i].ID)
fmt.Println("Desctiption: " + results.Results[i].Description)
}
}
And here is the html page I have created (test.html), just showing the body piece:
<body>
{{range .}}
<div>
<h3>{{.DisplayName}}</h3>
</div>
<div>{{.Id}}</div>
<div>{{.Description}}</div>
<div>{{.Type}}</div>
{{end}}
</body>
In Main.go I have the following references:
//handler to display the page
http.HandleFunc("/test", hndlerTest)
//hndlerTest - the Testing Page
func hndlerTest(w http.ResponseWriter, req *http.Request){
renderTemplate(w,"test.html", nil)
}
You can use the html template package to parse and render html filed and write the executed template to your http response. You can refer to the following example to start with.
package main
import (
"encoding/json"
"html/template"
"log"
"net/http"
)
var htmlFile = `
<body>
{{range .Foos}}
<span>Foo: {{.Foo}}</span>
{{end}}
</body>
`
var jsonData = `[{"foo":1},{"foo":2},{"foo":3}]`
type foo struct {
Foo int `json:"foo"`
}
type Data struct {
Foos []foo
}
func main() {
var d Data
if err := json.Unmarshal([]byte(jsonData), &d.Foos); err != nil {
panic(err)
}
http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
// Use template.New("index").ParseFiles("index.html") for files
templ, err := template.New("index").Parse(htmlFile)
if err != nil {
panic(err)
}
if err := templ.Execute(rw, d); err != nil {
panic(err)
}
})
log.Panic(http.ListenAndServe(":3030", nil))
}

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)
}
}