I am designing an API where a user sends integer values and some operation is performed depending on the value sent. During the validation check that whether this value exists in the request, I have to equate it to 0 i.e. if intValue == 0.
However, I also have to perform a different operation in case the value is 0. Is there any way I can check that the integer does not exist or is empty without equating to 0?
Note: The requirements are set by the client and I cannot change them.
When decoding JSON data into a struct, you can differentiate optional fields by declaring them as a pointer type.
For example:
type requestFormat struct {
AlwaysPresent int
OptionalValue *int
}
Now when the value is decoded, you can check if the value was supplied at all:
var data requestFormat
err := json.NewDecoder(request.Body).Decode(&data)
if err != nil { ... }
if data.OptionalValue == nil {
// OptionalValue was not defined in the JSON data
} else {
// OptionalValue was defined, but it still may have been 0
val := *data.OptionalValue
}
in production as #phuclv say
there's no such thing as an "empty integer"
inelegant implementation
check if it is required:
like
#Hymns For Disco use json.NewDecoder
or
validators
but it is being abandoned:
Why required and optional is removed in Protocol Buffers 3
another way:
use map but struct,which can tell if a field exists
follow is a example
package main
import (
"encoding/json"
"io/ioutil"
"net/http"
)
type Test struct {
Name string
Age int
}
func testJson(w http.ResponseWriter, r *http.Request) {
aMap := make(map[string]interface{})
data, _ := ioutil.ReadAll(r.Body)
json.Unmarshal(data, &aMap)
test := Test{}
json.Unmarshal(data, &test)
}
func main() {
http.HandleFunc("/", testJson)
http.ListenAndServe(":8080", nil)
}
Related
I'm trying to write a wrap around a function that uses an interface{} parameter to return data, by adding cache.
My problem is that once I have a valid interface{} I don't know how to assign it to be returned in the parameter. The wrapped call is (github.Client) .Do in github API client and the problem hit me when I tried to add caching with go-cache
This somewhat my function
func (c *cachedClient) requestAPI(url string, v interface{}) error {
x, found := c.cache.Get(url)
if found { // Found in cache code
log.Printf("cached: %v", x)
v = x // HERE: this do not work. x contains a valid copy of what I want but how do I store it in v?
return nil
}
req, _ := c.githubClient.NewRequest("GET", url, nil) // not found I cache, request it
res, err := c.githubClient.Do(*c.context, req, v)
if err != nil {
return err
}
if res.StatusCode != 200 {
return fmt.Errorf("Error Getting %v: %v", url, res.Status)
}
c.cache.Add(url, v, cache.DefaultExpiration) // store in cache
return nil // once here v works as expected and contain a valid item
}
It fails when has to return a cached value when I try to use it like this:
// Some init code c is a cachedClient
i := new(github.Issue)
c.requestAPI(anAPIValidURLforAnIssue, i)
log.Printf("%+v", i) // var i correctly contains an issue from the github api
o := new(github.Issue)
c.requestAPI(anAPIValidURLforAnIssue, o)
log.Printf("%+v", o) // var o should have been get from the cache but here is empty
So basically my problem is that when I correctly recover a cached item it is good but I can not store it in the parameter meant to be used to store it. I can not work with subclasses because the call I'm wrapping is using an interface{} already. And I can not move it to return values because you can't return a generic interface. How do I make the interface{} x be stored in v to have it available outside?
To archive what you want you need to use a bit of reflection magic.
Please try to replace v = x with next code snippet:
reflect.ValueOf(v).Elem().Set(reflect.ValueOf(x).Elem())
Note from OP: I had to add the last .Elem() to make this work.
NOTE: in the call of the requestAPI method you should use a pointer to the value:
let's say the cached value is of type int. Then you should call requestAPI like:
var dst int // destination of the cached value or a newly retrieved result
cc.requestAPI(url, &dst)
With certain assumptions like you are storing json data in your cache below is how I will try. Errors not handled.
package main
import (
"encoding/json"
"fmt"
)
type Data struct {
Name string
}
func main() {
var d Data
requestAPI(&d)
fmt.Println(d)
}
func requestAPI(v interface{}) {
var cache_res interface{} = []byte("{\"Name\":\"CCC\"}")
//assume you got cache_res from cache
x, _ := cache_res.([]byte)
_ = json.Unmarshal(x, &v)
}
Actually above is what githubClient.Do is also doing. It checks whether v satisfies io.Writer interface, if yes write data to v. If not then it unmarshals json into v as shown above. So same can be done from cache.
Check here:
https://github.com/google/go-github/blob/v32.1.0/github/github.go#L586
If the cache object is specific then below can be used. You don't deal with empty interface{} because you should be able to pass your specific type to c.githubClient.Do as v. Since it uses json package, it will detect the type information and accordingly fill the values into it.
Lets say you store type Data struct
In below code other details eliminated like condition checking whether to cache & error handling
package main
import (
"fmt"
)
type Data struct {
Name string
}
func main() {
var d Data
requestAPI(&d)
fmt.Println(d)
}
func requestAPI(v *Data) {
var cache_res interface{} = Data{"CCC"}
//assume you got cache_res from cache
x, _ := cache_res.(Data)
*v = x
//in case you did not find it in cache then githubClient.Do should unmarshal
//contents of response body into v *Data if Data fields match that of json
//res, err := c.githubClient.Do(*c.context, req, v)
}
I am trying to handle dynamic interface type where I don't know whether interface is of type float64 or int64. I am using an API and Decode the response on a map where price is sometimes integer and sometimes float. e.g. JSON response is sometimes {price: 35} and sometimes {price: 35}
I have created an example code here
package main
import "fmt"
func main() {
response := make(map[string]interface{})
response["price"] = 2.1
response1 := make(map[string]interface{})
response1["price"] = 2
price_response, _ := response["price"].(float64)
price_response1, _ := response1["price"].(float64)
fmt.Println(price_response, "==> price_response") //output = 2.1
fmt.Println(price_response1,"==> price_response1") // //output = 0
}
The Output is get is
2.1 price_response
0 price_response1
Now here, I have to define type statically while doing type assertions on interface types. How am I supposed to handle this type issue to avoid getting 0 and instead getting the actual value converted into float64?
How am I supposed to handle this type issue to avoid getting 0 and instead getting the actual value converted into float64?
t, ok := i.(T)
This line of code checks whether the interface value i holds the concrete type T. If not, ok will be false and t will be the zero value of type T
price_response1, _ := response1["price"].(float64)
Here response1["price"] doesn't hold a type float64. Hence price_response1 has zero value of type float64, which is 0.
To print the underlying type of interface{} as a string you can use:
getType := fmt.Sprintf("%T", response1["price"])
fmt.Println(getType)
See the below code to get the actual value converted into float64, if the underlying type is int:
package main
import "fmt"
func convertToFloat64(resp interface{}) {
switch v := resp.(type) {
case int:
fmt.Println(float64(v), "==> price_response1")
case float64:
fmt.Println(v, "==> price_response")
default:
fmt.Println("unknown")
}
}
func main() {
response := make(map[string]interface{})
response["price"] = 2.1
convertToFloat64(response["price"])
response1 := make(map[string]interface{})
response1["price"] = 2
convertToFloat64(response1["price"])
}
Output:
2.1 ==> price_response
2 ==> price_response1
I've been working on a problem and I figured I would demonstrate it using a pokemon setup. I am reading from a file, parsing the file and creating objects/structs from them. This normally isn't a problem except now I need to implement interface like inheriting of traits. I don't want there to be duplicate skills in there so I figured I could use a map to replicate a set data structure. However it seems that in the transitive phase of my recursive parsePokemonFile function (see the implementsComponent case), I appear to be losing values in my map.
I am using the inputs like such:
4 files
Ratatta:
name=Ratatta
skills=Tackle:normal,Scratch:normal
Bulbosaur:
name=Bulbosaur
implements=Ratatta
skills=VineWhip:leaf
Oddish:
name=Oddish
implements=Ratatatt
skills=Acid:poison
Venosaur:
name=Venosaur
implements=bulbosaur,oddish
I'm expecting the output for the following code to be something like
Begin!
{Venosaur [{VineWhip leaf} {Acid poison} {Tackle normal} {Scratch normal}]}
but instead I get
Begin!
{Venosaur [{VineWhip leaf} {Acid poison}]}
What am I doing wrong? Could it be a logic error? Or am I making an assumption about the map holding values that I shouldn't?
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
// In order to create a set of pokemon abilities and for ease of creation and lack of space being taken up
// We create an interfacer capability that imports the skills and attacks from pokemon of their previous evolution
// This reduces the amount of typing of skills we have to do.
// Algorithm is simple. Look for the name "implements=x" and then add x into set.
// Unfortunately it appears that the set is dropping values on transitive implements interfaces
func main() {
fmt.Println("Begin!")
dex, err := parsePokemonFile("Venosaur")
if err != nil {
fmt.Printf("Got error: %v\n", err)
}
fmt.Printf("%v\n", dex)
}
type pokemon struct {
Name string
Skills []skill
}
type skill struct {
SkillName string
Type string
}
func parsePokemonFile(filename string) (pokemon, error) {
file, err := os.Open(filename)
if err != nil {
return pokemon{}, err
}
defer file.Close()
scanner := bufio.NewScanner(file)
var builtPokemon pokemon
for scanner.Scan() {
component, returned := parseLine(scanner.Text())
switch component {
case nameComponent:
builtPokemon.Name = returned
case skillsComponent:
skillsStrings := strings.Split(returned, ",")
var skillsArr []skill
// split skills and add them into pokemon skillset
for _, skillStr := range skillsStrings {
skillPair := strings.Split(skillStr, ":")
skillsArr = append(skillsArr, skill{SkillName: skillPair[0], Type: skillPair[1]})
}
builtPokemon.Skills = append(builtPokemon.Skills, skillsArr...)
case implementsComponent:
implementsArr := strings.Split(returned, ",")
// create set to remove duplicates
skillsSet := make(map[*skill]bool)
for _, val := range implementsArr {
// recursively call the pokemon files and get full pokemon
implementedPokemon, err := parsePokemonFile(val)
if err != nil {
return pokemon{}, err
}
// sieve out the skills into a set
for _, skill := range implementedPokemon.Skills {
skillsSet[&skill] = true
}
}
// append final set into the currently being built pokemon
for x := range skillsSet {
builtPokemon.Skills = append(builtPokemon.Skills, *x)
}
}
}
return builtPokemon, nil
}
type component int
// components to denote where to put our strings when it comes time to assemble what we've parsed
const (
nameComponent component = iota
implementsComponent
skillsComponent
)
func parseLine(line string) (component, string) {
arr := strings.Split(line, "=")
switch arr[0] {
case "name":
return nameComponent, arr[1]
case "implements":
return implementsComponent, arr[1]
case "skills":
return skillsComponent, arr[1]
default:
panic("Invalid field found")
}
}
This has nothing to do with Golang maps dropping any values.
The problem is that you are using a map of skill pointers and not skills. Two pointers to the same skill content can be different.
skillsSet := make(map[*skill]bool)
If you change this to map[skill]bool, this should work. You may try it out!
I need some help with unmarshaling. I have this example code:
package main
import (
"encoding/json"
"fmt"
)
type Obj struct {
Id string `json:"id"`
Data []byte `json:"data"`
}
func main() {
byt := []byte(`{"id":"someID","data":["str1","str2"]}`)
var obj Obj
if err := json.Unmarshal(byt, &obj); err != nil {
panic(err)
}
fmt.Println(obj)
}
What I try to do here - convert bytes to the struct, where type of one field is []byte. The error I get:
panic: json: cannot unmarshal string into Go struct field Obj.data of
type uint8
That's probably because parser already sees that "data" field is already a slice and tries to represent "str1" as some char bytecode (type uint8?).
How do I store the whole data value as one bytes array? Because I want to unmarshal the value to the slice of strings later. I don't include a slice of strings into struct because this type can change (array of strings, int, string, etc), I wish this to be universal.
My first recommendation would be for you to just use []string instead of []byte if you know the input type is going to be an array of strings.
If data is going to be a JSON array with various types, then your best option is to use []interface{} instead - Go will happily unmarshal the JSON for you and you can perform checks at runtime to cast those into more specific typed variables on an as-needed basis.
If []byte really is what you want, use json.RawMessage, which is of type []byte, but also implements the methods for JSON parsing. I believe this may be what you want, as it will accept whatever ends up in data. Of course, you then have to manually parse Data to figure out just what actually IS in there.
One possible bonus is that this skips any heavy parsing because it just copies the bytes over. When you want to use this data for something, you use a []interface{}, then use a type switch to use individual values.
https://play.golang.org/p/og88qb_qtpSGJ
package main
import (
"encoding/json"
"fmt"
)
type Obj struct {
Id string `json:"id"`
Data json.RawMessage `json:"data"`
}
func main() {
byt := []byte(`{"id":"someID","data":["str1","str2", 1337, {"my": "obj", "id": 42}]}`)
var obj Obj
if err := json.Unmarshal(byt, &obj); err != nil {
panic(err)
}
fmt.Printf("%+v\n", obj)
fmt.Printf("Data: %s\n", obj.Data)
// use it
var d []interface{}
if err := json.Unmarshal(obj.Data, &d); err != nil {
panic(err)
}
fmt.Printf("%+v\n", d)
for _, v := range d {
// you need a type switch to deterine the type and be able to use most of these
switch real := v.(type) {
case string:
fmt.Println("I'm a string!", real)
case float64:
fmt.Println("I'm a number!", real)
default:
fmt.Printf("Unaccounted for: %+v\n", v)
}
}
}
Your question is:
convert bytes array to struct with a field of type []byte
But you do not have a bytearray but a string array. Your question is not the same as your example. So let answer your question, there are more solutions possible depending in how far you want to diverge from your original requirements.
One string can be converted to one byte-slice, two strings need first to be transformed to one string. So that is problem one. The second problem are the square-brackets in your json-string
This works fine, it implicitly converts the string in the json-string to a byte-slice:
byt := []byte(`{"id":"someID","data":"str1str2"}`)
var obj Obj
if err := json.Unmarshal(byt, &obj); err != nil {
panic(err)
}
fmt.Println(obj)
I'm new to golang; however based on my current knowledge I understand that a value-type and a reference-type can both fulfill an interface. But it seems in regards to type assertion, how you return a struct does matter. See the following:
package main
import (
"fmt"
)
type SomeError interface {
Error() string
}
type ConcreteError struct{}
func (ConcreteError) Error() string {
return "?"
}
func returnPointer() SomeError {
return &ConcreteError{}
}
func returnVal() SomeError {
return ConcreteError{}
}
func main() {
pointer := returnPointer()
value := returnVal()
_, pointerWithPointer := pointer.(*ConcreteError);
_, pointerWithValue := pointer.(ConcreteError);
_, valueWithValue := value.(ConcreteError);
_, valueWithPointer := value.(*ConcreteError)
fmt.Printf("Returning a pointer, assert using (*ConcreteError): %v\n", pointerWithPointer); // true
fmt.Printf("Returning a pointer, assert using (ConcreteError): %v\n", pointerWithValue); // false
fmt.Printf("Returning a value, assert using (ConcreteError): %v\n", valueWithValue); // true
fmt.Printf("Returning a value, assert using (*ConcreteError): %v\n", valueWithPointer); // false
}
So if my understanding is correct, the user needs to know how the struct is returned to correctly assert its type?
I'm going to guess and assume the standard practice in golang is to always return a pointer to a struct(i.e like *PathError)?
link to play: here
So if my understanding is correct, the user needs to know how the struct is returned to correctly assert its type?
It depends. If you need the type assertion to pass - you surely need to know the exact type of the value. a and *a are different types.
I'm going to guess and assume the standard practice in golang is to always return a pointer to a struct(i.e like *PathError)?
No.