Update data from incoming json request with existing json format using golangg - json

I have some json data coming from Postman and I want to update it in existing json file (update only required field not all fields) using Golang.
I created IP and port and open channel between me and postman I need to understand how update required element not all element(update name and phone inside authenticator) using Golang.
type he
type UserType struct {
User []struct {
CdbID string `json:"cdb_id"`
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
Phone int64 `json:"phone"`
Email string `json:"email"`
Address []struct {
Street string `json:"street"`
City string `json:"city"`
Zip string `json:"zip"`
Country string `json:"country"`
} `json:"address"`
Authenticators []Authenticator `json:"authenticators"`
VoiceSig string `json:"voice_sig"`
VoicesigCreatedTime string `json:"voicesig_created_time"`
Status string `json:"status"`
} `json:"user"`
}
type Authenticator struct {
Name string `json:"name"`
Phone int64 `json:"phone"`
}
var u UserType
func main() {
//Parse the Json-encoded Data and store the results in u
// update the user
r := mux.NewRouter()
r.HandleFunc("/", serverhome).Methods("POST")
log.Fatal(http.ListenAndServe("192.168.101.168:4000", r))
}
func serverhome(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("<h1>welcome to golang</h1>"))
file, err := os.ReadFile("user.json") //Read File
if err != nil {
panic(err)
}
json.Unmarshal(file, &u)
body, err := ioutil.ReadAll(r.Body) //read incoming request
if err != nil {
panic(err)
}
fmt.Println(string(body))
u.User[0].Authenticators = append(u.User[0].Authenticators, Authenticator{
Name: "",
})
result, e := json.Marshal(u) //Returns the Json encoding of u into the variable result
if e != nil {
panic(e)
}
var n = 0
n, err = os.Stdout.Write(result) //The line of code golang.org uses to print the Json encoding
if err != nil {
panic(err)
}
fmt.Println(n)
_ = os.WriteFile("test.json", result, 0644)
}re
type UserType struct {
User []struct {
CdbID string `json:"cdb_id"`
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
Phone int64 `json:"phone"`
Email string `json:"email"`
Address []struct {
Street string `json:"street"`
City string `json:"city"`
Zip string `json:"zip"`
Country string `json:"country"`
} `json:"address"`
Authenticators []Authenticator `json:"authenticators"`
VoiceSig string `json:"voice_sig"`
VoicesigCreatedTime string `json:"voicesig_created_time"`
Status string `json:"status"`
} `json:"user"`
}
type Authenticator struct {
Name string `json:"name"`
Phone int64 `json:"phone"`
}
var u UserType
func main() {
//Parse the Json-encoded Data and store the results in u
// update the user
r := mux.NewRouter()
r.HandleFunc("/", serverhome).Methods("POST")
log.Fatal(http.ListenAndServe("192.168.101.168:4000", r))
}
func serverhome(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("<h1>welcome to golang</h1>"))
file, err := os.ReadFile("user.json") //Read File
if err != nil {
panic(err)
}
json.Unmarshal(file, &u)
body, err := ioutil.ReadAll(r.Body) //read incoming request
if err != nil {
panic(err)
}
fmt.Println(string(body))
u.User[0].Authenticators = append(u.User[0].Authenticators, Authenticator{
Name: "",
})
result, e := json.Marshal(u) //Returns the Json encoding of u into the variable result
if e != nil {
panic(e)
}
var n = 0
n, err = os.Stdout.Write(result) //The line of code golang.org uses to print the Json encoding
if err != nil {
panic(err)
}
fmt.Println(n)
_ = os.WriteFile("test.json", result, 0644)
}

Related

Custom unmarshalling from flat json to nested struct

Lets say I have two struct that are related like this:
type SecretUser struct {
UserInfo `json:"userInfo"`
Password string `json:"password"`
}
type UserInfo struct {
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
Email string `json:"email"`
}
And I receive a JSON in this form:
{
"firstName": "nice",
"lastName":"guy",
"email":"nice#guy.co.uk",
"password":"abc123"
}
I want to unmarshall this JSON into a SecretUser. Is there a better way than doing it like this?
func (u *User) UnmarshalJSON(data []byte) error {
var objmap map[string]*json.RawMessage
var password string
var err error
err = json.Unmarshal(data, &objmap)
if err != nil {
return err
}
if err := json.Unmarshal(data, &u.UserInfo); err != nil {
return err
}
err = json.Unmarshal(*objmap["password"], &password)
if err != nil {
return err
}
u.Password = password
return nil
}
Basically, I partially unmarshall the JSON into a UserInfo struct and then read it again to extract the password. I don't want to create another struct for just unmarshalling this JSON cleanly or use an external library (unless it's part of the standard). Is there a more clean/efficient way of doing this, without reading the JSON twice or setting every field manually from a map?
Simply include the UserData into the SecretUser struct and do not specify a json tag for it.
type UserInfo struct {
FirstName string `json:"firstName"`
LastName string `json:"lastName"`
Email string `json:"email"`
}
type SecretUser struct {
UserInfo
Password string `json:"password"`
}
func main() {
data := []byte(`{"firstName": "nice","lastName":"guy","email":"nice#guy.co.uk","password":"abc123"}`)
var u SecretUser
json.Unmarshal(data, &u)
fmt.Println(u)
}
Go Play Space example

Parse stringified JSON

I'm trying to parse stringified JSON with Go, but I cannot find anything that explains how do to efficient parsing.
Example JSON:
{
"messages":"<ul class=\"messages\"><li class=\"success-msg\"><ul><li><span>Item succcessfully added<\/span><\/li><\/ul><\/li><\/ul>",
"app_cart":"[]",
"addcartrows":"[{\"productId\":\"1675688\",\"quantity\":1,\"unitPrice\":\"290.00\",\"currency\":\"EUR\",\"sku\":\"P00525485-3\"}]",
"minicart_content":"<ul class=\"checkout-types minicart\">\n
<li>",
"cart_qty":"1",
"added_product_json":"{\"id\":\"1675695\",\"size\":\"40\"}"
}
I usually parse json by casting it to a struct. like this:
type AutoGenerated struct {
Messages string `json:"messages"`
AppCart string `json:"app_cart"`
Addcartrows string `json:"addcartrows"`
MinicartContent string `json:"minicart_content"`
CartQty string `json:"cart_qty"`
AddedProductJSON string `json:"added_product_json"`
}
var j AutoGenerated
if err = json.Unmarshal(body, &AutoGenerated); err != nil {
fmt.Println(err) // json: cannot unmarshal string into Go struct field AutoGenerated.added_product_json
}
However I do not know how to correctly parse this type of response.
Any help?
do it in two steps.
package main
import (
"encoding/json"
"fmt"
)
func main() {
type AutoGenerated struct {
Messages string `json:"messages"`
AppCart string `json:"app_cart"`
Addcartrows string `json:"addcartrows"`
MinicartContent string `json:"minicart_content"`
CartQty string `json:"cart_qty"`
AddedProductJSON string `json:"added_product_json"`
}
type addedProduct struct {
ID string `json:"id"`
Size string `json:"size"`
}
type productRow struct {
ProductID string `json:"productId"`
Quantity int `json:"quantity"`
UnitPrice string `json:"unitPrice"`
Currency string `json:"currency"`
SKU string `json:"sku"`
}
var j AutoGenerated
body := []byte(`{
"messages":"<ul class=\"messages\"><li class=\"success-msg\"><ul><li><span>Item succcessfully added<\/span><\/li><\/ul><\/li><\/ul>",
"app_cart":"[]",
"addcartrows":"[{\"productId\":\"1675688\",\"quantity\":1,\"unitPrice\":\"290.00\",\"currency\":\"EUR\",\"sku\":\"P00525485-3\"}]",
"minicart_content":"<ul class=\"checkout-types minicart\">\n<li>",
"cart_qty":"1",
"added_product_json":"{\"id\":\"1675695\",\"size\":\"40\"}"
}`)
if err := json.Unmarshal(body, &j); err != nil {
panic(err)
}
var k []productRow
if err := json.Unmarshal([]byte(j.Addcartrows), &k); err != nil {
panic(err)
}
var u addedProduct
if err := json.Unmarshal([]byte(j.AddedProductJSON), &u); err != nil {
panic(err)
}
fmt.Printf("%#v\n\n", j)
fmt.Printf("%#v\n\n", k)
fmt.Printf("%#v\n\n", u)
}
Assuming the json you have added has a formatting issue for value of "minicart_content" , below should help -
package main
import (
"fmt"
"encoding/json"
)
var myjson string = `{
"messages": "<ul class=\"messages\"><li class=\"success-msg\"><ul><li><span>Item succcessfully added<\/span><\/li><\/ul><\/li><\/ul>",
"app_cart": "[]",
"addcartrows": "[{\"productId\":\"1675688\",\"quantity\":1,\"unitPrice\":\"290.00\",\"currency\":\"EUR\",\"sku\":\"P00525485-3\"}]",
"minicart_content": "<ul class=\"checkout-types minicart\">\n <li > ",
"cart_qty": "1",
"added_product_json": "{\"id\":\"1675695\",\"size\":\"40\"}"
}`
type AutoGenerated struct {
Messages string `json:"messages"`
AppCart string `json:"app_cart"`
Addcartrows string `json:"addcartrows"`
MinicartContent string `json:"minicart_content"`
CartQty string `json:"cart_qty"`
AddedProductJSON string `json:"added_product_json"`
}
func main() {
var j AutoGenerated
if err := json.Unmarshal([]byte(myjson), &j); err != nil {
fmt.Println(err)
}
fmt.Println(j.AddedProductJSON)
}

Unable to unmarshal golang response

I've been trying to extract some JSON by unmarshalling my json response into structs, but I have no idea why it's not doing it properly. I've also tried gjson but same result. Am I missing something here?
JSON Result:
{"availabilities":[{"pickup":{"status":"OnlineOnly","purchasable":false},"shipping":{"status":"InStockOnlineOnly","purchasable":true},"sku":"12341231","sellerId":"438178","saleChannelExclusivity":"OnlineOnly","scheduledDelivery":false,"isGiftCard":false,"isService":false}]}
Code:
// Inventory ...
type Inventory struct {
Availabilities []Availability `json:"availabilities"`
}
// Availability ...
type Availability struct {
Sku string `json:"sku"`
SellerID string `json:"sellerId"`
SaleChannelExclusivity string `json:"saleChannelExclusivity"`
ScheduledDelivery bool `json:"scheduledDelivery"`
IsGiftCard bool `json:"isGiftCard"`
IsService bool `json:"isService"`
Pickup Statuses `json:"pickup"`
Shipping Statuses `json:"shipping"`
}
// Statuses ..
type Statuses struct {
Status string `json:"status"`
Purchasable bool `json:"purchasable"`
}
func (pr *Program) checkInventory() {
url := fmt.Sprintf("https://www.bestbuy.ca/ecomm-api/availability/products?accept-language=en-CA&skus=%s", pr.Sku)
log.Infof("URL %s", url)
resp, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
log.Info(string(bodyBytes))
var inv Inventory
json.Unmarshal(bodyBytes, &inv)
log.Infof("%+v", inv)
}
Console:
INFO[2020-04-07T03:01:10-07:00] URL https://www.bestbuy.ca/ecomm-api/availability/products?accept-language=en-CA&skus=12341231
INFO[2020-04-07T03:01:10-07:00] {"availabilities":[{"pickup":{"status":"OnlineOnly","purchasable":false},"shipping":{"status":"InStockOnlineOnly","purchasable":true},"sku":"12341231
,"sellerId":"438178","saleChannelExclusivity":"OnlineOnly","scheduledDelivery":false,"isGiftCard":false,"isService":false}]}
INFO[2020-04-07T03:01:10-07:00] {Availabilities:[]}
The problem is in the json.Unmarshall call. It is returning an error: "invalid character 'ï' looking for beginning of value” from json.Unmarshal
As it is explained here: The server is sending you a UTF-8 text string with a Byte Order Mark (BOM). The BOM identifies that the text is UTF-8 encoded, but it should be removed before decoding.
This can be done with the following line (using package "bytes"):
body = bytes.TrimPrefix(body, []byte("\xef\xbb\xbf"))
So the resulting working code is:
// Inventory ...
type Inventory struct {
Availabilities []Availability `json:"availabilities"`
}
// Availability ...
type Availability struct {
Sku string `json:"sku"`
SellerID string `json:"sellerId"`
SaleChannelExclusivity string `json:"saleChannelExclusivity"`
ScheduledDelivery bool `json:"scheduledDelivery"`
IsGiftCard bool `json:"isGiftCard"`
IsService bool `json:"isService"`
Pickup Statuses `json:"pickup"`
Shipping Statuses `json:"shipping"`
}
// Statuses ..
type Statuses struct {
Status string `json:"status"`
Purchasable bool `json:"purchasable"`
}
func (pr *Program) checkInventory() {
url := fmt.Sprintf("https://www.bestbuy.ca/ecomm-api/availability/products?accept-language=en-CA&skus=%s", pr.Sku)
log.Infof("URL %s", url)
resp, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatal(err)
}
body := bytes.TrimPrefix(bodyBytes, []byte("\xef\xbb\xbf"))
log.Info(string(body))
var inv Inventory
err = json.Unmarshal([]byte(body), &inv)
if err != nil {
log.Fatal(err)
}
log.Infof("%+v", inv)
}

How to modify a return json

I'm taking the data from the github API with golang, however, I want to send to the front only the necessary data (id, name, url, language and description) instead of just returning everything that the github API gives me.
func GetAllReposStars(w http.ResponseWriter, r *http.Request) {
enableCors(&w)
params := mux.Vars(r)
username := params["username"]
res, err := http.Get("https://api.github.com/users/" + username + "/starred")
body, err := ioutil.ReadAll(res.Body)
res.Body.Close()
if err != nil {
log.Fatal(err)
}
log.Printf("Body: %s\n", body)
if err != nil {
log.Fatal(err)
}
w.Header().Set("Content-Type", "application/json")
w.Write(body)
json.NewEncoder(w)
}
You can define a type that has only the keys you need and decode the response from the GitHub API into a variable of that type so that only the keys you need are kept, then write that variable to the response.
For example:
package main
import (
"encoding/json"
"log"
"net/http"
"github.com/gorilla/mux"
)
type RelevantRepoData struct {
Id int `json:"id"`
Name string `json:"name"`
Url string `json:"url"`
Language string `json:"language"`
Description string `json:"description"`
}
func GetAllReposStars(w http.ResponseWriter, r *http.Request) {
enableCors(&w)
params := mux.Vars(r)
username := params["username"]
res, err := http.Get("https://api.github.com/users/" + username + "/starred")
var repoData RelevantRepoData
err = json.NewDecoder(res.Body).Decode(&repoData)
if err != nil {
log.Fatal(err)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(repoData)
}
If you want to send some specific fields to front-end but in future want to handle them on backend, you can use json:"-" in your struct
e.g :
type RelevantRepoData struct {
Irrelevant string `json:"-"`
Id int `json:"id"`
Name string `json:"name"`
Url string `json:"url"`
Language string `json:"language"`
Description string `json:"description"`
}
When you return this object, the fields with json:"-" will be ignored but you can still use them on backend.

How to prevent an array from being embedded into another array in my json response

In my api server I return a json object which has string arrays in it. I am finding that my arrays are being embedded into another array like this:
"Items": [
"[\"QQTISGXSJIS4DEV36JCBWQ4X\", \"HCOWEB7NVIQEUAINMM2KUV6J\", \"FCKP7D3H6Q7RQIRKSPVZBRHL\", \"UQLVH65PPBTVK6KMIV5KMGY6\", \"UR2XTXJFVURE5ERBLNW7ZUCR\", \"75N66F4DYGPM57V47N3IBMKD\", \"HQ2CRXQFPQM7TNNDZXZ2MQ2B\", \"3SLGKFR5GPHVZMQM4YM6KI4U\", \"UCQ3J7GYAYPZOCQKWIRGNGNY\", \"6INWDYWUFX6L5JYX2HEVMMHX\", \"ASQBRMKYSK2TINHBYQIWATS5\", \"QPCHVJ4HXYTUJNEZWQCKM5I3\", \"7JPYYH64Y3FQK6YJX5NBXMM6\", \"BI4NIBBOFBYAAS7ZROD6XEMB\", \"RGU3X36VYMXX4N3XPEZKY76K\", \"PLHVIQ7QT6TBWI5BZX6EJI74\", \"YATHGR6W6BIKFYXVZMGVBRB4\", \"ZZ5KZ5ZSBVLQRDKR2SJQ5CXW\", \"TNH56AOIMFSLOX5AW5I6WYP2\", \"VIFSURNJWJ6YYKXIWTWRNY6F\"]"
]
You can a complete JSON object here: https://gist.github.com/yshuman1/31b39333e2cd187707d98817171c3914
I am using gorm and saving my array as pq.StringArray.
Here is my function that returns that json:
func (u *Users) RetrieveItemCatModSort(w http.ResponseWriter, r *http.Request) {
var l model.ItemCatModSort
type data struct {
Email string
Password string
Location string
}
var form data
if err := parseForm(r, &form); err != nil {
log.Infof("error parsing form for logging in Kiosk %v", err)
return
}
user, err := u.us.ByEmail(string(form.Email))
if err != nil {
log.Infof("error looking up user by email while retrieving items %v", err)
return
}
bcrypt.CompareHashAndPassword([]byte(user.KioskPwHash), []byte(form.Password))
if err != nil {
log.Infof("invalid login credentials error: %v submitted data:\n%#v", err, form)
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
w.WriteHeader(422) // unprocessable entity
if err := json.NewEncoder(w).Encode(err); err != nil {
panic(err)
}
}
//lookup oauth token in DB
token, err := u.os.Find(user.ID, "square")
if err != nil {
fmt.Println(err)
}
l.LocationID = form.Location
l.Modifiers, err = pos.GetModifiers(token.AccessToken, l.LocationID)
if err != nil {
log.Infof("Error grabbing modifiers using GetInventory. %#v", err)
}
l.Categories, err = pos.GetCategories(token.AccessToken)
if err != nil {
log.Infof("Error grabbing categories using GetInventory. %#v", err)
}
l.Items, err = pos.GetItems(token.AccessToken, l.LocationID)
if err != nil {
log.Infof("Error grabbing items using GetInventory. %#v", err)
}
l.ItemSort, err = u.is.FindItemSortByLocation(l.LocationID)
if err != nil {
log.Infof("Error grabbing item sort. %#v", err)
}
l.CatSort, err = u.is.FindCatSortByLocation(l.LocationID)
if err != nil {
log.Infof("Error grabbing cat sort. %#v", err)
}
lJSON, err := json.Marshal(l)
if err != nil {
log.Infof("error marshalling data to lJSON inside RetrieveLocationInv %v", err)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
w.Write(lJSON)
}
Here are some of the models that I use:
type Categories struct {
Categories []Category `json:"objects"`
}
type ItemCatModSort struct {
LocationID string
Categories Categories
Items []Item
Modifiers []Modifier
ItemSort []ItemSort
CatSort CatSort
}
type CatSort struct {
gorm.Model
LocationID string
Category pq.StringArray `gorm:"type:varchar(10485760)[]"`
}
type Item struct {
gorm.Model
Type string `json:"type"`
ID string `json:"id"`
UpdatedAt time.Time `json:"updated_at"`
Version int64 `json:"version"`
IsDeleted bool `json:"is_deleted"`
PresentAtAllLocations bool `json:"present_at_all_locations"`
PresentAtLocationIds []string `json:"present_at_location_ids"`
AbsentAtLocationIds []string `json:"absent_at_location_ids"`
ItemData struct {
Name string `json:"name"`
Description string `json:"description"`
Visibility string `json:"visibility"`
CategoryID string `json:"category_id"`
ModifierListInfo []struct {
ModifierListID string `json:"modifier_list_id"`
Visibility string `json:"visibility"`
MinSelectedModifiers int `json:"min_selected_modifiers"`
MaxSelectedModifiers int `json:"max_selected_modifiers"`
Enabled bool `json:"enabled"`
} `json:"modifier_list_info"`
ImageURL string `json:"image_url"`
Variations []struct {
Type string `json:"type"`
ID string `json:"id"`
UpdatedAt time.Time `json:"updated_at"`
Version int64 `json:"version"`
IsDeleted bool `json:"is_deleted"`
PresentAtAllLocations bool `json:"present_at_all_locations"`
PresentAtLocationIds []string `json:"present_at_location_ids"`
AbsentAtLocationIds []string `json:"absent_at_location_ids"`
ItemVariationData struct {
ItemID string `json:"item_id"`
Name string `json:"name"`
Sku string `json:"sku"`
Ordinal int `json:"ordinal"`
PricingType string `json:"pricing_type"`
PriceMoney struct {
Amount int `json:"amount"`
Currency string `json:"currency"`
} `json:"price_money"`
} `json:"item_variation_data"`
} `json:"variations"`
ProductType string `json:"product_type"`
SkipModifierScreen bool `json:"skip_modifier_screen"`
} `json:"item_data"`
}
basically instead of
"Items": [
"[\"QQTISGXSJIS4DEV36JCBWQ4X\", \"HCOWEB7NVIQEUAINMM2KUV6J\",... \"VIFSURNJWJ6YYKXIWTWRNY6F\"]" ]
looking like that, I'd want it to look like this:
"Items": ["QQTISGXSJIS4DEV36JCBWQ4X\", \"HCOWEB7NVIQEUAINMM2KUV6J\",... \"VIFSURNJWJ6YYKXIWTWRNY6F\" ]