Robust & Easy to use struct mapper and utility methods for Go

go-model Stability: Sustained Build Status codecov GoReport Version GoDoc License

Robust & Easy to use model mapper and utility methods for Go struct. Typical methods increase productivity and make Go development more fun 😄

v1.1.0 released and tagged on Aug 27, 2018

go-model tested with Go v1.2 and above.

Features

go-model library provides handy methods to process struct with below highlighted features. It's born from typical need while developing Go application or utility. I hope it's helpful to Go community!

  • Embedded/Anonymous struct
  • Multi-level nested struct/map/slice
  • Pointer and non-pointer within struct/map/slice
  • Struct within map and slice
  • Embedded/Anonymous struct fields appear in map at same level as represented by Go
  • Interface within struct/map/slice
  • Get struct field reflect.Kind by field name
  • Get all the struct field tags (reflect.StructTag) or selectively by field name
  • Get all reflect.StructField for given struct instance
  • Get or Set by individual field name on struct
  • Add global no traverse type to the list or use notraverse option in the struct field
  • Options to name map key, omit empty fields, and instruct not to traverse with struct/map/slice
  • Conversions between mixed non-pointer types - add custom conversation method, refer to usage

Installation

Stable Version - Production Ready

Please refer section Versioning for detailed info.

go.mod

require gopkg.in/jeevatkm/go-model.v1 v1.1.0

go get

go get -u gopkg.in/jeevatkm/go-model.v1

It might be beneficial for your project 😄

go-model author also published following projects to Go Community.

Usage

Import go-model into your code and refer it as model. Have a look on model test cases to know more possibilities.

import (
  "gopkg.in/jeevatkm/go-model.v1"
)

Supported Methods

Copy Method

How do I copy my struct object into another? Not to worry, go-model does deep copy.

// let's say you have just decoded/unmarshalled your request body to struct object.
tempProduct, _ := myapp.ParseJSON(request.Body)

product := Product{}

// tag your Product fields with appropriate options like
// -, omitempty, notraverse to get desired result.
// Not to worry, go-model does deep copy :)
errs := model.Copy(&product, tempProduct)
fmt.Println("Errors:", errs)

fmt.Printf("\nSource: %#v\n", tempProduct)
fmt.Printf("\nDestination: %#v\n", product)

Map Method

I want to convert my struct into Map (map[string]interface{}). Sure, go-model does deep convert.

// tag your SearchResult fields with appropriate options like
// -, name, omitempty, notraverse to get desired result.
sr, _ := myapp.GetSearchResult( /* params here */ )

// Embedded/Anonymous struct fields appear in map at same level as represented by Go
srchResMap, err := model.Map(sr)
fmt.Println("Error:", err)

fmt.Printf("\nSearch Result Map: %#v\n", srchResMap)

Clone Method

I would like to clone my struct object. That's nice, you know go-model does deep processing.

input := Product { /* Product struct field values go here */ }

// have your struct fields tagged appropriately. Options like
// -, name, omitempty, notraverse to get desired result.
clonedObj := model.Clone(input)

// let's see the result
fmt.Printf("\nCloned Object: %#v\n", clonedObj)

IsZero Method

I want to check my struct object is empty or not. Of course, go-model does deep zero check.

// let's say you have just decoded/unmarshalled your request body to struct object.
productInfo, _ := myapp.ParseJSON(request.Body)

// wanna check productInfo is empty or not
isEmpty := model.IsZero(productInfo)

// tag your ProductInfo fields with appropriate options like
// -, omitempty, notraverse to get desired result.
fmt.Println("Hey, I have all fields zero value:", isEmpty)

HasZero Method

I want to check my struct object has any zero/empty value. Of course, go-model does deep zero check.

// let's say you have just decoded/unmarshalled your request body to struct object.
productInfo, _ := myapp.ParseJSON(request.Body)

// wanna check productInfo is empty or not
isEmpty := model.HasZero(productInfo)

// tag your ProductInfo fields with appropriate options like
// -, omitempty, notraverse to get desired result.
fmt.Println("Hey, I have zero values:", isEmpty)

IsZeroInFields Method

Is it possible to check to particular fields has zero/empty values. Of-course you can.

// let's say you have just decoded/unmarshalled your request body to struct object.
product, _ := myapp.ParseJSON(request.Body)

// check particular fields has zero value or not
fieldName, isEmpty := model.IsZeroInFields(product, "SKU", "Title", "InternalIdentifier")

fmt.Println("Empty Field Name:", fieldName)
fmt.Println("Yes, I have zero value:", isEmpty)

Fields Method

You wanna all the fields from struct, Yes you can have it :)

src := SampleStruct {
  /* struct fields go here */
}

fields, _ := model.Fields(src)
fmt.Println("Fields:", fields)

Kind Method

go-model library provides an ability to know the reflect.Kind in as easy way.

src := SampleStruct {
  /* struct fields go here */
}

fieldKind, _ := model.Kind(src, "BookingInfoPtr")
fmt.Println("Field kind:", fieldKind)

Tag Method

I want to get Go lang supported Tag value from my struct. Yes, it is easy to get it.

src := SampleStruct {
	BookCount      int         `json:"-"`
	BookCode       string      `json:"-"`
	ArchiveInfo    BookArchive `json:"archive_info,omitempty"`
	Region         BookLocale  `json:"region,omitempty"`
}

tag, _ := model.Tag(src, "ArchiveInfo")
fmt.Println("Tag Value:", tag.Get("json"))

// Output:
Tag Value: archive_info,omitempty

Tags Method

I would like to get all the fields Tag values from my struct. It's easy.

src := SampleStruct {
	BookCount      int         `json:"-"`
	BookCode       string      `json:"-"`
	ArchiveInfo    BookArchive `json:"archive_info,omitempty"`
	Region         BookLocale  `json:"region,omitempty"`
}

tags, _ := model.Tags(src)
fmt.Println("Tags:", tags)

Get Method

I want to get value by field name on my struct. Yes, it is easy to get it.

src := SampleStruct {
	BookCount: 100,
	BookCode:  "GHT67HH00",
}

value, _ := model.Get(src, "BookCode")
fmt.Println("Value:", value)

// Output:
Value: GHT67HH00

Set Method

I want to set value by field name on my struct. Yes, it is easy to get it.

src := SampleStruct {
	BookCount: 100,
	BookCode:  "GHT67HH00",
}

err := model.Set(&src, "BookCount", 200)
fmt.Println("Error:", err)

AddNoTraverseType & RemoveNoTraverseType Methods

There are scenarios, where you want the object values but not to traverse/look inside the struct object. Use notraverse option in the model tag for those fields or Add it NoTraverseTypeList. Customize it as per your need.

Default NoTraverseTypeList has these types time.Time{}, &time.Time{}, os.File{}, &os.File{}, http.Request{}, &http.Request{}, http.Response{}, &http.Response{}.

// If you have added your type into list then you need not mention `notraverse` option for those types.

// Adding type into NoTraverseTypeList
model.AddNoTraverseType(time.Location{}, &time.Location{})

// Removing type from NoTraverseTypeList
model.RemoveNoTraverseType(time.Location{}, &time.Location{})

AddConversion & RemoveConversion Methods

This example registers a custom conversion from the int to the string type.

AddConversion((*int)(nil), (*string)(nil), func(in reflect.Value) (reflect.Value, error) {
		return reflect.ValueOf(strconv.FormatInt(in.Int(), 10)), nil
	})

If a an integer field on the source struct matches the name of a string field on the target struct, the provided Converter method is invoked.

Note that if you want to register a converter from int to *string you will have to provide a pointer to a pointer as destination type ( (**string)(nil) ).

More examples can be found in the AddConversion godoc.

Versioning

go-model releases versions according to Semantic Versioning

gopkg.in/jeevatkm/go-model.vX points to appropriate tag versions; X denotes version number and it's a stable release. It's recommended to use version, for eg. gopkg.in/jeevatkm/go-model.v0. Development takes place at the master branch. Although the code in master should always compile and test successfully, it might break API's. We aim to maintain backwards compatibility, but API's and behaviour might be changed to fix a bug.

Contributing

Welcome! If you find any improvement or issue you want to fix, feel free to send a pull request. I like pull requests that include test cases for fix/enhancement. I have done my best to bring pretty good code coverage. Feel free to write tests.

BTW, I'd like to know what you think about go-model. Kindly open an issue or send me an email; it'd mean a lot to me.

Author

Jeevanandam M. - [email protected]

Contributors

Have a look on Contributors page.

License

go-model released under MIT license, refer LICENSE file.

Owner
Jeevanandam M.
Creator of aah framework, go-resty, THUMBAI, go-model, RWH, DigitalOcean Java Client and others
Jeevanandam M.
Comments
  • Add support for mapping mixed types

    Add support for mapping mixed types

    Hi,

    I wanted to map my uuid.UUID type from a field "UUID" to a string field called "UUID". Since this was not possible I added a feature to add custom converters for specific types.

    I also added some unit tests. It seems to work but I want to write a little bit more tests and I have some documentation TODOs. Before I do the polishing I wanted to share it with you, to hear what you think about it.

  • Allow destination to not have all src fields

    Allow destination to not have all src fields

    In order to do mapping from internal objects to different external data transfer objects, it would be nice if we could say that "missing fields" on the destination are not an error.

    Currently I get errors like

    Field: 'Y', does not exists
    

    For illustration:

    type DomainObject struct {
       Name string
       Address string
       Phone string
    }
    
    type LoginGreeterDTO struct {
      Name string
    }
    
    model.Copy(&loginGreetingDTO, &domainObject )
    
  • Add Kind, Tag, Tags methods

    Add Kind, Tag, Tags methods

    • Kind - getting kind for given field
    • Tag - getting Tag (reflect.StructTag) for given field
    • Tags - Get all the Tags (reflect.StructTag) from the struct - return type is map[string]reflect.StructTag
  • bug: fail to copy field named with struct

    bug: fail to copy field named with struct

    go-model won't copy filed with the same type name,

    following are the reproduce code.

    package main
    
    import (
    	"encoding/json"
    	"log"
    
    	"golang.org/x/oauth2"
    	go_model "gopkg.in/jeevatkm/go-model.v1"
    )
    
    type Endpoint struct {
    	AuthURL  string `yaml:"auth_url" validate:"required,min=1"`
    	TokenURL string `yaml:"token_url" validate:"required,min=1"`
    
    	AuthStyle oauth2.AuthStyle `yaml:"auth_style" validate:"required,oneof=0 1 2"`
    }
    
    type OauthConfig struct {
    	ClientID     string `yaml:"client_id" validate:"required,min=1"`
    	ClientSecret string `yaml:"client_secret" validate:"required,min=1"`
    	Endpoint     Endpoint
    	RedirectURL  string   `yaml:"redirect_url"  validate:"required,min=1"`
    	Scopes       []string `yaml:"scopes" example:"state"`
    }
    
    func DumpJson(obj interface{}) string {
    	data, _ := json.Marshal(obj)
    	return string(data)
    }
    
    func main() {
    	var sample = OauthConfig{
    		ClientID:     "aa",
    		ClientSecret: "bb",
    		Endpoint: Endpoint{
    			AuthURL:   "https://sample.com",
    			TokenURL:  "https://sample.com",
    			AuthStyle: 1,
    		},
    		RedirectURL: "https://sample.com",
    		Scopes:      []string{"state"},
    	}
    	var d = oauth2.Config{}
    	go_model.Copy(&d, sample)
    
    	log.Println(DumpJson(d))
    }
    

    sample code output:

    2022/03/24 11:16:32 {"ClientID":"aa","ClientSecret":"bb","Endpoint":{"AuthURL":"","TokenURL":"","AuthStyle":0},"RedirectURL":"https://sample.com","Scopes":["state"]}
    

    Endpoint fields are empty.

  • AddConversionByType for time.Time and timestamppb.Timestamp

    AddConversionByType for time.Time and timestamppb.Timestamp

    I'm trying to convert from time.Time to timestamppb.Timestamp. This is the code I have written below but it doesn't work, and return such an error panic: reflect.Set: value of type time.Time is not assignable to type *timestamppb.Timestamp

    Conversion:

    	srcType := reflect.TypeOf((*time.Time)(nil)).Elem()
    	targetType := reflect.TypeOf((**timestamppb.Timestamp)(nil)).Elem()
    	model.AddConversionByType(srcType, targetType, func(in reflect.Value) (reflect.Value, error) {
    		t := timestamppb.New(in.Interface().(time.Time))
    		return reflect.ValueOf(&t), nil
    	})
    
    
    struct A {
     CreatedAt timeTime
    }
    
    struct B {
     CreatedAt *timestamppb.Timestamp
    }
    

    Is there anyone who can show me what I'm doing wrong here? cc: @jeevatkm @rmohr @CodinCat

Copier for golang, copy value from struct to struct and more

Copier I am a copier, I copy everything from one to another Features Copy from field to field with same name Copy from method to field with same name

Jan 8, 2023
Go linter to check the struct literal to use field name

Structfield Find struct literals using non-labeled fields. The structfield analysis reports the usage of struct literal using non-labeled fields more

Aug 23, 2021
Golang 1.18+ Generics implementation of Set methods

Golang Generics: Set A golang 1.18+ implementation of Set using Go generics Installation $ go get -u github.com/chrispappas/golang-generics-set Quick

Oct 26, 2022
Use is a go utility library using go1.18 generics

use use is a go utility library using go1.18 generics created by halpdesk 2022-01-22 use/slice Map updates a slice by applying a function to all membe

Jan 22, 2022
Easy to use, light enough, good performance Golang library
 Easy to use, light enough, good performance Golang library

指令使用 特性 简单易用、足够轻量,避免过多的外部依赖,最低兼容 Window 7 等老系统 快速上手 安装 $ go get github.com/sohaha/zlsgo HTTP 服务 // main.go

Dec 29, 2022
Easy to use open source hardware to drive WS2811 LEDs with high-quality color

STOP DOING FADECANDY LEDs were not supposed to be given data pins YEARS of "temporal dithering" but no real-world use found for having more than three

Dec 29, 2022
A Runtime Struct Builder for Go

A Runtime Struct Builder for Go

Jul 8, 2022
Go tool to modify struct field tags
Go tool to modify struct field tags

Go tool to modify/update field tags in structs. gomodifytags makes it easy to update, add or delete the tags in a struct field. You can easily add new tags, update existing tags (such as appending a new key, i.e: db, xml, etc..) or remove existing tags

Jan 1, 2023
gin struct controller

gin struct controller

Oct 4, 2021
Tugas Alta Immersive Backend Golang Fundamental Programming (Pointer, Struct, Method, Interface)
Tugas Alta Immersive Backend Golang Fundamental Programming (Pointer, Struct, Method, Interface)

Tatacara Melakukan Setup Tugas clone project ini dengan cara git clone https://github.com/Immersive-Backend-Resource/Pointer-Struct-Method-Interface.g

Jan 9, 2022
Highly configurable struct to map converter.

Mapify Highly configurable struct to map converter. Will convert maps into other maps as well (work in progress). Features configuration outside the s

Jul 30, 2022
🍕 Enjoy a slice! A utility library for dealing with slices and maps that focuses on type safety and performance.

?? github.com/elliotchance/pie Enjoy a slice! pie is a library of utility functions for common operations on slices and maps. Quick Start FAQ What are

Dec 30, 2022
Scylla-octopus is a backup and maintenance utility for scylladb.

scylla-octopus: a scylladb backup utility Scylla-octopus is a backup and maintenance utility for scylladb. It attempts to reproduce some functionality

Oct 19, 2022
Simple utility to get/set the PWM duty cycle and to measure the RPM for a fan connected to the 4-pin header on the CM4IO.

cm4iofan Simple utility to get/set the PWM duty cycle and to measure the RPM for a fan connected to the 4-pin header on the CM4IO. Requirements Enable

Mar 31, 2022
Envoy utility to process envoy config for fast development and debugging.

envoyconf-tools Envoy is a proxy, really awesome and we are devs who often use it, face errors and struggle to debug it, when envoy config's source is

Oct 31, 2021
The utility that created for easily database and their tables rolling up

The utility that created for easily database and their tables rolling up

Nov 6, 2021
A super simple Lodash like utility library with essential functions that empowers the development in Go
A super simple Lodash like utility library with essential functions that empowers the development in Go

A simple Utility library for Go Go does not provide many essential built in functions when it comes to the data structure such as slice and map. This

Jan 4, 2023
gopkg is a universal utility collection for Go, it complements offerings such as Boost, Better std, Cloud tools.

gopkg is a universal utility collection for Go, it complements offerings such as Boost, Better std, Cloud tools. Table of Contents Introduction

Jan 5, 2023
Utility to add network config file in apk

Utility to add network config file in apk. Which bypass the proxy intercept restriction for user installed burpsuit CA certificate.

Aug 19, 2022