Skip to content

Boost your Go projects with xMapper, a tool for easy struct mapping and real-time data validation and transformation. Say goodbye to repetitive coding as xMapper automates data changes and adds custom functions seamlessly. Streamline your data handling effortlessly with xMapper!

License

Notifications You must be signed in to change notification settings

dev3mike/go-xmapper

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

69 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

xMapper 🔄 - Dynamic Struct Validator and Mapper

Welcome to xMapper, where your Go structs gain superpowers! 🚀 Ever tired of manually copying fields from one struct to another? Forget about the hassle! xMapper automates the mapping of structs and dynamically transforms data as it flows from source to destination. It's not just about mapping; it's about transforming data effortlessly with power and style!

Features

  • Automatic Struct Mapping: Automate the boring stuff! Map fields between structs without writing boilerplate code.

  • Single Value Validation and Transforming: Validate and transform individual values to ensure they meet specific criteria or format requirements before they are used.

  • Dynamic Data Transformation: Apply transformations to your data dynamically as it's being mapped. Upper case, add suffixes, manipulate data on the go!

  • Extensible and Customizable: Easily extend xMapper by adding your own custom transformation functions.

  • Data Validation: Ensure data integrity with custom validators that check data before transformation.

  • Default Validation Rules: Check input using built-in validators for common requirements like email format, length, or specific starting/ending characters without writing custom code.

  • Error Handling: Robust error handling to let you know exactly what went wrong during the mapping process.

xMapper also provides powerful and flexible capabilities to automatically handle various types of conversions in Go. It can seamlessly map data between different structures, including:

  • JSON to Struct: Convert JSON strings into Go structs.
  • Struct to JSON: Convert Go structs into JSON strings.
  • JSON to Slice: Convert JSON strings into slices.
  • Slice to JSON: Convert slices into JSON strings.

Getting Started

Here’s how to get started with xMapper:

Installation

To start using xMapper in your Go project, simply use the go get command to retrieve the package:

go get github.com/dev3mike/go-xmapper

Ensure your environment is set up with Go modules (Go 1.11+ required), and this command will manage everything for you, fetching the latest version of xMapper and adding it to your project's dependencies.

Usage

Validate a single struct without mapping

If you don't need any mapping, you can directly validate the struct. You can also use transformers to ensure your data is clean. Exciting, isn't it?

	type User struct {
		Email string `json:"email" validators:"email" transformers:"trim,lowercase"`
	}

	user := User{Email: "[email protected]"}

	// Call ValidateStruct to check validation.
	err := xmapper.ValidateStruct(&user)

	// Check error type for validation errors
	if errors.Is(err, xmapper.ErrValidation){
		// Return bad request or ...
	}

Transformers

  1. Define Your Transformers: Create functions that match the TransformerFunc signature:
func  toUpperCase(input interface{}) interface{} {
	str, ok := input.(string)
	if ok {
		return strings.ToUpper(str)
	}
	return input
}
  1. Register Your Transformers: Before you map your structs, make sure to register your transformers:
xmapper.RegisterTransformer("toUpperCase", toUpperCase)
  1. Map Your Structs: Now let the magic happen:
type  Source  struct {
	Name string  `json:"name" transformers:"toUpperCase"`
}

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

src := Source{Name: "frodo"}
dest := Destination{}

err := xmapper.MapStructs(&src, &dest)

if err != nil {
	fmt.Println("Oops! Something went wrong:", err)
}

Map slice of structs You can use MapSliceOfStructs to map slice of structs

	xmapper.MapSliceOfStructs(&src, &dest)
  1. Validate/Transform single values:
email := "[email protected]"

transformedEmail, err := xmapper.ValidateSingleField(value, "validators:'isEmail' transformers:'toUpperCase'")

if err != nil {

	// Check error type for validation errors
	if errors.Is(err, xmapper.ErrValidation){
		// Return bad request or ...
	}

	fmt.Println("Oops! Something went wrong:", err)
	return
}

fmt.Printf("Hello %s", transformedEmail.(string))

Validate, Transform and Map JSON to Struct

	type ProfileDto struct {
		Email    string `json:"email" validators:"isEmail" transformers:"toUpperCase"`
		Age     int    `json:"age"`
	}


	jsonStr := `{"email":"[email protected]","age":30}`
    var profile ProfileDto

    err := xmapper.MapJsonStruct(jsonStr, &profile)
    if err != nil {

		// Check error type for validation errors
		if errors.Is(err, xmapper.ErrValidation){
			// Return bad request or ...
		}

        t.Errorf("MapJsonStruct failed: %s", err)
    }

Example with Error Handling

Want to ensure your transformers are set up correctly? Here’s how to handle errors:

err := xmapper.MapStructs(&src, &dest)
if err != nil {
	// Check error type for validation errors
	if errors.Is(err, xmapper.ErrValidation){
		// Return bad request or ...
	}

	fmt.Println("Failed to map structs:", err)
}

Default Transformers

You can use these default transformers without a need of registering them.

Transformer Description
uppercase Converts text to uppercase letters
lowercase Converts text to lowercase letters
trim Removes whitespace from both sides of the text
trimLeft Removes whitespace from the left side of the text
trimRight Removes whitespace from the right side of the text
base64Encode Encodes text to Base64 format
base64Decode Decodes Base64 text to original format
urlEncode Encodes text to be URL-friendly
urlDecode Decodes URL-encoded text to original format

Example Code:

type  Source  struct {
	Greeting string  `json:"greeting" transformers:"uppercase,trim"`
}

Using Multiple Transformers

xMapper allows you to apply multiple transformations to a single field in sequence, which can be extremely powerful for complex data manipulation. This section guides you through setting up and using multiple transformers on a single struct field.

Step 1: Define Your Transformers

First, define each transformer function. Each function should match the TransformerFunc signature. Here are examples of three simple transformers:

// Converts a string to uppercase
func  toUpperCase(input interface{}) interface{} {
	str, ok := input.(string)
	if ok {
		return strings.ToUpper(str)
	}
	return input
}

// Adds an exclamation mark at the end of a string

func  addExclamation(input interface{}) interface{} {
	str, ok := input.(string)
	if ok {
		return str + "!"
	}
	return input
}

// Repeats the string twice, separated by a space
func  repeatTwice(input interface{}) interface{} {
	str, ok := input.(string)
	if ok {
		return str + " " + str
	}
	return input
}

Step 2: Register Your Transformers

Register each transformer with xMapper before you attempt to map your structs:

func  init() {
	xmapper.RegisterTransformer("toUpperCase", toUpperCase)
	xmapper.RegisterTransformer("addExclamation", addExclamation)
	xmapper.RegisterTransformer("repeatTwice", repeatTwice)
}

Step 3: Set Up Your Structs

Define your source and destination structs. Use the transformer tag to specify multiple transformers separated by commas. The transformers will be applied in the order they are listed:

type  Source  struct {
	Greeting string  `json:"greeting" transformers:"toUpperCase,addExclamation,repeatTwice"`
}

type  Destination  struct {
	Greeting string  `json:"greeting"`
}

func  main() {
	src := Source{Greeting: "hello"}
	dest := Destination{}

	if  err := xmapper.MapStructs(&src, &dest); err != nil {
		fmt.Println("Error mapping structs:", err)
	} else {
		fmt.Println("Mapped Greeting:", dest.Greeting)
		// Output should be: "HELLO! HELLO!"
	}

}

Using Validators

Validators in xMapper ensure your data meets specific criteria before it's transformed and mapped to the destination struct. Validators can prevent invalid data from being processed and provide descriptive error messages if data validation fails.

You can use built-in validator as follows:

Validator Name Description
required Checks if the input is not empty.
email Validates that the input is a valid email address.
phone Checks if the input is a valid international phone number.
strongPassword Requires at least 8 characters, including upper, lower, digit, and special character.
date Validates that the input matches the YYYY-MM-DD date format.
time Validates that the input matches the HH:MM:SS time format.
datetime Validates date and time with timezone in YYYY-MM-DD HH:MM:SS format.
url Checks if the input is a valid URL.
ip Validates that the input is a valid IP address.
minLength Checks if the input has at least a specified minimum length.
maxLength Ensures the input does not exceed a specified maximum length.
gt Validates that a number is greater than a specified value.
lt Validates that a number is less than a specified value.
gte Checks if a number is greater than or equal to a specified value.
lte Checks if a number is less than or equal to a specified value.
range Validates that a number falls within a specified range.
enum Checks if the input matches one of a list of predefined values.
boolean Validates that the input is a boolean value.
contains Checks if the input contains one of the specified substrings.
notContains Ensures the input does not contain any of the specified substrings.
startsWith Validates that the input starts with a specified substring.
endsWith Checks if the input ends with a specified substring.

How to use built-in validators:

type  User  struct {
	Name string  `json:"name" validators:"required,minLength:3,maxLength:100"`
	Email string  `json:"email" validators:"email,maxLength:255"`
	Username string  `json:"username" validators:"minLength:6,maxLength:27"`
	Password string  `json:"password" validators:"strongPassword"`
	Type string  `json:"type" validators:"enum:buyer-seller"`
}

Use your own validation

If you need a custom validation logic, then you can register and use your own validator.

Step 1: Define Your Own Validators

First, define each validator function. Each function must match the ValidatorFunc signature, which typically returns true if the validation passes and false otherwise:

// Ensures the input string is not empty
// The second argument is always required. 
// It's used to receive specific settings for the validation. 
// For instance, for `minLength:10`, 
// the number `10` is the argument that sets the minimum length.
func  isNotEmpty(input interface{}, _ string) bool {
	str, ok := input.(string)
	return ok && str != ""
}

// Checks that the input string does not contain spaces
func  doesNotContainSpaces(input interface{}, _ string) bool {
	str, ok := input.(string)
	return ok && !strings.Contains(str, " ")
}

// Checks if the input string's length is at least the specified minimum
func  MinLengthValidator(input interface{}, length string) error {
	str, ok  := input.(string)
	if  !ok {
		return fmt.Errorf("failed to map the input to a string")
	}
	minLength, err  := strconv.Atoi(length)
	if err !=  nil {
		return fmt.Errorf("failed to convert length to integer")
	}
	if  len(str) < minLength {
		return fmt.Errorf("input does not meet the minimum length requirement, minimum length is %s", length)
	}
	return  nil
}

Step 2: Register Your Validators

Register each validator with xMapper just like you register transformers. This registration links the validator name with its corresponding function:

func  init() {
	xmapper.RegisterValidator("isNotEmpty", isNotEmpty)
	xmapper.RegisterValidator("doesNotContainSpaces", doesNotContainSpaces)
	xmapper.RegisterValidator("customMinLength", MinLengthValidator)
}

Step 3: Apply Validators to Your Structs

When defining your structs, use the validator tag to assign validators to struct fields. Multiple validators can be applied to a single field and are separated by commas. Validators will be executed in the order they are listed:

type  Person  struct {
	FirstName string  `json:"firstName" validators:"isNotEmpty,doesNotContainSpaces"`
	LastName string  `json:"lastName" validators:"isNotEmpty,customMinLength:4"`
}

type  Destination  struct {
	FirstName string  `json:"firstName"`
	LastName string  `json:"lastName"`
}

Step 4: Handle Validation Errors

When mapping your structs, handle any validation errors that might arise. If a validation fails, xMapper will return an error indicating which field and validator failed:

src := Person{FirstName: "John", LastName: "Doe"}
dest := Destination{}

err := xmapper.MapStructs(&src, &dest)
if err != nil {
	fmt.Println("Validation error:", err)
	return
}

fmt.Println("Data successfully validated and mapped:", dest)

Contributing

Got a cool idea for a new feature? Found a bug? We love contributions!

  1. Fork the repo.
  2. Create a new branch (git checkout -b cool-new-feature).
  3. Commit your changes (git commit -am 'Add some feature').
  4. Push to the branch (git push origin cool-new-feature).
  5. Create a new Pull Request.

License

Distributed under the MIT License. See LICENSE for more information.


Feel the power of easy struct transformations and focus on what really matters in your Go applications. Try xMapper today and say goodbye to boilerplate code! 🎉

About

Boost your Go projects with xMapper, a tool for easy struct mapping and real-time data validation and transformation. Say goodbye to repetitive coding as xMapper automates data changes and adds custom functions seamlessly. Streamline your data handling effortlessly with xMapper!

Resources

License

Stars

Watchers

Forks

Packages

No packages published