Skip to content

Input and output formats

Goverter supports three different input output format combinations. This guide is for you to decide the formats you want to use.

interface to struct

This is the default when using goverter:converter.

Pros:

  • All goverter features are supported
  • The interface is usable before goverter generated the implementation.
    • reduces the occurence of compile errors because of missing or outdated generated implementation.
    • allows using generated methods in custom methods

Cons:

  • You need to initialize the implementation.
  • You need to call methods on the struct to execute conversions
Example (click me)
go
package house

import (
	"database/sql"

	"github.com/jmattheis/goverter/example/format/common"
)

// goverter:converter
// goverter:output:format struct
// goverter:extend SQLStringToPString
// goverter:extend ConvertToApartmentMap
type Converter interface {
	ConvertHouse(source common.DBHouse) common.APIHouse
	// goverter:map Owner.Name OwnerName
	ConvertApartment(source common.DBApartment) common.APIApartment
	// goverter:map Name FirstName
	// goverter:ignore Age
	ConvertPerson(source common.DBPerson) common.APIPerson
}

func SQLStringToPString(value sql.NullString) *string {
	if value.Valid {
		return &value.String
	}
	return nil
}

func ConvertToApartmentMap(c Converter, source []common.DBApartment) map[uint]common.APIApartment {
	m := make(map[uint]common.APIApartment)
	for _, apartment := range source {
		m[apartment.Position] = c.ConvertApartment(apartment) // !! this is not supported in some formats
	}
	return m
}
go
package common

import "database/sql"

type DBHouse struct {
	Address    string
	Apartments []DBApartment
}

type DBApartment struct {
	Position   uint
	Owner      DBPerson
	CoResident []DBPerson
}

type DBPerson struct {
	ID         int
	Name       string
	MiddleName sql.NullString
	Friends    []DBPerson
	Info       Info
}

type APIHouse struct {
	Address    string
	Apartments map[uint]APIApartment
}

type APIApartment struct {
	Position   uint
	Owner      APIPerson
	OwnerName  string
	CoResident []APIPerson
}

type APIPerson struct {
	ID         int
	MiddleName *string
	FirstName  *string
	Friends    []APIPerson
	Info       Info
	Age        int
}

type Info struct {
	Birthplace string
}
go
// Code generated by github.com/jmattheis/goverter, DO NOT EDIT.
//go:build !goverter

package generated

import (
	common "github.com/jmattheis/goverter/example/format/common"
	interfacetostruct "github.com/jmattheis/goverter/example/format/interfacetostruct"
)

type ConverterImpl struct{}

func (c *ConverterImpl) ConvertApartment(source common.DBApartment) common.APIApartment {
	var commonAPIApartment common.APIApartment
	commonAPIApartment.Position = source.Position
	commonAPIApartment.Owner = c.ConvertPerson(source.Owner)
	commonAPIApartment.OwnerName = source.Owner.Name
	if source.CoResident != nil {
		commonAPIApartment.CoResident = make([]common.APIPerson, len(source.CoResident))
		for i := 0; i < len(source.CoResident); i++ {
			commonAPIApartment.CoResident[i] = c.ConvertPerson(source.CoResident[i])
		}
	}
	return commonAPIApartment
}
func (c *ConverterImpl) ConvertHouse(source common.DBHouse) common.APIHouse {
	var commonAPIHouse common.APIHouse
	commonAPIHouse.Address = source.Address
	commonAPIHouse.Apartments = interfacetostruct.ConvertToApartmentMap(c, source.Apartments)
	return commonAPIHouse
}
func (c *ConverterImpl) ConvertPerson(source common.DBPerson) common.APIPerson {
	var commonAPIPerson common.APIPerson
	commonAPIPerson.ID = source.ID
	commonAPIPerson.MiddleName = interfacetostruct.SQLStringToPString(source.MiddleName)
	pString := source.Name
	commonAPIPerson.FirstName = &pString
	if source.Friends != nil {
		commonAPIPerson.Friends = make([]common.APIPerson, len(source.Friends))
		for i := 0; i < len(source.Friends); i++ {
			commonAPIPerson.Friends[i] = c.ConvertPerson(source.Friends[i])
		}
	}
	commonAPIPerson.Info = c.commonInfoToCommonInfo(source.Info)
	return commonAPIPerson
}
func (c *ConverterImpl) commonInfoToCommonInfo(source common.Info) common.Info {
	var commonInfo common.Info
	commonInfo.Birthplace = source.Birthplace
	return commonInfo
}

variables to assign-variable

This is the default when using goverter:variables.

Pros:

  • All goverter features are supported.
  • The variables are usable before goverter generated the implementation.
    • reduces the occurence of compile errors because of missing or outdated generated implementation.
    • allows using generated functions in custom methods
  • You can execute conversions directly without having to initializing a struct

Cons:

  • Possible runtime overhead when Go cannot optimizen variables the same as functions. Benchmark your use-case, if speed is really important to you.
Example (click me)
go
package house

import (
	"database/sql"

	"github.com/jmattheis/goverter/example/format/common"
)

// goverter:variables
// goverter:output:format assign-variable
// goverter:extend SQLStringToPString
// goverter:extend ConvertToApartmentMap
var (
	ConvertHouse func(source common.DBHouse) common.APIHouse
	// goverter:map Name FirstName
	// goverter:ignore Age
	ConvertPerson func(source common.DBPerson) common.APIPerson
	// goverter:map Owner.Name OwnerName
	ConvertApartment func(source common.DBApartment) common.APIApartment
)

func SQLStringToPString(value sql.NullString) *string {
	if value.Valid {
		return &value.String
	}
	return nil
}

func ConvertToApartmentMap(source []common.DBApartment) map[uint]common.APIApartment {
	m := make(map[uint]common.APIApartment)
	for _, apartment := range source {
		m[apartment.Position] = ConvertApartment(apartment) // !! this is not supported in some formats
	}
	return m
}
go
package common

import "database/sql"

type DBHouse struct {
	Address    string
	Apartments []DBApartment
}

type DBApartment struct {
	Position   uint
	Owner      DBPerson
	CoResident []DBPerson
}

type DBPerson struct {
	ID         int
	Name       string
	MiddleName sql.NullString
	Friends    []DBPerson
	Info       Info
}

type APIHouse struct {
	Address    string
	Apartments map[uint]APIApartment
}

type APIApartment struct {
	Position   uint
	Owner      APIPerson
	OwnerName  string
	CoResident []APIPerson
}

type APIPerson struct {
	ID         int
	MiddleName *string
	FirstName  *string
	Friends    []APIPerson
	Info       Info
	Age        int
}

type Info struct {
	Birthplace string
}
go
// Code generated by github.com/jmattheis/goverter, DO NOT EDIT.
//go:build !goverter

package house

import common "github.com/jmattheis/goverter/example/format/common"

func init() {
	ConvertApartment = func(source common.DBApartment) common.APIApartment {
		var commonAPIApartment common.APIApartment
		commonAPIApartment.Position = source.Position
		commonAPIApartment.Owner = ConvertPerson(source.Owner)
		commonAPIApartment.OwnerName = source.Owner.Name
		if source.CoResident != nil {
			commonAPIApartment.CoResident = make([]common.APIPerson, len(source.CoResident))
			for i := 0; i < len(source.CoResident); i++ {
				commonAPIApartment.CoResident[i] = ConvertPerson(source.CoResident[i])
			}
		}
		return commonAPIApartment
	}
	ConvertHouse = func(source common.DBHouse) common.APIHouse {
		var commonAPIHouse common.APIHouse
		commonAPIHouse.Address = source.Address
		commonAPIHouse.Apartments = ConvertToApartmentMap(source.Apartments)
		return commonAPIHouse
	}
	ConvertPerson = func(source common.DBPerson) common.APIPerson {
		var commonAPIPerson common.APIPerson
		commonAPIPerson.ID = source.ID
		commonAPIPerson.MiddleName = SQLStringToPString(source.MiddleName)
		pString := source.Name
		commonAPIPerson.FirstName = &pString
		if source.Friends != nil {
			commonAPIPerson.Friends = make([]common.APIPerson, len(source.Friends))
			for i := 0; i < len(source.Friends); i++ {
				commonAPIPerson.Friends[i] = ConvertPerson(source.Friends[i])
			}
		}
		commonAPIPerson.Info = commonInfoToCommonInfo(source.Info)
		return commonAPIPerson
	}
}
func commonInfoToCommonInfo(source common.Info) common.Info {
	var commonInfo common.Info
	commonInfo.Birthplace = source.Birthplace
	return commonInfo
}

interface to functions

Pros:

  • You can execute conversions directly without having to initializing a struct

Cons:

  • The interface is only used for defining the conversions and is otherwise not usable
  • The use-case map method with converter is unsupported without replacement.
Example (click me)
go
package house

import (
	"database/sql"

	"github.com/jmattheis/goverter/example/format/common"
)

// goverter:converter
// goverter:output:format function
// goverter:extend SQLStringToPString
type Converter interface {
	// goverter:map Owner.Name OwnerName
	ConvertApartment(source common.DBApartment) common.APIApartment
	// goverter:map Name FirstName
	// goverter:ignore Age
	ConvertPerson(source common.DBPerson) common.APIPerson
}

func SQLStringToPString(value sql.NullString) *string {
	if value.Valid {
		return &value.String
	}
	return nil
}
go
package common

import "database/sql"

type DBHouse struct {
	Address    string
	Apartments []DBApartment
}

type DBApartment struct {
	Position   uint
	Owner      DBPerson
	CoResident []DBPerson
}

type DBPerson struct {
	ID         int
	Name       string
	MiddleName sql.NullString
	Friends    []DBPerson
	Info       Info
}

type APIHouse struct {
	Address    string
	Apartments map[uint]APIApartment
}

type APIApartment struct {
	Position   uint
	Owner      APIPerson
	OwnerName  string
	CoResident []APIPerson
}

type APIPerson struct {
	ID         int
	MiddleName *string
	FirstName  *string
	Friends    []APIPerson
	Info       Info
	Age        int
}

type Info struct {
	Birthplace string
}
go
// Code generated by github.com/jmattheis/goverter, DO NOT EDIT.
//go:build !goverter

package generated

import (
	common "github.com/jmattheis/goverter/example/format/common"
	interfacefunction "github.com/jmattheis/goverter/example/format/interfacefunction"
)

func ConvertApartment(source common.DBApartment) common.APIApartment {
	var commonAPIApartment common.APIApartment
	commonAPIApartment.Position = source.Position
	commonAPIApartment.Owner = ConvertPerson(source.Owner)
	commonAPIApartment.OwnerName = source.Owner.Name
	if source.CoResident != nil {
		commonAPIApartment.CoResident = make([]common.APIPerson, len(source.CoResident))
		for i := 0; i < len(source.CoResident); i++ {
			commonAPIApartment.CoResident[i] = ConvertPerson(source.CoResident[i])
		}
	}
	return commonAPIApartment
}
func ConvertPerson(source common.DBPerson) common.APIPerson {
	var commonAPIPerson common.APIPerson
	commonAPIPerson.ID = source.ID
	commonAPIPerson.MiddleName = interfacefunction.SQLStringToPString(source.MiddleName)
	pString := source.Name
	commonAPIPerson.FirstName = &pString
	if source.Friends != nil {
		commonAPIPerson.Friends = make([]common.APIPerson, len(source.Friends))
		for i := 0; i < len(source.Friends); i++ {
			commonAPIPerson.Friends[i] = ConvertPerson(source.Friends[i])
		}
	}
	commonAPIPerson.Info = commonInfoToCommonInfo(source.Info)
	return commonAPIPerson
}
func commonInfoToCommonInfo(source common.Info) common.Info {
	var commonInfo common.Info
	commonInfo.Birthplace = source.Birthplace
	return commonInfo
}