Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Golang convert interface{} to struct

Tags:

go

I want to improve the getCustomerFromDTO method in the code below, I need to create a struct from an interface{} and currently i need to marshall that interface to byte[] and then unmarshal the array to my struct - there must be a better way.

My use case is that I send structs via rabbitmq and to send them I use this general DTO wrapper that has additional domain specific data about them. When I receive the DTO from rabbit mq one layer below the message is unmarshaled to my DTO and then i need to get my struct from that DTO.

type Customer struct {
    Name string `json:"name"`
}

type UniversalDTO struct {
    Data interface{} `json:"data"`
    // more fields with important meta-data about the message...
}

func main() {
    // create a customer, add it to DTO object and marshal it
    customer := Customer{Name: "Ben"}
    dtoToSend := UniversalDTO{customer}
    byteData, _ := json.Marshal(dtoToSend)

    // unmarshal it (usually after receiving bytes from somewhere)
    receivedDTO := UniversalDTO{}
    json.Unmarshal(byteData, &receivedDTO)

    //Attempt to unmarshall our customer
    receivedCustomer := getCustomerFromDTO(receivedDTO.Data)
    fmt.Println(receivedCustomer)
}

func getCustomerFromDTO(data interface{}) Customer {
    customer := Customer{}
    bodyBytes, _ := json.Marshal(data)
    json.Unmarshal(bodyBytes, &customer)
    return customer
}
like image 563
Tomas Avatar asked Apr 10 '17 14:04

Tomas


People also ask

What is interface {} Golang?

interface{} means you can put value of any type, including your own custom type. All types in Go satisfy an empty interface ( interface{} is an empty interface). In your example, Msg field can have value of any type.

Can a struct have an interface Golang?

The interface is a contract of implicit behaviors (object methods) you invoke as needed without the rigors of explicit declaration. These methods are then added onto user-defined structs to create an interface that is about behavior, not data.


1 Answers

Before unmarshaling the DTO, set the Data field to the type you expect.

type Customer struct {
    Name string `json:"name"`
}

type UniversalDTO struct {
    Data interface{} `json:"data"`
    // more fields with important meta-data about the message...
}

func main() {
    // create a customer, add it to DTO object and marshal it
    customer := Customer{Name: "Ben"}
    dtoToSend := UniversalDTO{customer}
    byteData, _ := json.Marshal(dtoToSend)

    // unmarshal it (usually after receiving bytes from somewhere)
    receivedCustomer := &Customer{}
    receivedDTO := UniversalDTO{Data: receivedCustomer}
    json.Unmarshal(byteData, &receivedDTO)

    //done
    fmt.Println(receivedCustomer)
}

If you don't have the ability to initialize the Data field on the DTO before it's unmarshaled, you can use type assertion after the unmarshaling. Package encoding/json unamrshals interface{} type values into a map[string]interface{}, so your code would look something like this:

type Customer struct {
    Name string `json:"name"`
}

type UniversalDTO struct {
    Data interface{} `json:"data"`
    // more fields with important meta-data about the message...
}

func main() {
    // create a customer, add it to DTO object and marshal it
    customer := Customer{Name: "Ben"}
    dtoToSend := UniversalDTO{customer}
    byteData, _ := json.Marshal(dtoToSend)

    // unmarshal it (usually after receiving bytes from somewhere)
    receivedDTO := UniversalDTO{}
    json.Unmarshal(byteData, &receivedDTO)

    //Attempt to unmarshall our customer
    receivedCustomer := getCustomerFromDTO(receivedDTO.Data)
    fmt.Println(receivedCustomer)
}

func getCustomerFromDTO(data interface{}) Customer {
    m := data.(map[string]interface{})
    customer := Customer{}
    if name, ok := m["name"].(string); ok {
        customer.Name = name
    }
    return customer
}
like image 158
mkopriva Avatar answered Oct 01 '22 22:10

mkopriva