0

I got a map[string]interface{} because decoding to JSON; with normal data, interface most be only a number but in type string, like this :

var a interface{}
a="3"

And then all data will be stored into a struct.

type someStruct struct {
   ID string
   Number1 int
   Number2 int
   Number3 int
   Number4 int
}

So I need to convert interface to int, but can´t do it easily and efficiently because only the code would be https://play.golang.org/p/oktbvTUbk93, pretty annoying and code does not seem to be readable if you take in mind the fact that I should handle all of possible errors

I would want to convert it directly to int, I´ve been searching for solutions but any of this convertions works as I want https://play.golang.org/p/Dw67U6kZwHC

In case you wondering why I don´t just decode it into struct directly, it´s because it´s dynamic data, the actual decoding occurs like this :

type dataIn struct {
   Code int         `json:"code"`
   ID   string      `json:"id"`
   Data interface{} `json:"data"`
}

And then I handle Data according to code and id, and they all are different data structures, so I can´t directly handle it with JSON

mkopriva
  • 35,176
  • 4
  • 57
  • 71
John Balvin Arias
  • 2,632
  • 3
  • 26
  • 41
  • 1
    Use the [blank identifier](https://golang.org/ref/spec#Blank_identifier) to use values. For example, `_, _ = ok, err`. – peterSO Mar 30 '18 at 07:15
  • If the problem space is wide your code will be wide too. No magic here. Do some programming. – Volker Mar 30 '18 at 07:46
  • The json package can encode and decode numbers as strings: https://play.golang.org/p/MdrfqQjXLkw. See [this question](https://stackoverflow.com/questions/48907574/go-unmarshal-nested-unknown-fields/48909049) for how to deal with inconsistent fields. – Peter Mar 30 '18 at 11:11
  • @peterSO If I do that it will continue to be inefficient, because there will be one variable assigment and then I can store it into destine – John Balvin Arias Mar 30 '18 at 14:28
  • @Peter "In case you wondering why I don´t just decode it into struct directly, it´s because it´s dynamic data" – John Balvin Arias Mar 30 '18 at 14:29
  • @Volker maybe, but I was looking opinions for a optimal solution – John Balvin Arias Mar 30 '18 at 14:30

3 Answers3

3

Create a helper function which does parsing and validating for you, in one place:

func parseInt(i interface{}) (int, error) {
    s, ok := i.(string)
    if !ok {
        return 0, errors.New("not string")
    }
    return strconv.Atoi(s)
}

And you can use this where needed. Here's a complete example, in which I also used another helper function, which takes care of the error handling:

m := map[string]interface{}{
    "number1": "1",
    "number2": "2",
    "number3": "3",
    "number4": "4",
    "ID":      "asdsa",
    "Title":   "asdas",
}

getInt := func(key string) int {
    n, err := parseInt(m[key])
    if err != nil {
        panic(err) // Decide what you wanna do with error
    }
    return n
}

// converting to struct
data := element{
    ID:      m["ID"].(string),
    Title:   m["Title"].(string),
    Number1: getInt("number1"),
    Number2: getInt("number2"),
    Number3: getInt("number3"),
    Number4: getInt("number4"),
}

fmt.Printf("%+v\n", data)

Output of the above (try it on the Go Playground):

{ID:asdsa Title:asdas Number1:1 Number2:2 Number3:3 Number4:4}

Also note that the open source package github.com/icza/dyno should help you handle dynamic objects at ease. (Disclosure: I'm the author.) It has a dyno.GetInteger() function for example, which is capable of extracting an int64 value out of multiple types (such as integers, floats, strings etc.).

icza
  • 389,944
  • 63
  • 907
  • 827
  • your solution was pretty simple compared as I tried to do it by the way. Although this solve the actual question, there is a solution in order not to need to convert anything in first place – John Balvin Arias Mar 30 '18 at 16:27
0

Sounds like what you need is a custom unmarshal json method on your main struct. First, unmarshal into your main struct to get your code and id, then use those in a switch statement to determine which struct to use for the rest of the data and unmarshal into that, stirring that struct in your Data field.

darkliquid
  • 4,003
  • 1
  • 25
  • 17
  • For that scenario I would have to unmarshal twice, I think this is not possible, could you add an example please – John Balvin Arias Mar 30 '18 at 14:47
  • https://golang.org/pkg/encoding/json/#RawMessage is a good example of exactly this kind of behaviour (look at the decode example) – darkliquid Mar 30 '18 at 15:23
  • Also, this is the kind of thing I was talking about using a custom unmarshal method: https://play.golang.org/p/ZyKhnXQ-nFB – darkliquid Mar 30 '18 at 15:41
  • I didn´t know I could do that, also that custom marshal seems to be what I need because using rawMessage it would make my code bigger, Thanks! – John Balvin Arias Mar 30 '18 at 16:19
0

I still didn't get the part where you stated the struct is generated dynamically. Anyways, you can have a struct method attached, that does the int conversion. If the Data field, which is of type interface{} is always gonna hold integers, you an try this:

type DataIn struct {
  Code int         `json:"code"`
  ID   string      `json:"id"`
  Data interface{} `json:"data"`
}

func (s DataIn) toInt() int {
   switch t := s.Data.(type)
   case int:
     i, _ := strings.Atoi(fmt.Sprintf("%v",s.Data))
     return i
}

// using it
sampleData := someStruct{
  Number1: datain.toInt(),
}
codehakase
  • 20
  • 5