golang unmarshall unknown json data - json

I have a use case where I need to unmarshal response from HTTP requests. I don't know the response format ahead of time but would just like to return the result upstream to web client (similar functionality to a proxy)
Normally I would just unmarshal like the following:
resp, _ = http.Post(url, "application/json", bytes.NewBuffer(jsonPayload))
body, _ := ioutil.ReadAll(resp.Body)
responseJson := make(map[string]interface{})
json.Unmarshal(body, &responseJson)
However, if the result is instead an array of JSON [{},{}...]
then I would need to do the following
var responseList []map[string]interface{}
json.Unmarshal([]byte(body), &responseList)
And if the result is a single string value like "ok" it would also require different unmarshaling methods
But If I don't know the response type ahead of time, how would I know how to unmarshal?

You can just unmarshall it as interface like :
var responseJson interface{}
json.Unmarshal(body, &responseJson)
To read the type of response:
switch resp := responseBody.(type) {
case string:
fmt.Println(resp)
case float64:
fmt.Println(int(resp))
default:
fmt.Println(resp)
}

Related

Loop over dynamic json.RawMessage data and set it as net/http headers

I am making a fire and forget micro-service that accepts http POST requests and gathers the needed data from the request body which it then requests to the given URL in the body.
The issue I'm having is that the numbers of headers in the input data is dynamic, meaning that there can be 1 or more headers. It differs.
I therefor read Unmarshal the incoming requestBody to the following struct:
type Request struct {
Headers json.RawMessage `json:"headers"`
Method string `json:"method"`
Body json.RawMessage `json:"body"`
}
type Event struct {
Url string `json:"url"`
Request Request `json:"request"`
}
with:
var newEvent Event
reqBody, err := ioutil.ReadAll(r.Body)
if err != nil {
fmt.Fprintln(w, err)
}
err2 := json.Unmarshal(reqBody, &newEvent)
if err2 != nil {
fmt.Fprintln(w, err2)
}
The body is easy to just pass on when creating the request:
req, err := http.NewRequest(newEvent.Request.Method , newEvent.Url, bytes.NewBuffer(newEvent.Request.Body))
if err != nil {
panic(err)
}
But when setting the headers I can't find any nifty way to just pass the json.RawMessage format. Optimally I would just like to do something like this:
req.Header = http.Header{newEvent.Request.Headers}
Is there any smart solution for this or do I have to opt to Unmarshalling the headers to a string and then parsing the sting and add them all with http.Header.Add()?
As a matter of fact, there was a cleaver solution using map! If anyone comes across this in the future here's what I did:
var headerMap map[string]interface{}
json.Unmarshal([]byte(newEvent.Request.Headers), &headerMap)
for key, element := range headerMap {
headerKey := fmt.Sprintf("%s", key)
headerElement := fmt.Sprintf("%s", element)
req.Header.Add(headerKey, headerElement)
}
There was no need to exactly specify the input, the dynamic json.RawMessagedeclaration worked just fine.
Is there any smart solution for this [...]
No.
[...] or do I have to opt to Unmarshalling the headers to a string and then parsing the sting and add them all with http.Header.Add()?
Yes.
(Except you redesign your input and get rid of the json.RawMessage.)
In general there are no shortcuts, no clever hacks, magic beans or syntactic sugar in Go to avoid some code.

Skip marshalling field in Go

Hi how would I skip marshalling the Data property in this example?
Once I receive this response, I have to parse this data property as JSON in my frontend because it has been remarshalled. I would like everything to be ready to use from the Go backend. I'm not sure how to do this. Or perhaps I can update the main unmarshaled response with the unmarshaled Data so I don't get this issue; not sure how to do this.
Get response from API and unmarshal it
Get another API response based on "Data"
Unmarshal "Data" and update it
Marshal updated "Data" and update original response
Final marshal to send to client
Generic Example:
res, err := ... // from API
type Response struct {
Id string
Data string
}
var response Response
json.Unmarshal([]byte(res), &response)
res, err = ... // fetch additional data based on response
var data map[string]interface{} // Data may have unknown properties
json.Unmarshal([]byte(res), &data)
data["knownproperty"] = "changed"
json, _ := json.Marshal(data)
data := string(json)
// update original response
response.Data = data
// marshal to send to client
json, _ = json.Marshal(response)
data = string(json)
fmt.Fprintf(w, "%v", data)
Thanks

Encode Set data structure in Golang

I have a Set data structure implemented in Go with the basic operations like Add, Remove, Difference, Union. I am trying to send a http request using the json encoder to encode the request body which contains the object of the form map[string]Set. The Set data structure is defined below:
type Set map[interface{}]struct{}
func NewSet() Set {
set := make(Set)
return set
}
The encoder looks like this:
func (req *Request) BodyContentInJson (val interface{}) error {
buf := bytes.NewBuffer(nil)
enc := json.NewEncoder(buf)
if err := enc.Encode(val); err != nil {
return err
}
req.Obj = val
req.Body = buf
req.BodySize = int64(buf.Len())
return nil
}
This code fails at
if err := enc.Encode(val); err != nil {
return err
}
giving an error:{"errors":["json: unsupported type: Set"]}. Also, the type of val is map[string]interface{}when I debugged it.
How could I possibly encode and marshal/unmarshal JSON content here using the Go's encoder ?
You could write your own UnmarshalJSON method on the *Set type which would then be used by the json.Encoder to encode the json data into the Set. Here's a simple example https://play.golang.org/p/kx1E-jDu5e.
By the way, the reason you're getting the error is because a map key of type interface{} is not supported by the encoding/json package. (https://github.com/golang/go/blob/master/src/encoding/json/encode.go#L697-L699)

go-json-rest: JSON payload is empty

I'm trying to use go-json-rest to create RESTful APIs
I have this model struct:
type Todo struct {
id int
name string
}
And I am trying to do a POST request to create an object of Todo type:
func CreateTodo(w rest.ResponseWriter, r *rest.Request) {
body, _ := ioutil.ReadAll(r.Body)
log.Println("body content is: ", string(body)) // here I can see {"name": "test1"}
var todo Todo = Todo{}
err := r.DecodeJsonPayload(&todo) // here the error shows JSON payload is empty
defer r.Body.Close()
if err != nil {
log.Println("error in parsing json")
rest.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if todo.name == "" {
rest.Error(w, "todo name is required", 400)
return
}
lock.Lock()
var nextId int = len(todos)
// todos[nextId] = todo
todo.id = nextId // set its id
todos = append(todos, todo)
log.Println("num of todos: ", len(todos))
lock.Unlock()
w.WriteJson(&todo)
}
However, in the console of the POSTMAN client, the error shows:
{
"Error": "JSON payload is empty"
}
I was wondering where might go wrong. Thanks
Edit:
This should not be considered a duplicate question, because I am not even trying to use the json package to do marshalling/unmarshalling of JSON object. Instead I am using the rest.Request object (built in go-json-rest) to decode the json parameters as posted from client.
After much digging and search on this problem I found this answer below resolved my issue:
body, _ := ioutil.ReadAll(r.Body) will consume everything from the
request body. So after removing this line, the json parsing works!
I was just being silly doing body, _ := ioutil.ReadAll(r.Body) before decoding the JSON parameters without really understanding what ioutil.ReadAll() does to the request body.
As I quoted above in the edited post, ioutil.ReadAll() consumes everything in the request body, leaving nothing for the json decoder to parse. After removing the line body, _ := ioutil.ReadAll(r.Body), the json parsing works as expected.

Passing JSON parameter to function in GOLANG

I want to pass a JSON object to a function in GOLANG ,so how would I define my parameters,would it be fine if I can define my params as string .below is a sample
func getDetailedNameSpace(authentication string,id string) string{
var jsonStr = []byte(string);
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{Transport: tr}
req, _ := http.NewRequest("PUT", "https://"+authentication.endPoint+"/object/namespaces/"+id, bytes.NewBuffer(jsonStr))
req.Header.Add("X-Sds-Auth-Token",authentication.authtoken)
req.Header.Add("Accept","application/json")
res,err:=client.Do(req)
body, err := ioutil.ReadAll(res.Body)
if err!=nil{
fmt.Printf("%s",err)
}
return string(body);
}
Also i have to validate the params ,as in python we can have like below
def getDetailedNameSpace(authentication,id=None):
assert authentication!=None,"Authentication Required"
I'm assuming you're trying to put the JSON as the body of your PUT request. In this case you'll just want to use assignment. The request object has a field of type io.ReadCloser for it.
req.Header.Add("Accept","application/json")
req.Body = bytes.NewBufferString(MyJsonAsAString)
res,err:=client.Do(req)
There are some other methods like http.Post which take the body as an argument but in this case, the Do method takes a Request object as an argument and that has a property for the Body.