golang - fomatting struct to json - json

Does anyone know how to set the tag names for multilevel structs?
The top level tag-names of the struct works ok, but all sublevels tag names have the same name as in the struct. Trying to set all tag-name to lowercase.
The code can be run here:
package main
import (
"encoding/json"
"log"
)
type Source struct {
Pointer string `json:pointer,omitempty"`
Parameter string `json:parameter,omitempty"`
}
type Error struct {
Status int `json:"status,omitempty"`
Source *Source `json:"source,omitempty"`
Title string `json:"title,omitempty"`
Detail string `json:"detail,omitempty"`
}
type Errors struct {
Errors *[]Error `json:"errors"`
}
func main() {
errors := new(Errors)
errors.Errors = new([]Error)
error := new(Error)
error.Source = new(Source)
error.Source.Pointer = "pointer"
error.Status = 401
error.Title = "title"
error.Detail = "detail"
*errors.Errors = append(*(errors.Errors), *error)
response, _ := json.Marshal(errors)
log.Println("response", string(response))
}
Output:
{
"errors": [
{
"status": 400,
"source": {
"Pointer": "pointer",
"Parameter": ""
},
"title": "title",
"detail": "detail"
}
]
}

You've missed a few quotes:
Pointer string `json:pointer,omitempty"`
Parameter string `json:parameter,omitempty"`
// ^^^ Here.
Playground: https://play.golang.org/p/P3oHK29VKQ.

Related

removing \n \r\n from json in go lang

The program reads the file from input json,then appends extra structure within the program. I have everything working but when i printout in my console it has extra n\ r and spaces in the json. In the playground it doesnt but when i run it from shell it does. The problem how can i removed to match the outcome file.
File thats getting inputted:
{
"mac_address": "",
"serial_number": "4UW1234",
"device_type": "STORuhaiul",
"device_model": "N9ZsdfsdA",
"part_number": "N9sdfsdfsdf5A",
"extra_attributes": [
{
"attribute_type": "ENTITLEMENT_ID",
"attribute_value": "ABC123JKJUBCDZ"
}
],
"platform_customer_id": "f850243a4c1911eca07f6db9a",
"application_customer_id": "88e0ff88c07811ed76c7988eb"
}
Outcome file should be like this exactly quoted(result)
{
"specversion": "1.0",
"id": "ce20b92d-b45b7-a544-8fe4d8f7ff06",
"source": "CCS",
"type": "",
"time": "2022-08-09T23:59:08.468903+00:00",
"data": "{\"mac_address\": null, \"serial_number\": \"ADL-DHCI1\", \"device_type\": \"DHCI_STORAGE\", \"device_model\": \"NIMBLE DHCI STORAGE\", \"part_number\": \"60\", \"extra_attributes\": [
{\"attribute_type\": \"ENTITLEMENT_ID\", \"attribute_value\": \"YIZUYYNEYV0LVAU\"}
], \"tag_entities\": [], \"platform_customer_id\": \"a07db1081d5b41eeed59645ee95\", \"application_customer_id\": \"3118643e201cb2c92e8b7aa93178\", \"folder_name\": null}"
}
my code
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"time"
"github.com/google/uuid"
)
type Event struct {
Specversion string `json:"specversion"`
ID uuid.UUID `json:"id"` /// uuid
Source string `json:"source"`
Type string `json:"type"`
Time time.Time `json:"time"`
Data string `json:"data"` /// this part supposed to come from the file
}
type Data struct {
MacAddress string `json:"mac_address"`
SerialNumber string `json:"serial_number"`
DeviceType string `json:"device_type"`
DeviceModel string `json:"device_model"`
PartNumber string `json:"part_number"`
ExtraAttributes []DeviceAttribute `json:"extra_attributes"`
PlatformCustomerID string `json:"platform_customer_id"`
ApplicationCustomerID string `json:"application_customer_id"`
}
type DeviceAttribute struct {
AttributeType string `json:"attribute_type"`
AttributeValue string `json:"attribute_value"`
}
func main() {
if len(os.Args) < 1 { //checking for provided argument if the first arg satisfy then use read file
fmt.Println("Usage : " + os.Args[0] + " file name")
help()
os.Exit(1)
}
Data, err := ioutil.ReadFile(os.Args[1])
if err != nil {
fmt.Println("Cannot read the file or file does not exists")
os.Exit(1)
}
e := Event{
Specversion: "1.0",
ID: uuid.New(),
Source: "CSS",
Type: "", // user input -p or -u,
Time: time.Now(),
Data: string(Data),
}
out, _ := json.Marshal(e)
fmt.Println(string(out))
}
func help() {
fmt.Println("help")
}
When im running the program im getting this output from vs code
{"specversion":"1.0","id":"c053519-71ef-4a80-a47b-392ac2434f99","source":"CSS","type":"","time":"2022-09-09T14:01:40.2599627-05:00","data":"{\r\n \"mac_address\": \"\",\r\n \"serial_number\": \"4UW1234\",\r\n \"device_type\": \"STOuhaul\",\r\n \"device_model\": \"N95A\",\r\n \"part_number\": \"N9Z55A\",\r\n \"extra_attributes\": [\r\n {\r\n \"attribute_type\": \"ENTITLEMENT_ID\",\r\n \"attribute_value\": \"ABC123JV9UBCDZ\"\r\n }\r\n ],\r\n \"platform_cust.....
output from shell
{"specversion":"1.0","id":"5f5b1841-d99c-4eef-8867-7c09a23d5bb5","source":"CSS","type":"","time":"2022-09-09T19:34:43.802856511Z","data":" \"mac_address\": \"\",\n \"serial_number\": \"j\",\n \"device_type\": \"STORAGE\",\n \"device_model\": \"VIRTUAL\",\n \"part_number\": \"VIRTUAL\",\n \"extra_attributes\": [\n {\n \"attribute_type\": \"ENTITLEMENT_ID\",\n \"attribute_value\": \"H6MI9JYNJCOSBL1GTjfgjfjY6P\"\n }\n ],\n \"platform_customer_id\": \"b050fa3a49hfghfghfbbf0a2a9c6414efb\",\n \"application_customer_id\": \"03eda3027f4c11ec8cee12749a9c9018\"\n\n"}
go playcode
Use the following code to remove carriage return and line feed from json:
replacer := strings.NewReplacer("\r", "", "\n", "")
Data = []byte(replacer.Replace(string(Data)))
https://go.dev/play/p/0bDpGqybskL

Encoding struct to json Go

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.

Unmarshal JSON in go with different types in a list

I have trouble unmarschaling a JSON contruct:
{
"id": 10,
"result": [
{
"bundled": true,
"type": "RM-J1100"
},
[
{
"name": "PowerOff",
"value": "AAAAAQAAAAEAAAAvAw=="
},
{
"name": "Input",
"value": "AAAAAQAAAAEAAAAlAw=="
}
]
]
}
I actually need the second slice item from the result.
My current attempt is
type Codes struct {
Id int32 `json:"id"`
Result []interface{} `json:"result"`
}
type ResultList struct {
Info InfoMap
Codes []Code
}
type InfoMap struct {
Bundled bool `json:"bundled"`
Type string `json:"type"`
}
type Code struct {
Name string `json:"name"`
Value string `json:"value"`
}
the output is like:
{10 {{false } []}}
but I also tried to use this:
type Codes struct {
Id int32 `json:"id"`
Result []interface{} `json:"result"`
}
the output is okay:
{10 [map[type:RM-J1100 bundled:true] [map[name:PowerOff value:AAAAAQAAAAEAAAAvAw==] map[name:Input value:AAAAAQAAAAEAAAAlAw==]]]}
I can also reference the Result[1] index:
[map[name:PowerOff value:AAAAAQAAAAEAAAAvAw==] map[name:Input value:AAAAAQAAAAEAAAAlAw==]]
But I fail to convert the interface type to any other Type which would match. Can anybody tell me how to do interface conversion. And what approach would be the "best".
One option would be to unmarshal the top level thing into a slice of json.RawMessage initially.
Then loop through the members, and look at the first character of each one. If it is an object, unmarshal that into your InfoMap header struct, and if it is an array, unmarshal it into a slice of the Code struct.
Or if it is predictable enough, just unmarshal the first member to the one struct and the second to a slice.
I made a playground example of this approach.
type Response struct {
ID int `json:"id"`
RawResult []json.RawMessage `json:"result"`
Header *Header `json:"-"`
Values []*Value `json:"-"`
}
type Header struct {
Bundled bool `json:"bundled"`
Type string `json:"type"`
}
type Value struct {
Name string `json:"name"`
Value string `json:"value"`
}
func main() {
//error checks ommitted
resp := &Response{}
json.Unmarshal(rawJ, resp)
resp.Header = &Header{}
json.Unmarshal(resp.RawResult[0], resp.Header)
resp.Values = []*Value{}
json.Unmarshal(resp.RawResult[1], &resp.Values)
}
(I will not point out how horrific it is this JSON struct, but as always: sXXt happens)
You can convert your struct like this, by using a cycle of JSON Marshal / Unmarshal. Code follows:
package main
import (
"encoding/json"
"log"
)
const (
inputJSON = `{
"id": 10,
"result": [
{
"bundled": true,
"type": "RM-J1100"
},
[
{
"name": "PowerOff",
"value": "AAAAAQAAAAEAAAAvAw=="
},
{
"name": "Input",
"value": "AAAAAQAAAAEAAAAlAw=="
}
]
]
}`
)
type Codes struct {
Id int32 `json:"id"`
Result [2]interface{} `json:"result"`
}
type Result struct {
Info InfoMap
Codes []Code
}
type InfoMap struct {
Bundled bool `json:"bundled"`
Type string `json:"type"`
}
type Code struct {
Name string `json:"name"`
Value string `json:"value"`
}
func main() {
newCodes := &Codes{}
err := json.Unmarshal([]byte(inputJSON), newCodes)
if err != nil {
log.Fatal(err)
}
// Prints the whole object
log.Println(newCodes)
// Prints the Result array (!)
log.Println(newCodes.Result)
if len(newCodes.Result) != 2 {
log.Fatal("Invalid Result struct")
}
// Marshal and Unmarshal data to obtain the code list
byteCodeList, _ := json.Marshal(newCodes.Result[1])
codeList := make([]Code, 0)
err = json.Unmarshal(byteCodeList, &codeList)
if err != nil {
log.Fatal("Invalid Code list")
}
// Prints the codeList
log.Println(codeList)
}
Test it on playground.

Manually read JSON values

In Go I usually unmarshal my JSON into a struct and read values from the struct.. it works very well.
This time around I am only concerned with a certain element of a JSON object and because the overall JSON object is very large, I don't want to have to create a struct.
Is there a way in Go so that I can lookup values in the JSON object using keys or iterate arrays as per usual.
Considering the following JSON how could I pull out the title field only.
{
"title": "Found a bug",
"body": "I'm having a problem with this.",
"assignee": "octocat",
"milestone": 1,
"labels": [
"bug"
]
}
Dont declare fields you dont want.
https://play.golang.org/p/cQeMkUCyFy
package main
import (
"fmt"
"encoding/json"
)
type Struct struct {
Title string `json:"title"`
}
func main() {
test := `{
"title": "Found a bug",
"body": "I'm having a problem with this.",
"assignee": "octocat",
"milestone": 1,
"labels": [
"bug"
]
}`
var s Struct
json.Unmarshal([]byte(test), &s)
fmt.Printf("%#v", s)
}
Or if you want to completely get rid of structs:
var i interface{}
json.Unmarshal([]byte(test), &i)
fmt.Printf("%#v\n", i)
fmt.Println(i.(map[string]interface{})["title"].(string))
Or. It will swallow all conversion errors.
m := make(map[string]string)
json.Unmarshal([]byte(test), interface{}(&m))
fmt.Printf("%#v\n", m)
fmt.Println(m["title"])
To extend Darigaaz's answer, you can also use an anonymous struct declared within the parsing function. This avoids having to have package-level type declarations litter the code for single-use cases.
https://play.golang.org/p/MkOo1KNVbs
package main
import (
"encoding/json"
"fmt"
)
func main() {
test := `{
"title": "Found a bug",
"body": "I'm having a problem with this.",
"assignee": "octocat",
"milestone": 1,
"labels": [
"bug"
]
}`
var s struct {
Title string `json:"title"`
}
json.Unmarshal([]byte(test), &s)
fmt.Printf("%#v", s)
}
Check out go-simplejson
Example:
js, err := simplejson.NewJson([]byte(`{
"test": {
"string_array": ["asdf", "ghjk", "zxcv"],
"string_array_null": ["abc", null, "efg"],
"array": [1, "2", 3],
"arraywithsubs": [{"subkeyone": 1},
{"subkeytwo": 2, "subkeythree": 3}],
"int": 10,
"float": 5.150,
"string": "simplejson",
"bool": true,
"sub_obj": {"a": 1}
}
}`))
if _, ok = js.CheckGet("test"); !ok {
// Missing test struct
}
aws := js.Get("test").Get("arraywithsubs")
aws.GetIndex(0)
package main
import (
"encoding/json"
"fmt"
)
type Seller struct {
Name string
ShareName string
Holdings int
Quantity int
PerShare float64
}
type Buyer struct {
Name string
ShareName string
Holdings int
Quantity int
PerShare float64
}
func validateTransaction(seller Seller,buyer Buyer) bool{
var isallowTransact bool =false
if (seller.Quantity >=buyer.Quantity &&seller.PerShare == buyer.PerShare && seller.ShareName ==buyer.ShareName){
isallowTransact=true;
}
return isallowTransact
}
func executeTransaction(seller Seller,buyer Buyer) {
seller.Holdings -=seller.Quantity;
buyer.Holdings +=seller.Quantity;
fmt.Printf("seller current holding : %d, \n buyyer current holding: %d",seller.Holdings,buyer.Holdings)
}
func main() {
sellerJson :=`{"name":"pavan","ShareName":"TCS","holdings":100,"quantity":30,"perShare":11.11}`
buyerJson :=`{"name":"Raju","ShareName":"TCS","holdings":0,"quantity":30,"perShare":14.11}`
var seller Seller
var buyer Buyer
json.Unmarshal([]byte(sellerJson ), &seller)
json.Unmarshal([]byte(buyerJson ), &buyer)
//fmt.Printf("seller name : %s, shares of firm: %s,total holdings: %d, want to sell: %d,unit cost: %f", seller.Name , seller.ShareName,seller.Holdings , seller.Quantity,seller.PerShare )
var isallowExecute bool =false
isallowExecute =validateTransaction(seller,buyer)
if(isallowExecute){
executeTransaction(seller,buyer)
}else{
fmt.Print("\n seems buyer quotes are not matching with seller so we are not able to perform transacrion ,Please check and try again");
}
fmt.Println("\n Happy Trading...!!");
}
Update: This answer is wrong; I'm leaving it as an example of what not to do.
You should look it up with a regex then (pseudocode):
String s = {json source};
int i = s.indexOf("\"title\": \"")
s.substring(i,s.indexOf("\"",i))
more on substrings in Java

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.