I'm new at golang and got some little problem:
I got remoteApi that give me some response when I'm making http request like here:
res, err := http.DefaultClient.Do(req)
the body of the response contains some json such as :
{
a: 'hello'
b: 5
c:[1,2,3]
}
I need to assign the value of "a" to other variable .
What is the best way to access one of res.Body properties? Ive tried to convert to json / string and so but no success
thanks
Something like this should work:
var s struct {
A string
}
err := json.NewDecoder(response.Body).Decode(&s)
// check err
result := s.A
Also please note that your JSON response example is not valid JSON (single quotes instead of double quotes, field names are not quoted, field separators missing) and will not be parsed successfully as such.
Related
I'm making an API server using GoLang and I am using Mux as the HTTP router. I have one endpoint in my app, /tasks. When I hit that endpoint, I get back an array of results, which is as expected. Now, I want to build upon that and add an optional query parameter into the URL to only return N results, like so: /tasks?num=5 which would only return 5 results. I have accomplished this using the handler below:
vars := r.URL.Query()
t := task{}
if numOfTasksParam, ok := vars["num"]; ok {
fmt.Printf("%+v", numOfTasksParam[0])
numOfTasks, err := strconv.Atoi(vars.Get("num"))
//return only n number of results
} else {
//return the entire result set
}
I devised this solution because I discovered that URL.Query() returns a map of the query parameters and therefore, I can just check to see if that map contained the key of "num". If so, the client wants N number of results. If not, the client must want the whole result set.
The main issue I have with this approach is that when I go to check if the keys exists, I make a temporary variable called numOfTasksParam which holds the query parameter value, but it holds the value as a string and I need a int. Therefore, I must use the numOfTasksParam somehow and then create another variable to convert that to an integer value.
Is there more succinct or convenient way of checking if a query parameter exists in the request URL?
This is probably the most succinct, and works because Get returns an empty string if the parameter isn't set, which Atoi will fail to parse:
vars := r.URL.Query()
t := task{}
if numOfTasks, err := strconv.Atoi(vars.Get("num")); err == nil {
//return only numOfTasks number of results
} else {
//return the entire result set
}
The price you pay for having less code is that if a user passes an invalid value like ?num=taco, it will be treated as if they passed nothing, rather than telling the user they passed something unusable. This may or may not be what you want. It is also slightly less efficient, because it will run Atoi even if the value is known to be empty.
I've been working on a toy project and noticed that dynamo added a (NULL) suffix in two of my attribute names. I had not noticed before, so I assume it must have happened after one of my code changes. I could not find any reference to this behavior online.
The script I'm running is a simple PutItem got from the official Dynamodb documentation, where I insert a few mock users in a table.
func InsertModel(m interface{}) error {
av, err := dynamodbattribute.MarshalMap(m)
if err != nil {
return fmt.Errorf("handlers: Got error marshalling map: %v", err)
}
input := &dynamodb.PutItemInput{
Item: av,
TableName: aws.String(appTableName),
ConditionExpression: aws.String("attribute_not_exists(PK) AND attribute_not_exists(SK)"),
}
_, err = svc.PutItem(input)
if err != nil {
return fmt.Errorf("handlers: Got error calling PutItem: %v", err)
}
return nil
}
m (user mock data) has all fields type string:
UserModel{PK: "910cc6d8-b7e2-dfg6-d8d4-sh6d0e3fde6b", SK: "user_info", Name: "bla", ImageURI: "aaa"},
When I remove the fields "Name" and "ImageURI", the PutItem inserts a boolean true to the field value as seen below.
Here is the value in av after the MarshalMap operation.
with populated "Name" and "ImageURI" fields:
map[ImageURI:{
S: "aaa"
} Name:{
S: "bla"
} PK:{
S: "910cc6d8-b7e2-dfg6-d8d4-sh6d0e3fde6b"
} SK:{
S: "user_info"
}]
and here without "Name" and "ImageURI" as in UserModel{PK: "910cc6d8-b7e2-dfg6-d8d4-sh6d0e3fde6b", SK: "user_info"}
map[ImageURI:{
NULL: true
} Name:{
NULL: true
} PK:{
S: "910cc6d8-b7e2-dfg6-d8d4-sh6d0e3fde6b"
} SK:{
S: "user_info"
}]
I have tried to delete all the records from the table and insert again but the behavior continues. Also, I did the same process for an int type attribute (inserting the object with the int attribute populated and not populated) and I get 0 when it's not populated (which is what I'd expect). I tried replicating this with a different string type attribute, and I get the same behavior (true when empty), but the attribute name doesn't get the suffix NULL.
So in summary, it seems this behavior is mostly happening with type string in my case, and I only get a NULL suffix in the attributes "Name" and "ImageURI", but not on the other string attribute I've tried (nor the int one).
I had the same issue for one of the fields of my table items.
For that field I was doing an update using the NameMap option, useful when you want to use a name that, for some other reasons, is reserved by dynamo.
I just tried not to use the NameMap option, giving another name for my field, and that suffix disappeared.
I hope my experience could be somehow helpful.
Regards
Option A
The dynamodbattribute.MarshalMap method reads json struct tags if present. By adding omitempty it will leave the attribute off of the item. Then when reading it back later, it will default to the empty value in the struct.
type Foo struct {
Bar string `json:"bar,omitempty"`
}
Option B
You can explicitly set empty struct values by either creating your own dynamodb.AttributeValue or implement the marshaller interface on your struct. For example:
item := map[string]*dynamodb.AttributeValue{
"Foo": {
S: aws.String("")
}
}
Making the (NULL) suffix to go away
After deleting all the rows containing a NULL value in a column with the (NULL) suffix, it seems to take some time for the suffix to go away from the AWS UI. When I tested this, it took roughly 12 hours.
Discussion
Continue the discussion of null behaviors on github.
https://github.com/aws/aws-sdk-go/pull/2419
https://github.com/aws/aws-sdk-go/issues?q=MarshalMap+null
The Goal:
Using github.com/neelance/graphql-go starwars example, I'm trying to write a JSON response to my ReactJS client. That struct stuff is completely new for me, Golang as well btw.
The question:
What should data variable be in order to get the appropriate response to the following example GraphQL query?
query clientQuery {
character(id: 1000) {
name
appearsIn
}
}
Additional info:
From what I've read here and there, data must be some kind of struct. I've got plenty of structs available in the example (see starwars.go below).
The Code to be modified (main.go):
package main
import (
"encoding/json"
"log"
"net/http"
"github.com/neelance/graphql-go"
"github.com/neelance/graphql-go/example/starwars"
"github.com/neelance/graphql-go/relay"
)
var schema *graphql.Schema
func init() {
schema = graphql.MustParseSchema(starwars.Schema, &starwars.Resolver{})
}
func main() {
port := ":8080"
log.Printf(`GraphQL server starting up on http://localhost%v`, port)
http.Handle("/query", &relay.Handler{Schema: schema})
http.HandleFunc("/graphql", func(w http.ResponseWriter, r *http.Request) {
// THIS IS SUPER WRONG, data should be something
// like data := starwars.Resolver{} or so?
data := `{"data":{"character":{"name":"Luke Skywalker","appearsIn":["NEWHOPE","EMPIRE","JEDI"]}}}`
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(data)
})
log.Fatal(http.ListenAndServe(port, nil))
}
REFERENCE 1 - starwars.go
REFERENCE 2 - relay.go
You question is a bit confusing. You are asking about JSON, but your wanted response is not in a valid JSON format: It looks like you added unrelated GraphQL information.
I am not sure if this is an question about GraphQL or JSON. I will try to answer it anyway.
Your example data looks like this, therefore I assume that is the result you want to generate:
data := `{"data":{"character":{"name":"Luke Skywalker","appearsIn":["NEWHOPE","EMPIRE","JEDI"]}}}`
A clean way to make Go generate a proper JSON is to create Structs which contain the data:
type Response struct {
Data Data `json:"data"`
}
type Data struct {
Character Character `json:"characer"`
}
type Character struct {
Name string `json:"name"`
AppearsIn []string `json:"appearsIn"`
}
Then you can define the data like this:
data := Response{
Data: Data{
Character: Character{
Name: "Luke Skywalker",
AppearsIn: []string{"NEWHOPE", "EMPIRE", "JEDI"},
},
},
}
Perhaps this question isn't about JSON rather than GraphQL. In this case you need to clarify your question.
References
Details about JSON tags (ie json:".."): https://golang.org/pkg/encoding/json/#Marshal
Example with Structs: https://gobyexample.com/structs
Here is a simpler example, which would have helped me if it had been here.
type Password struct {
Password string `json:"password"`
}
func keyHandler(w http.ResponseWriter, r *http.Request) {
response := Password{"password"}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(response)
}
I have a very strange problem, and i'm either really blind, or this is some kind of a bug. I have the following http.Handler:
func ServeHTTP(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
log.Println("Error while parsing form data")
return
}
log.Println("Printing r.PostForm:")
for key, values := range r.PostForm { // range over map
for _, value := range values { // range over []string
log.Println(key, value)
}
}
b, _ := ioutil.ReadAll(r.Body)
s := string(b)
log.Println("Printing body: ",s)
}
Now, when sending a PUT-Request to the url binded to this handler with the following FORM-Data:
Name=someName
Version=1.0.0
PLanguage=java
GitRepo=someRepo
This is ALWAYS the output:
Printing r.PostForm:
Printing body: Name=someName&Version=1.0.0&PLanguage=java&GitRepo=someRepo
I've been trying to find the cause for like 2 hours already and i just have no idea what the heck is wrong here. There is no error parsing the Form-Data, but the r.PostForm map is always empty (i also tried r.Form, with same result). So for debugging i added the part where i print the body, just to make sure there actually is some data in there - and it is. I would really appreciate any help here. Thanks in advance!
You need to set the 'Content-Type' header.
If no header is set "application/octet-stream" is used according to RFC 2616.
Long story short that is a binary format so your body will not be parsed into the Form.
I am trying to write a csv parser using the example provided here. It works great for all native types but I am having trouble with any structs that contain a timestamp of type time.Time. It exits with an error of "cannot convert this type".
This is the code.
//For each field in a given struct...
//Get a field
val := sv.Field(i)
// this is necessary because Kind can't tell
// distinguish between a primitive type
// and a type derived from it. We're looking
// for a Value interface defined on
// the pointer to this value
_, ok := val.Addr().Interface().(Value)
if ok {
val = val.Addr()
kind = value_k
} else {
switch Kind {
case reflect.Int, reflect.Int16, reflect.Int8,
reflect.Int32, reflect.Int64:
kind = int_k
case reflect.Uint, reflect.Uint16, reflect.Uint8,
reflect.Uint32, reflect.Uint64:
kind = uint_k
case reflect.Float32, reflect.Float64:
kind = float_k
case reflect.String:
kind = string_k
default:
// Kind is Struct here
kind = value_k
_, ok := val.Interface().(Value)
if !ok {
err = os.NewError("cannot convert this type ")
this = nil
return
}
}
}
What this code does is take an interface and a reader. It attempts to match the field headers in the reader (csv file) with field names in the interface. It also reflects on the interface (struct) and collects positional a type information for later setting the fields in the iterator. It is this step that is failing for non-native types.
I've tried a few methods to work around this but the only thing that seems to work is changing the timestamp to a string. I am undoubtedly missing something and would greatly appreciate some guidance.