Encoding struct to json Go - json

I have an issue encoding struct to json my code is
type MainStructure struct {
Text string json:"text,omitempty"
Array []TestArray json:"test_array,omitmepty"
}
type TestArray struct {
ArrayText string json:"array_text,omitempty"
}
func main() {
Test := MainStructure{
Text: "test",
Array: [
{
ArrayText: "test1"
},
{
ArrayText: "test2"
}
]
}
body := new(bytes.Buffer)
json.NewEncoder(body).Encode(&Test)
fmt.Println(string([]byte(body)))
}
the wanted result should be
{
"text": "test",
"test_array": [
{
"array_text": "test1"
},
{
"array_text": "test2"
}
]
}
I do not mind if it's "Marshal" or "encoding/json"

To encode a struct to JSON string, there are three ways that provided by standard library :
Using Encoder which convert struct to JSON string, then write it to io.Writer. This one usually used if you want to send JSON data as HTTP request, or saving the JSON string to a file.
Using Marshal which simply convert struct to bytes, which can be easily converted to string.
Using MarshalIndent which works like Marshal, except it's also prettify the output. This is what you want for your problem right now.
To compare between those three methods, you can use this code (Go Playground):
package main
import (
"bytes"
"encoding/json"
"fmt"
)
type MainStructure struct {
Text string `json:"text,omitempty"`
Array []TestArray `json:"test_array,omitempty"`
}
type TestArray struct {
ArrayText string `json:"array_text,omitempty"`
}
func main() {
Test := MainStructure{
Text: "test",
Array: []TestArray{
{ArrayText: "test1"},
{ArrayText: "test2"},
},
}
// Using marshal indent
btResult, _ := json.MarshalIndent(&Test, "", " ")
fmt.Println("Using Marshal Indent:\n" + string(btResult))
// Using marshal
btResult, _ = json.Marshal(&Test)
fmt.Println("\nUsing Marshal:\n" + string(btResult))
// Using encoder
var buffer bytes.Buffer
json.NewEncoder(&buffer).Encode(&Test)
fmt.Println("\nUsing Encoder:\n" + buffer.String())
}
The output will look like this :
Using Marshal Indent:
{
"text": "test",
"test_array": [
{
"array_text": "test1"
},
{
"array_text": "test2"
}
]
}
Using Marshal:
{"text":"test","test_array":[{"array_text":"test1"},{"array_text":"test2"}]}
Using Encoder:
{"text":"test","test_array":[{"array_text":"test1"},{"array_text":"test2"}]}

First of all I think you are going wrong on how to create a struct in go, as you can easily convert them to json.
You should be first making a proper struct then do json.marshal(Test) to convert it to proper json like:
package main
import (
"encoding/json"
"fmt"
)
func main() {
type TestArray struct {
ArrayText string `json:"array_text,omitempty"`
}
type MainStructure struct {
Text string `json:"text,omitempty"`
Array []TestArray `json:"test_array,omitmepty"`
}
Test := MainStructure{
Text: "test",
Array: []TestArray{
TestArray{ArrayText: "test1"},
TestArray{ArrayText: "test2"},
}}
bytes, err := json.Marshal(Test)
if err != nil {
fmt.Println("eror marshalling")
} else {
fmt.Println(string(bytes))
}
}
Check this out on play.golang.org

I could not get the point you wanted to use bytes.Buffer if your objective is just to put the result into the console. Assuming the point is:
Create a struct instance (corresponding to a JSON object)
Emit it in the screen
The following code can help you:
package main
import "encoding/json"
import "fmt"
type MainStructure struct {
Text string `json:"text,omitempty"`
Array []TestArray `json:"test_array,omitmepty"`
}
type TestArray struct {
ArrayText string `json:"array_text,omitempty"`
}
func main() {
Test := MainStructure{
Text: "test",
Array: []TestArray{
TestArray{"test1"},
TestArray{"test2"},
},
}
res, _ := json.MarshalIndent(Test, "", "\t")
fmt.Println(string(res))
}
json.MarshalIndent is used to make result pretty-formatted, if you bother it.

Related

Trying to get 2 integers out of a JSON subsection

I'm using the VirusTotal HTTP API in Golang to get the votes on an URL, both malicious and harmless.
I want to get them using structs, and then unmarshaling the URL using data from these structs. But when I try that, this error shows up:
cannot convert "harmless" (untyped string constant) to int
The code I am currently using is:
type Votes struct {
harmless int
malicious int
}
type Attributes struct {
votes []Votes `json:"total_votes"`
}
type data struct {
attributes []Attributes `json:"attributes"`
}
type jsonstruct struct {
data []data `json:"data"`
}
var printdata jsonstruct
fmt.Println(resp)
json.Unmarshal([]byte(body), &printdata)
fmt.Println(printdata.data[0].attributes[0].votes["harmless"])
And the part of the JSON that I want to get is:
{
"data": {
"attributes": {
"last_modification_date": 1642534694,
"last_http_response_cookies": {
"1P_JAR": "2022-01-18-19",
"NID": "511=drq8-0Gwl0gpw2D-iyZhxrizpE--UMOyc_bO381XXkxknypvl_IETvsxRw3p8kMlBtiYEuSbASKK1wHirmgxce79kgzGMg9MryT0PnHox6kWbmEQTe2vsv_HtVZDFDXiLt4HKpcyDczOT8c_OK8bPb_P-f8rbIXJu_xrA0Ce4lw",
"SameSite": "none"
},
"times_submitted": 82289,
"total_votes": {
"harmless": 54,
"malicious": 18
},
As you can see, I want to get the contents of the subsection total_votes, which are integers harmless and malicious. In short, how can I get them without getting the error about them being untyped strings?
You need to define valid data structure that matches json structure. And after Unmarshal you can access votes fields as res.Data.Attributes.TotalVotes.Harmless . For example (https://go.dev/play/p/YCtV1u-KF7Y):
package main
import (
"encoding/json"
"fmt"
"log"
)
type Result struct {
Data struct {
Attributes struct {
LastHTTPResponseCookies struct {
OnePJAR string `json:"1P_JAR"`
Nid string `json:"NID"`
SameSite string `json:"SameSite"`
} `json:"last_http_response_cookies"`
LastModificationDate float64 `json:"last_modification_date"`
TimesSubmitted float64 `json:"times_submitted"`
TotalVotes struct {
Harmless float64 `json:"harmless"`
Malicious float64 `json:"malicious"`
} `json:"total_votes"`
} `json:"attributes"`
} `json:"data"`
}
var input = `{
"data": {
"attributes": {
"last_modification_date": 1642534694,
"last_http_response_cookies": {
"1P_JAR": "2022-01-18-19",
"NID": "pcyDczOT8c_OK8bPb_P-f8rbIXJu_xrA0Ce4lw",
"SameSite": "none"
},
"times_submitted": 82289,
"total_votes": {
"harmless": 54,
"malicious": 18
}
}
}
}
`
func main() {
var res Result
if err := json.Unmarshal([]byte(input), &res); err != nil {
log.Fatal(err)
}
fmt.Printf("%+v", res.Data.Attributes.TotalVotes.Harmless)
}

json unmarshal in gloang for complex json data

i want to json unmarshal following data in golang :
{
RESPONSE : {
CODE : "123"
NEW_RESPONSE :{
0:[
{
key1:val1,
key2:val2
},
{
key3:val3,
key4:val4
}
]
1:[
{
key5:val5,
key6:val6,
key7:val7
},
{
key31:val31,
key42:val42
}
]
2:{
key8:val8,
key9:val9,
key1-:val10
}
3:{}
}
}
}
I want to access every key of this data. What i was trying is
type testData struct{
Code string `json:"CODE"`
NewResponse map[string]interface{} `json:"NEW_RESPONSE"`}
type GetData struct{
TestResponse testData `json:"RESPONSE"`}
After this I am not able to furthur use "NewResponse". Need help. Thanks in advance.
You can access elements of NewResponse using the key:
elem:=NewResponse["0"]
Looking at the input document, elem is an array of objects. The rest of the code will use type assertions:
if arr, ok:=elem.([]interface{}); ok {
// arr is a JSON array
objElem:=arr[0].(map[string]interface{})
for key,value:=range objElem {
// key: "key1"
// value: "val1"
val:=value.(string)
...
}
} else if obj, ok:=elem.(map[string]interface{}); ok {
// obj is a JSON object
for key, val:=range obj {
// key: "key1"
value:=val.(string)
}
}
Plz validate your json format
this may solve your purpose.
package main
import (
"encoding/json"
"fmt"
)
func main() {
Json := `{
"RESPONSE" : {
"CODE" : "123",
"NEW_RESPONSE" :{
"0":{
"s" : 1,
"s1" :2,
"s3": 3
}
}
}
}`
// Declared an empty interface
var result map[string]interface{}
// Unmarshal or Decode the JSON to the interface.
err := json.Unmarshal([]byte(Json), &result)
if err != nil{
fmt.Println("Err : ",err)
}else{
fmt.Println(result)
}
}

How to unmarshal JSON array of different datatypes?

Part of the JSON I'm trying to unmarshal has an array that can contain either strings or ints. How would I go about parsing this?
{
"id": "abc",
"values": [1,2,3]
},
{
"id": "def",
"values": ["elephant", "tomato", "arrow"]
},
{
//etc...
}
I tried the following:
type Thing struct {
ID string `json:"id"`
Values []string `json:"values,string,omitempty"`
}
Get the following error:
panic: json: cannot unmarshal array into Go struct field Thing.values of type string
You can set the type of the field Values to []interface{}, but you might have to use some type assertions.
Here is a link to a playground where I use the data you provided: https://play.golang.org/p/ahePgggr8o1
I will paste the code here anyways:
package main
import (
"fmt"
"encoding/json"
)
type Thing struct {
ID string `json:"id"`
Values []interface{} `json:"values,omitempty"`
}
func print(values []interface{}) {
for _, v := range values{
switch value := v.(type){
case string:
fmt.Println("String", value)
case int:
fmt.Println("Integer", value)
case float64:
fmt.Println("Floats", value)
default:
fmt.Printf("I don't know about type %T!\n", v)
}
}
}
func main() {
var t1, t2 Thing
data := []byte("{ \"id\": \"abc\", \"values\": [1,2,3]}")
data2 := []byte("{ \"id\": \"def\", \"values\": [\"elephant\", \"tomato\", \"arrow\"]}")
_ = json.Unmarshal(data, &t1)
_ = json.Unmarshal(data2, &t2)
print(t1.Values)
print(t2.Values)
}

JSON: Nesting a populated struct into a new struct

I have a struct like so:
type my_struct struct {
First string `json:"first"`
Second string `json:"second"`
Number int `json:"number"`
}
When I marshal that into JSON, it outputs very simple JSON as you'd expect:
var output_json []byte
output_json, _ = json.Marshal(output)
fmt.Println(string(output_json))
Result:
{"first":"my_string","second":"another_string","number":2}
All fine so far!
What I'd like to do, before marshalling that struct into JSON, is nest it inside another struct. The resulting output would be JSON that looks like this:
{
"fields": {
"first": "my_string",
"number": 2,
"second": "another_string"
},
"meta": "data"
}
How can I do that?
I think you could statically declare another struct to use:
type WrappedOutput struct {
fields my_struct
meta string
}
Then you could embed your struct before marshalling
o := WrappedOutput{fields: output}
Brand new to go so not sure if this is the easiest way to do it
The clean way to do this would be to declare 2 structs (I've done it globally below) and nest My_struct inside the higher level struct.
Then you can initialize the higher level struct before Mashalling it:
package main
import (
"encoding/json"
"fmt"
)
type My_struct struct {
First string `json:"first"`
Second string `json:"second"`
Number int `json:"number"`
}
type Big_struct struct {
Fields My_struct
Meta string
}
func main() {
output := Big_struct{
Fields: My_struct{
First: "my_string",
Second: "another_string",
Number: 2,
},
Meta: "data",
}
output_json, err := json.Marshal(output)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(output_json))
}
if you don't want Big_struct you can declare an anonymous struct within your code as you need it and nest My_struct inside:
package main
import (
"encoding/json"
"fmt"
)
type My_struct struct {
First string `json:"first"`
Second string `json: "second"`
Number int `json:"number"`
}
func main() {
output := struct {
Fields My_struct
Meta string
}{
Fields: My_struct{
First: "my_string",
Second: "another_string",
Number: 2,
},
Meta: "data",
}
output_json, err := json.Marshal(output)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(output_json))
}
If you don't want to use a new structure, you can do:
data := my_struct{First: "first", Second: "2", Number: 123}
result, _ := json.Marshal(&map[string]interface{}{"fields":data,"meta":"data"})
fmt.Println(string(result))
it's not clean, but it does the work.

How to unmarshal two json with same internal structure into one single golang struct?

I have two json files with following structure
{
"cast": [
{
"url": "carey-mulligan",
"name": "Carey Mulligan",
"role": "Actress"
},
{
"url": "leonardo-dicaprio",
"name": "Leonardo DiCaprio",
"role": "Actor"
},
.
.
.
]
}
and
{
"movie": [
{
"url": "carey-mulligan",
"name": "Carey Mulligan",
"role": "Actress"
},
{
"url": "leonardo-dicaprio",
"name": "Leonardo DiCaprio",
"role": "Actor"
},
.
.
.
]
}
as you can see internal structure of the json is same for cast and movie. I want to unmarshel these json file into the same golang structure. But i am not able to give two name tags (cast and movie) for same struct element. I want something like
type Detail struct {
Name string `json:"name"`
Url string `json:"url"`
Role string `json:"role"`
}
type Info struct {
Detail []Detail `json:"cast or movie"`
}
In which case Detail could parse both cast and movie.
Here is my current code
// RIMAGE project main.go
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
)
const (
website = "https://data.moviebuff.com/"
)
func main() {
fmt.Println("Hello World!")
content, err := ioutil.ReadFile("data/great-getsby")
if err != nil {
panic(err)
}
var info Info
err = json.Unmarshal(content, &info)
if err != nil {
panic(err)
}
fmt.Println(info.Detail)
}
type Detail struct {
Name string `json:"name"`
Url string `json:"url"`
Role string `json:"role"`
}
type Info struct {
Detail []Detail `json:"cast" json:"movie"
}
but it only works for first tag "cast" and gives nill in case json contain the movie.
Thanks in advance.
You can use type Info map[string][]Detail instead of your struct.
Try it on the Go playground
Or you can use both types in your structure, and make method Details() which will return right one:
type Info struct {
CastDetails []Detail `json:"cast"`
MovieDetails []Detail `json:"movie"`
}
func (i Info) Details() []Detail {
if i.CastDetails == nil {
return i.MovieDetails
}
return i.CastDetails
}
Try it on the Go playground
Try anonymous field in struct:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
)
type Detail struct {
Name string `json:"name"`
Url string `json:"url"`
Role string `json:"role"`
}
type Cast struct {
Detail []Detail `json:"cast"`
}
type Movie struct {
Detail []Detail `json:"movie"`
}
type Info struct {
Cast
Movie
}
func (i *Info) getDetails() []Detail {
if len(i.Cast.Detail) > 0 {
return i.Cast.Detail
}
return i.Movie.Detail
}
func main() {
cast, _ := ioutil.ReadFile("./cast.json")
movie, _ := ioutil.ReadFile("./movie.json")
var cInfo Info
err := json.Unmarshal(cast, &cInfo)
fmt.Printf("cast: %+v\n", &cInfo)
fmt.Printf("err: %v\n", err)
fmt.Printf("details: %v\n", cInfo.getDetails())
var mInfo Info
err = json.Unmarshal(movie, &mInfo)
fmt.Printf("movie: %+v\n", &mInfo)
fmt.Printf("err: %v\n", err)
fmt.Printf("details: %v\n", mInfo.getDetails())
}
Things to note:
One more level of indirection: to access 'Details' field, you need to access either 'Cast' or 'Movie' field first in Info first.
Better provide an access function for 'Details' ('getDetail' in this example)
If you dig far enough down into encoding/json you'll get to https://github.com/golang/go/blob/master/src/encoding/json/encode.go and the following:
tag := sf.Tag.Get("json")
if tag == "-" {
continue
}
So it gets one json tag and keeps on going.
You could always go with RoninDev's solution and just copy it over when done.