tagconv

package module
v1.4.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 28, 2021 License: MIT Imports: 4 Imported by: 0

README

Documentation Go Report Card Coverage Status MIT License

TagConv

Convert any Go Struct to a Map based on custom struct tags with dot notation.

Background

This package tries to simplify certain use-cases where the same struct needs to be organised/represented differently (eg: mapping data from the db to a presentable API JSON output). This would normally have to be done by having two different structs and manually mapping the data between each other.

This package allows you to use any custom struct tag to define the mapping. This mapping follows the dot-notation convention. Example:

Hello string `mytag:"hello.world"`

The above will result in a map with the JSON equivalent of:

{
    "hello": {
        "world": "hello world"
    }
}

Any number of custom tags can be used to represent the same struct in unlimited number of different ways. For examples, see below.

Usage & Examples

Import the package

import "github.com/dumim/tagconv"

Define your struct with custom struct tags:

type MyStruct struct {
    Age   string `foo:"age"`
    Year  int    `foo:"dob.year"`
    Month int    `foo:"dob.month"`
}

obj := MyStruct{
    Age:   "22",
    Year:  1998,
    Month: 1,
}

tagName = "foo"
myMap, err := ToMap(obj, tagName)
if err != nil {
    panic()
}

This will result in a map that looks like:

myMap = map[string]interface{}{
	"age": "22",
	"dob": map[string]interface{}{
	    "year": 1998,
	    "month": 1,
    }
}

Converting to JSON ...

myMapJSON, err := json.MarshalIndent(myMap, "", "    ")
    if err != nil {
    panic()
}
fmt.Print(myMapJSON)

... will result in something similar to:

{
  "age": "22",
  "dob": {
    "year": 1998,
    "month": 1
  }
}

Multiple struct tags

You can use multiple struct tags for different representation of the same struct: For example, similar to the previous example:

type MyStructMultiple struct {
    Age   string `foo:"age" bar:"details.my_age"`
}

obj := MyStruct{
    Age:   "22",
}

Using tagconv for obj over the foo tag (ToMap(obj, "foo")) will result in:

{
  "age": "22"
}

whereas using bar (ToMap(obj, "bar")) on the same obj will result in:

{
  "details": {
    "my_age": "22"
  }
}

Tag options
  • If a nested struct has a tag, this will create a parent-child relationship with that tag and the tags of the fields within that struct.
  • Dot notation will create a parent-child relationship for every ..
  • Not setting any tag will ignore that field, unless if it's a struct; then it will go inside the struct to check its tags
  • - will explicitly ignore that field. As opposed to above, it will not look inside even if the field is of struct type.

For an example that includes all the above scenarios see the code below:

More complex example

Given a deeply-nested complex struct with custom tags like below:

type Obj struct {
	Name  string `custom:"name"`
	Text  string `custom:"text"`
	World string `custom:"data.world"` // dot notation inside nested struct
}
type ObjTwo struct {
	Hello string `custom:"hello"`
	Text  string `custom:"data.text"`
}
type ObjThree struct {
	Name  string `custom:"name"`
	Value int    `custom:"value"`
}
type Example struct {
	Name     string     `custom:"name"`
	Email    string     `custom:"email"`
	Obj      Obj        `custom:"object"`
	ObjTwo   ObjTwo     // no tag
	ObjThree ObjTwo     `custom:"-"` // explicitly ignored
	Id       int        `custom:"id"`
	Call     int        `custom:"data.call"` // top-level dot notation
	ArrayObj []ObjThree `custom:"list"`
}

The ToMap function can be used to convert this into a JSON/Map based on the values defined in the given custom tag like so.

obj := Example{
    Name:  "2",
    Email: "3",
    Obj: Obj{
        Name:  "4",
        Text:  "5",
        World: "6",
    },
    ObjTwo: ObjTwo{
        Hello: "1",
        Text:  "2",
    },
    Id:   01,
    Call: 02,
    ArrayObj: []ObjThree{
        {"hi", 1},
        {"world", 2},
    },
}

// get the map from custom tags
tagName = "custom"
myMap, err := ToMap(obj, tagName)
if err != nil {
    panic()
}

myMapJSON, err := json.MarshalIndent(myMap, "", "    ")
if err != nil {
    panic()
}
fmt.Print(myMapJSON)

This will produce a result similar to:

{
    "name": "2",
    "email": "3",
    "object": {
        "name": "4",
        "text": "5",
        "data": {
        "world": "6"
        }
    },
    "hello": "1",
    "data": {
        "text": "2",
        "call": 2
    },
    "id": 1,
    "list": [
        {
            "name": "hi",
            "value": 1
        },
        {
            "name": "world",
            "value": 2
        }
    ]
}

Testing

Run the go tests using go test ./.. -v


Acknowledgements

Contributing

Contributions are always welcome!

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ToMap

func ToMap(obj interface{}, tag string) (*map[string]interface{}, error)

ToMap creates a map based on the custom struct tag: `tag` values these values can be written in dot notation to create complex nested maps for a more comprehensive example, please see the

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL