Giter VIP home page Giter VIP logo

mapstructure's Introduction

mapstructure

GitHub Workflow Status go.dev reference Go Version

mapstructure is a Go library for decoding generic map values to structures and vice versa, while providing helpful error handling.

This library is most useful when decoding values from some data stream (JSON, Gob, etc.) where you don't quite know the structure of the underlying data until you read a part of it. You can therefore read a map[string]interface{} and use this library to decode it into the proper underlying native Go structure.

Installation

go get github.com/go-viper/mapstructure/v2

Migrating from github.com/mitchellh/mapstructure

@mitchehllh announced his intent to archive some of his unmaintained projects (see here and here). This is a repository achieved the "blessed fork" status.

You can migrate to this package by changing your import paths in your Go files to github.com/go-viper/mapstructure/v2. The API is the same, so you don't need to change anything else.

Here is a script that can help you with the migration:

sed -i 's/github.com\/mitchellh\/mapstructure/github.com\/go-viper\/mapstructure\/v2/g' $(find . -type f -name '*.go')

If you need more time to migrate your code, that is absolutely fine.

Some of the latest fixes are backported to the v1 release branch of this package, so you can use the Go modules replace feature until you are ready to migrate:

replace github.com/mitchellh/mapstructure => github.com/go-viper/mapstructure v1.6.0

Usage & Example

For usage and examples see the documentation.

The Decode function has examples associated with it there.

But Why?!

Go offers fantastic standard libraries for decoding formats such as JSON. The standard method is to have a struct pre-created, and populate that struct from the bytes of the encoded format. This is great, but the problem is if you have configuration or an encoding that changes slightly depending on specific fields. For example, consider this JSON:

{
  "type": "person",
  "name": "Mitchell"
}

Perhaps we can't populate a specific structure without first reading the "type" field from the JSON. We could always do two passes over the decoding of the JSON (reading the "type" first, and the rest later). However, it is much simpler to just decode this into a map[string]interface{} structure, read the "type" key, then use something like this library to decode it into the proper structure.

Credits

Mapstructure was originally created by @mitchellh. This is a maintained fork of the original library.

Read more about the reasons for the fork here.

License

The project is licensed under the MIT License.

mapstructure's People

Contributors

mitchellh avatar sagikazarmark avatar dependabot[bot] avatar calvn avatar carlohamalainen avatar ryanuber avatar bastjan avatar tlipoca9 avatar ozgursoy avatar perenecabuto avatar skipor avatar suzuki-shunsuke avatar mkeeler avatar mcos avatar jen20 avatar gernoteger avatar dnephin avatar kochetkov-av avatar gyim avatar radeksimko avatar nobu-k avatar nmiyake avatar triarius avatar m1k1o avatar uvw avatar mback2k avatar bored-engineer avatar larstore-sonat avatar julnicolas avatar abhinav avatar

Stargazers

Burak Karakan avatar Elvin Zeng avatar  avatar FAN VINGA avatar Kevinello avatar  avatar Rahuλ Dé avatar João Victor Oliveira Couto avatar  avatar Trevor Foster avatar charmer avatar yusuke avatar Fanno Chea avatar rot1024 avatar  avatar Wiliam Souza avatar abbas ali chezgi avatar Jakub Chábek avatar  avatar  avatar Sem Rekkers avatar Isaac avatar Tsln avatar Tarun Ramakrishna Elankath avatar Eugene Ponizovsky avatar Ayodeji O. avatar  avatar Oleksandr Nosov avatar Valery avatar Filipe Tagliatti avatar Thiago Navarro avatar Trương Quang Chứ avatar Łukasz Pankowski avatar Patrick Bédat avatar Andrey Nabiullin avatar  avatar Eric Miller avatar Fabian Grutschus avatar  avatar Aakash Gajjar avatar Ekaterina Vinnik avatar Alexandre GV. avatar zhaolei avatar Chang avatar Lee avatar Amarjeet Singh Rai avatar Dmitriy Korzhev avatar Telanflow avatar Xudong Cai avatar Henrique Pires avatar  avatar 一代咩神 avatar  avatar Tiago Peczenyj avatar Mihai Todor avatar Qingshan Luo avatar thinkgos avatar It's me avatar  avatar Damian Peckett avatar Matheus Calegaro avatar Timo Sulg avatar Oleh Mykhalskyi avatar andig avatar Nicholas Molen avatar Motalleb Fallahnezhad avatar ik5 avatar Ben Brandt avatar Matt Johnson-Pint avatar Gabe Cook avatar Jacky avatar Juha Jantunen avatar montag451 avatar Tom avatar  avatar Budi avatar  avatar Jan Buschtöns avatar iain wright avatar Anton N Ryabkov avatar Jonathan Bluett-Duncan avatar Dmitry Razumov avatar Fatih Cetinkaya avatar Frank Perrakis avatar Roker2 (Dmitry Minko) avatar fioepq9 avatar dyma solovei avatar  avatar xu0o0 avatar Todd Kazakov avatar raffaele messuti avatar Ron avatar Mikael Johansson avatar Kuldeep avatar Daniel Michaels avatar Kevin Mingtarja avatar Lovro Mažgon avatar Aaron Jheng avatar  avatar Daniel Hakim avatar

mapstructure's Issues

Feature Request: Metadata when decoding into maps

I would like to be able to decode to a map, with deep processing of the data, and collect metadata about the keys that were decoded. For example:

actual := map[string]interface{}
config := &mapstructure.DecoderConfig{
  Metadata:   &mapstructure.Metadata{},
  Result:     actual,
}
decoder, err := mapstructure.NewDecoder(config)

decoder.Decode(map[string]interface{}{
  "a": map[string]interface{}{
    "b": "value",
  },
})
assert.Equals(t,  []string{"[a]", "[a][b]"}, config.Metadata.Keys)

At the moment, the nested map (a) is simply referred to directly in the target map, and the .Metadata.Keys contains only [a].

Performance of decode hook

Hello, thanks for having taken the time of forking https://github.com/mitchellh/mapstructure/

When using the original project noticed the dynamic decode hook was an expensive part of decoding due to it using reflect on the hook on each pass. Which can happens a lot due as soon as you have slices or nested struct.

I had opened this PR mitchellh#324 to pre compute the hook to typed method.

I think it would be easy to port to the fork, is that something that would interest you ? (In which case I would be interested in helping 😄 )

Fabrice

Usability of `DecodeHookFunc*` types

Hi 👋
Thanks for taking over the project!

I've noticed in the past (and today I'm noticing again), that it's not immediately obvious to me how to use the DecodeHookFunc* family of types/ funcs. Both times, I ended up diving into the code to understand how they work from reading the tests and implementations of StringTo{stdLibType}HookFuncs.
The code is very easy to read, so this wasn't exactly a bad experience 😄 But: I'd still expect the GoDoc to contain all the info I need to use them correctly.

IMO they could really use an example and/ or a section in the package overview. Therefore, I suggest to

Would anybody object if I created a PR to do this?

2.0 Release date?

There is currently a 2.0.0 alpha version which contains some useful fixes.

When I can expect a full 2.0.0 release, which is out of alpha?

DecodeHook for url.URL

Hello,
I'd like to add a decode hook for parsing url.URL. Would you accept a PR for it?

Allow DecodeHook to run for zero values

Use Case

In the OpenTelemetry Collector we support a configuration like this:

receiver:
  http:
  grpc:

Here the http: entry has no fields, but its presence indicate that the user wants an HTTP server, with all standard defaults. If http: is missing than this server will not be started.

Problem
Today we're handling it with a custom hook for the receiver: which catches the scenario and applies the defaults. The hook is brittle as it depends on the exact YAML field name.

We've been experimenting with a custom "Optional" type that would allow to express this much cleaner:

type ReceiverConfig struct {
  HTTP optional.Optional[HTTPServerConfig] `mapstructure:"http"`
}

func DefaultConfig() *ReceiverConfig {
  return &ReceiverConfig{
    HTTP: optional.WithDefault(defaultHTTPConfig), // passing a function
  }
}

func defaultHTTPConfig() HTTPServerConfig { ... }

Here optional.WithDefault returns Optional that has no value, but if during unmarshaling it sees that there was a corresponding entry it first creates default value and then runs normal unmarshal on it.

The issue is that this approach doesn't work today because when mapstructure sees an empty value http: it just exists, without running the decode hook (which could've made the result non-empty).

if input == nil {
// If the data is nil, then we don't set anything, unless ZeroFields is set
// to true.
if d.config.ZeroFields {
outVal.Set(reflect.Zero(outVal.Type()))
if d.config.Metadata != nil && name != "" {
d.config.Metadata.Keys = append(d.config.Metadata.Keys, name)
}
}
return nil
}

Proposal

Could we add another config flag to allow not bailing early on empty values, at least to allow decoder hooks to run?

Allow for multiple tags

I receive data from multiple sources and the key's of the data are not always the same.

For example, one source may provide a map similar to
{"LAT": 1, "LON": 2}

While another source may provide a map similar to
{"RLT_FOO": 1, "RLN_BAR": 2}

It would be very helpful if I could simply supply multiple tags for a single field.

For example

type Foo struct {
  Lat float64 `mapstructure:"LAT,RLT_FOO"`
  Lon float64 `mapstructure:"LON,RLN_BAR"`
}

The tags could be resolved in order and the first one that matches could be used.
This would avoid having to run a re-keying operation on maps before running through the mapstructure decoder.

I know there is the MatchName function but that only works if the variations are derivable from a base fieldName, if each field could have one of multiple unique keys it becomes a pain.

Also, if I'm just flat out missing a feature that would make this easy... please let me know.
Thanks

Mapstructure tags are ignored on slice struct items

I'm trying to convert a struct to a map, and can't figure out how to make it work.

Basically I have these structs:

type SQLStepResult struct {
	Queries []QueryResult `mapstructure:"queries"`
}

type QueryResult struct {
	Rows []map[string]any `mapstructure:"rows"`
}

and I want to convert SQLStepResult to:

{
  "queries": [
    {
      "rows": [
        // ...
      ]
    }
  ]
}

But I always end up with Rows instead of rows.

I tried using the undocumented mapstructure.RecursiveStructToMapHookFunc but it doesn't appear to do anything

Is there a solution?

Go playground for reference: https://go.dev/play/p/ZlTOw1mqnH5

How to customize the values mapped to the map?

package main

import (
	"fmt"
	"reflect"
	"time"

	"github.com/go-viper/mapstructure/v2"
)

type Person struct {
	Time time.Time `json:"time"`
	Name string    `json:"name"`
}

func main() {
	result := map[string]any{}
	input := Person{time.Now(), "testName"}

	decoder, errDecoder := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
		DecodeHook: func(f reflect.Type, t reflect.Type, data any) (any, error) {
			if value, ok := data.(*time.Time); ok {
				return value.Format(time.DateTime), nil
			}
			return data, nil
		},
		Result:  &result,
		TagName: "json",
	})

	if errDecoder != nil {
		panic(errDecoder)
	}

	err := decoder.Decode(input)
	if err != nil {
		panic(err)
	}

	fmt.Printf("%#v\n", result)
}

Here, I want to add the 'time' of the structure Convert the Time type to a string in the 'result', but it cannot be done. report errors:

panic: 'time' expected a map, got 'string'

goroutine 1 [running]:
main.main()
	C:/Users/78081/AppData/Roaming/JetBrains/GoLand2024.1/scratches/scratch.go:37 +0x1a6

Is there a solution?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.