{"id":16179781,"url":"https://github.com/bep/tmc","last_synced_at":"2025-03-19T01:31:03.304Z","repository":{"id":57488755,"uuid":"204685921","full_name":"bep/tmc","owner":"bep","description":"Provides basic roundtrip JSON etc. encoding/decoding of a map[string]any with custom type adapters.","archived":false,"fork":false,"pushed_at":"2022-02-13T11:04:06.000Z","size":45,"stargazers_count":9,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-17T01:41:22.385Z","etag":null,"topics":["go","golang","json"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bep.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-08-27T11:11:07.000Z","updated_at":"2024-04-27T13:02:20.000Z","dependencies_parsed_at":"2022-08-29T15:10:12.620Z","dependency_job_id":null,"html_url":"https://github.com/bep/tmc","commit_stats":null,"previous_names":["bep/typedmapcodec"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bep%2Ftmc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bep%2Ftmc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bep%2Ftmc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bep%2Ftmc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bep","download_url":"https://codeload.github.com/bep/tmc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244336158,"owners_count":20436777,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["go","golang","json"],"created_at":"2024-10-10T05:44:00.345Z","updated_at":"2025-03-19T01:31:03.082Z","avatar_url":"https://github.com/bep.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch2 align=\"center\"\u003eCodec for a Typed Map\u003c/h2\u003e\n\u003cp align=\"center\"\u003eProvides round-trip serialization of typed Go maps.\u003cp\u003e\n\u003cp align=\"center\"\u003e\u003ca href=\"https://godoc.org/github.com/bep/tmc\"\u003e\u003cimg src=\"https://godoc.org/github.com/bep/tmc?status.svg\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://goreportcard.com/report/github.com/bep/tmc\"\u003e\u003cimg src=\"https://goreportcard.com/badge/github.com/bep/tmc\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://codecov.io/gh/bep/tmc\"\u003e\u003cimg src=\"https://codecov.io/gh/bep/tmc/branch/master/graph/badge.svg\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/bep/tmc/actions\"\u003e\u003cimg src=\"https://action-badges.now.sh/bep/tmc?workflow=test\" /\u003e\u003c/a\u003e\u003c/p\u003e\n\n\n### How to Use\n\nSee the [GoDoc](https://godoc.org/github.com/bep/tmc) for some basic examples and how to configure custom codec, adapters etc.\n\n### Why?\n\nText based serialization formats like JSON and YAML are convenient, but when used with Go maps, most type information gets lost in translation.\n\nListed below is a round-trip example in JSON (see https://play.golang.org/p/zxt-wi4Ljz3 for a runnable version):\n\n```go\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"log\"\n\t\"math/big\"\n\t\"time\"\n\n\t\"github.com/kr/pretty\"\n)\n\nfunc main() {\n\tmi := map[string]interface{}{\n\t\t\"vstring\":   \"Hello\",\n\t\t\"vint\":      32,\n\t\t\"vrat\":      big.NewRat(1, 2),\n\t\t\"vtime\":     time.Now(),\n\t\t\"vduration\": 3 * time.Second,\n\t\t\"vsliceint\": []int{1, 3, 4},\n\t\t\"nested\": map[string]interface{}{\n\t\t\t\"vint\":      55,\n\t\t\t\"vduration\": 5 * time.Second,\n\t\t},\n\t\t\"nested-typed-int\": map[string]int{\n\t\t\t\"vint\": 42,\n\t\t},\n\t\t\"nested-typed-duration\": map[string]time.Duration{\n\t\t\t\"v1\": 5 * time.Second,\n\t\t\t\"v2\": 10 * time.Second,\n\t\t},\n\t}\n\n\tdata, err := json.Marshal(mi)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tm := make(map[string]interface{})\n\tif err := json.Unmarshal(data, \u0026m); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tpretty.Print(m)\n\n}\n```\n\nThis prints:\n\n```go\nmap[string]interface {}{\n    \"vint\":      float64(32),\n    \"vrat\":      \"1/2\",\n    \"vtime\":     \"2009-11-10T23:00:00Z\",\n    \"vduration\": float64(3e+09),\n    \"vsliceint\": []interface {}{\n        float64(1),\n        float64(3),\n        float64(4),\n    },\n    \"vstring\": \"Hello\",\n    \"nested\":  map[string]interface {}{\n        \"vduration\": float64(5e+09),\n        \"vint\":      float64(55),\n    },\n    \"nested-typed-duration\": map[string]interface {}{\n        \"v2\": float64(1e+10),\n        \"v1\": float64(5e+09),\n    },\n    \"nested-typed-int\": map[string]interface {}{\n        \"vint\": float64(42),\n    },\n}\n```\n\nAnd that is very different from the origin:\n\n* All numbers are now `float64`\n* `time.Duration` is also `float64`\n* `time.Now` and `*big.Rat` are strings\n* Slices are `[]interface {}`, maps `map[string]interface {}`\n\nSo, for structs, you can work around some of the limitations above with custom `MarshalJSON`, `UnmarshalJSON`, `MarshalText` and `UnmarshalText`. \n\nFor the commonly used flexible and schema-less `map[string]interface {}` this is, as I'm aware of, not an option.\n\nUsing this library, the above can be written to (see https://play.golang.org/p/PlDetQP5aWd for a runnable example):\n\n```go\npackage main\n\nimport (\n\t\"log\"\n\t\"math/big\"\n\t\"time\"\n\n\t\"github.com/bep/tmc\"\n\n\t\"github.com/kr/pretty\"\n)\n\nfunc main() {\n\tmi := map[string]interface{}{\n\t\t\"vstring\":   \"Hello\",\n\t\t\"vint\":      32,\n\t\t\"vrat\":      big.NewRat(1, 2),\n\t\t\"vtime\":     time.Now(),\n\t\t\"vduration\": 3 * time.Second,\n\t\t\"vsliceint\": []int{1, 3, 4},\n\t\t\"nested\": map[string]interface{}{\n\t\t\t\"vint\":      55,\n\t\t\t\"vduration\": 5 * time.Second,\n\t\t},\n\t\t\"nested-typed-int\": map[string]int{\n\t\t\t\"vint\": 42,\n\t\t},\n\t\t\"nested-typed-duration\": map[string]time.Duration{\n\t\t\t\"v1\": 5 * time.Second,\n\t\t\t\"v2\": 10 * time.Second,\n\t\t},\n\t}\n\n\tc, err := tmc.New()\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tdata, err := c.Marshal(mi)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tm := make(map[string]interface{})\n\tif err := c.Unmarshal(data, \u0026m); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tpretty.Print(m)\n\n}\n```\n\nThis prints:\n\n```go\nmap[string]interface {}{\n    \"vduration\":        time.Duration(3000000000),\n    \"vint\":             int(32),\n    \"nested-typed-int\": map[string]int{\"vint\":42},\n    \"vsliceint\":        []int{1, 3, 4},\n    \"vstring\":          \"Hello\",\n    \"vtime\":            time.Time{\n        wall: 0x0,\n        ext:  63393490800,\n        loc:  (*time.Location)(nil),\n    },\n    \"nested\": map[string]interface {}{\n        \"vduration\": time.Duration(5000000000),\n        \"vint\":      int(55),\n    },\n    \"nested-typed-duration\": map[string]time.Duration{\"v1\":5000000000, \"v2\":10000000000},\n    \"vrat\":                  \u0026big.Rat{\n        a:  big.Int{\n            neg: false,\n            abs: {0x1},\n        },\n        b:  big.Int{\n            neg: false,\n            abs: {0x2},\n        },\n    },\n}\n```\n\n\n### Performance\n\nThe implementation is easy to reason about (it uses reflection), but It's not particulary fast and probably not suited for _big data_. A simple benchmark with a roundtrip marshal/unmarshal is included. On my MacBook it shows:\n\n```bash\nBenchmarkCodec/JSON_regular-16         \t   63921\t     16261 ns/op\t    6486 B/op\t     163 allocs/op\nBenchmarkCodec/JSON_typed-16           \t   31791\t     37396 ns/op\t   14538 B/op\t     387 allocs/op\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbep%2Ftmc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbep%2Ftmc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbep%2Ftmc/lists"}