I would like to find out if it is possible for an HTTP client to POST a specific JSON object and server response with a different JSON object in Go. For a example a client send the JSON object
request body
{
"nfNssaiAvailabilityUri": "string",
"taiList": [
{
"plmnId": {
"mcc": "string",
"mnc": "string"
},
"tac": "string"
}
],
"expiry": "2019-04-01T10:41:54.344Z"
}
response body as
{
"subscriptionId": "string",
"expiry": "2019-04-01T10:41:54.363Z",
"authorizedNssaiAvailabilityData": [
{
"tai": {
"plmnId": {
"mcc": "string",
"mnc": "string"
},
"tac": "string"
},
"supportedSnssaiList": [
{
"sst": 0,
"sd": "string"
}
],
"restrictedSnssaiList": [
{
"homePlmnId": {
"mcc": "string",
"mnc": "string"
},
"sNssaiList": [
{
"sst": 0,
"sd": "string"
}
]
}
]
}
]
}
As you can see, the request body JSON is different from the response. I have structs for both JSONs and currently I can only POST with the request body and receive the same body as response. What am expecting is to be able to get a JSON object indicated in the above response.
I have:
type NssfEventSubscriptionCreateData struct {
NfNssaiAvailabilityUri string `json:"nfNssaiAvailabilityUri"`
TaiList []Tai `json:"taiList,omitempty"`
...
}
type NssfEventSubscriptionCreatedData struct {
SubscriptionId string `json:"subscriptionId"`
Expiry time.Time `json:"expiry,omitempty"`
....
}
func (m *SliceDataAccess) InsertNssaiSubscriptionInfo(subdata NssfEventSubscriptionCreateData) error {
err := db.C(COLLECTION).Insert(subdata)
if err != nil {
return err
}
return nil
}
func NSSAIAvailabilityPost(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Accept") != "application/json" {
WriteError(w, ErrNotAcceptable)
return
}
if r.Method == "POST" {
var reqsubData NssfEventSubscriptionCreateData
err := json.NewDecoder(r.Body).Decode(&reqsubData)
if err != nil {
respondWithError(w, http.StatusBadRequest, "Object body not well decoded")
return
}
//reqsubData.ID = bson.NewObjectId()
if err := da.InsertNssaiSubscriptionInfo(reqsubData); err != nil {
respondWithError(w, http.StatusInternalServerError, err.Error())
} else {
scheme := "http"
if r.URL.Scheme != "" {
scheme = r.URL.Scheme
}
w.Header().Set("Location", scheme+"://"+r.Host+r.URL.Path)
w.Header().Set("Response-Code", "201")
w.Header().Set("Response-Desc", "Success")
respondWithJson(w, http.StatusCreated, reqsubData)
}
}
}
The NSSAIAvailabilityPost function respond with NssfEventSubscriptionCreateData struct type JSON object but I would like to be able to respond with the NssfEventSubscriptionCreatedData struct type JSON object.
Create a struct of type NssfEventSubscriptionCreatedData initialise its values and return in respondWithJSON.
respData : = NssfEventSubscriptionCreatedData{}
// init fields
respondWithJson(w, http.StatusCreated, respData)
Related
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
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)
}
Here is a sample of my JSON post:
{
"jsonFilter" :[{
"column": "",
"contains": "",
"condition": "",
"not": true
}, {
"column": "",
"contains": "",
"condition": "",
"not": true
}]
}
My Echo framework handler:
func GetFilteredFileData(c echo.Context) error {
body := echo.Map{}
if err := c.Bind(&body); err != nil {
fmt.Println(err)
}
fmt.Println(body)
for key, element := range body {
fmt.Println(key, element)
}
}
Results:
jsonFilter [map[column:StartTimestamp condition:contains contains:<nil> not:false] map[column:SourceIP condition:startsWith contains:<nil> not:true]]
This is not index accessible, nor key accessible. I was told I needed to make a struct or 2, but all my efforts there did not work (gorilla schema, json.Marshal, etc)
What is the proper way to take this map and make it actually usable in a variable/struct ref?
You can unmarshal that json into the following struct:
type Body struct {
Filter []JSONFilter `json:"jsonFilter"`
}
type JSONFilter struct {
Column string `json:"column"`
Contains string `json:"contains"`
Condition string `json:"condition"`
Not bool `json:"not"`
}
I don't know about this echo framework, but it looks like something like this should work:
body := Body{}
if err := c.Bind(&body); err != nil {
fmt.Println(err)
}
Or, json.Unmarshal, if the above doesn't do.
I make a GET request, and receive a JSON file, that I cannot parse.
Here is the data I have to parse
{
"codeConv": "ACC00000321",
"start": "2019-07-01T00:00:00Z",
"end": "2019-08-21T00:00:00Z",
"details": [
{
"idPrm": "30000000123456",
"keys": [
{
"timestamp": "2019-07-01T00:00:00Z",
"value": 0
},
{
"timestamp": "2019-07-01T00:30:00Z",
"value": 0
},
...
]
},
{
"idPrm": "30000000123457",
"keys": [
{
"timestamp": "2019-07-01T00:00:00Z",
"value": 1
},
{
"timestamp": "2019-07-01T00:30:00Z",
"value": 2
},
...
]
}
]
}
Here are my objects:
type APIMain struct {
CodeConv string `json:"codeConv"`
Start string `json:"start"`
End []Keys `json:"end"`
Details []APIData `json:"details"`
}
//APIData match the data we receive from api
type APIData struct {
Prm string `json:"idPrm"`
Keys []Keys `json:"keys"`
}
type Keys struct {
Timestamp string `json:"timestamp"`
Value string `json:"value"`
}
and here is the method to get data with basic auth:
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tr}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
if login != "" && password != "" {
req.SetBasicAuth(login, password)
}
response, err := client.Do(req)
//defer response.Body.Close()
if err != nil {
return nil, err
}
if response.StatusCode != 200 {
fmt.Println(response.Body)
panic(response.Status)
}
err = json.NewDecoder(response.Body).Decode(&result)
fmt.Println("result", result) // result is empty array
How can I see if the problem is in a request, or if the problem is in parsing ?
I have get a response.Body object, but it needs to be decoded.
I fixed it using: https://mholt.github.io/json-to-go/
which generated this structure:
type AutoGenerated struct {
CodeConv string `json:"codeConv"`
Start time.Time `json:"start"`
End time.Time `json:"end"`
Details []struct {
IDPrm string `json:"idPrm"`
Keys []struct {
Timestamp time.Time `json:"timestamp"`
Value float64 `json:"value"`
} `json:"keys"`
} `json:"details"`
}
Thanks for your comments
Great time saver !
I'm trying to demarshal the JSON requests of Google Actions. These have arrays of tagged unions like this:
{
"requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
"inputs": [{
"intent": "action.devices.QUERY",
"payload": {
"devices": [{
"id": "123",
"customData": {
"fooValue": 74,
"barValue": true,
"bazValue": "foo"
}
}, {
"id": "456",
"customData": {
"fooValue": 12,
"barValue": false,
"bazValue": "bar"
}
}]
}
}]
}
{
"requestId": "ff36a3cc-ec34-11e6-b1a0-64510650abcf",
"inputs": [{
"intent": "action.devices.EXECUTE",
"payload": {
"commands": [{
"devices": [{
"id": "123",
"customData": {
"fooValue": 74,
"barValue": true,
"bazValue": "sheepdip"
}
}, {
"id": "456",
"customData": {
"fooValue": 36,
"barValue": false,
"bazValue": "moarsheep"
}
}],
"execution": [{
"command": "action.devices.commands.OnOff",
"params": {
"on": true
}
}]
}]
}
}]
}
etc.
Obviously I can demarshal this to an interface{} and use fully dynamic type casts and everything to decode it, but Go has decent support for decoding to structs. Is there a way to do this elegantly in Go (like you can in Rust for example)?
I feel like you could almost do it by reading demarshalling to this initially:
type Request struct {
RequestId string
Inputs []struct {
Intent string
Payload interface{}
}
}
However once you have the Payload interface{} there doesn't seem to be any way to deserialise that into a struct (other than serialising it and deserialising it again which sucks. Is there any good solution?
Instead of unmarshaling Payload to an interface{} you can store it as a json.RawMessage and then unmarshal it based on the value of Intent. This is shown in the example in the json docs:
https://golang.org/pkg/encoding/json/#example_RawMessage_unmarshal
Using that example with your JSON and struct your code becomes something like this:
type Request struct {
RequestId string
Inputs []struct {
Intent string
Payload json.RawMessage
}
}
var request Request
err := json.Unmarshal(j, &request)
if err != nil {
log.Fatalln("error:", err)
}
for _, input := range request.Inputs {
var payload interface{}
switch input.Intent {
case "action.devices.EXECUTE":
payload = new(Execute)
case "action.devices.QUERY":
payload = new(Query)
}
err := json.Unmarshal(input.Payload, payload)
if err != nil {
log.Fatalln("error:", err)
}
// Do stuff with payload
}
I made https://github.com/byrnedo/pjson for exactly this reason.
So in your case you'd have:
type Input interface {
// Variant func is required
Variant() string
}
type ExecuteInput struct {
Payload struct {
// is any just to avoid typing it for this example
Commands []any `json:"commands"`
} `json:"payload"`
}
func (q ExecuteInput) Variant() string {
return "action.devices.EXECUTE"
}
type QueryInput struct {
Payload struct {
// is any just to avoid typing it for this example
Devices []any `json:"devices"`
} `json:"payload"`
}
func (q QueryInput) Variant() string {
return "action.devices.QUERY"
}
type Inputs []Input
func (i Inputs) MarshalJSON() ([]byte, error) {
return pjson.New(Inputs{}, pjson.WithVariantField("intent")).MarshalArray(i)
}
func (i *Inputs) UnmarshalJSON(bytes []byte) (err error) {
*i, err = pjson.New(Inputs{ExecuteInput{}, QueryInput{}}, pjson.WithVariantField("intent")).UnmarshalArray(bytes)
return
}
type Request struct {
RequestId string `json:"requestId"`
Inputs Inputs `json:"inputs"`
}
Try it on go playground