{"id":48135173,"url":"https://github.com/liip/sheriff","last_synced_at":"2026-04-04T16:44:28.537Z","repository":{"id":47410851,"uuid":"79792403","full_name":"liip/sheriff","owner":"liip","description":"Conditional marshalling for Go","archived":false,"fork":false,"pushed_at":"2024-10-03T06:46:57.000Z","size":96,"stargazers_count":256,"open_issues_count":3,"forks_count":33,"subscribers_count":40,"default_branch":"master","last_synced_at":"2025-10-25T03:51:22.636Z","etag":null,"topics":["golang","json","marshalling","reflection"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/liip/sheriff","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/liip.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-01-23T10:03:16.000Z","updated_at":"2025-09-12T15:00:05.000Z","dependencies_parsed_at":"2024-05-31T07:49:15.311Z","dependency_job_id":"f46fbd56-8554-42bf-a9e0-f4f485b63e8a","html_url":"https://github.com/liip/sheriff","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/liip/sheriff","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liip%2Fsheriff","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liip%2Fsheriff/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liip%2Fsheriff/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liip%2Fsheriff/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/liip","download_url":"https://codeload.github.com/liip/sheriff/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liip%2Fsheriff/sbom","scorecard":{"id":589540,"data":{"date":"2025-08-11","repo":{"name":"github.com/liip/sheriff","commit":"51bf2c98fbdcbeaace1f47cffcd0810d4fa41554"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.2,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/golangci-lint.yml:1","Warn: no topLevel permission defined: .github/workflows/test.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/golangci-lint.yml:14: update your workflow using https://app.stepsecurity.io/secureworkflow/liip/sheriff/golangci-lint.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/golangci-lint.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/liip/sheriff/golangci-lint.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/golangci-lint.yml:24: update your workflow using https://app.stepsecurity.io/secureworkflow/liip/sheriff/golangci-lint.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/liip/sheriff/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/liip/sheriff/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:23: update your workflow using https://app.stepsecurity.io/secureworkflow/liip/sheriff/test.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/test.yml:35: update your workflow using https://app.stepsecurity.io/secureworkflow/liip/sheriff/test.yml/master?enable=pin","Info:   0 out of   5 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":4,"reason":"Found 9/22 approved changesets -- score normalized to 4","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: BSD 3-Clause \"New\" or \"Revised\" License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 23 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-20T21:29:19.197Z","repository_id":47410851,"created_at":"2025-08-20T21:29:19.198Z","updated_at":"2025-08-20T21:29:19.198Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31405793,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["golang","json","marshalling","reflection"],"created_at":"2026-04-04T16:44:25.518Z","updated_at":"2026-04-04T16:44:28.516Z","avatar_url":"https://github.com/liip.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# sheriff\n[![GoDoc](https://godoc.org/github.com/liip/sheriff?status.svg)](https://godoc.org/github.com/liip/sheriff) [![Build Status](https://travis-ci.org/liip/sheriff.svg?branch=master)](https://travis-ci.org/liip/sheriff) [![Coverage Status](https://coveralls.io/repos/github/liip/sheriff/badge.svg?branch=master)](https://coveralls.io/github/liip/sheriff?branch=master)\n\n```\ngo get github.com/liip/sheriff/v2\n```\n\nPackage sheriff marshals structs conditionally based on tags on the fields. \n\nA typical use is an API which marshals structs into JSON and\nmaintains different API versions. Using sheriff, struct fields can be annotated\nwith API version and group tags. \n\nBy invoking sheriff with specific options,\nthose tags determine whether a field will be added to the output map or not. It\ncan then be marshalled using \"encoding/json\".\n\n**NOTE**: This package is tested only on Go 1.7+, it *might* work on Go 1.6 too, but no support is given.\n\n## Implemented tags\n\n### Groups\nGroups can be used for limiting the output based on freely defined parameters. For example: restrict marshalling the email\naddress of a user to the user itself by just adding the group `personal` if the user fetches his profile.\nMultiple groups can be separated by comma.\n\nExample:\n\n```go\ntype GroupsExample struct {\n    Username      string `json:\"username\" groups:\"api\"`\n    Email         string `json:\"email\" groups:\"personal\"`\n    SomethingElse string `json:\"something_else\" groups:\"api,personal\"`\n}\n```\n\n### Anonymous fields\n\nTags added to a struct’s anonymous field propagates to the inner-fields if no other tags are specified.\n\nExample:\n\n```go\ntype UserInfo struct {\n    UserPrivateInfo `groups:\"private\"`\n    UserPublicInfo  `groups:\"public\"`\n}\ntype UserPrivateInfo struct {\n    Age string\n}\ntype UserPublicInfo struct {\n    ID    string\n    Email string\n}\n``` \n### Since\nSince specifies the version since that field is available. It's inclusive and SemVer compatible using\n[github.com/hashicorp/go-version](https://github.com/hashicorp/go-version).\nIf you specify version `2` in a tag, this version will be output in case you specify version `\u003e=2.0.0` as the API version.\n\nExample:\n\n```go\ntype SinceExample struct {\n    Username string `json:\"username\" since:\"2.1.0\"`\n    Email    string `json:\"email\" since:\"2\"`\n}\n```\n\n### Until\nUntil specifies the version until that field is available. It's the opposite of since, inclusive and SemVer\ncompatible using [github.com/hashicorp/go-version](https://github.com/hashicorp/go-version).\nIf you specify version `2` in a tag, this version will be output in case you specify version `\u003c=2.0.0` as the API version.\n\nExample:\n\n```go\ntype UntilExample struct {\n    Username string `json:\"username\" until:\"2.1.0\"`\n    Email    string `json:\"email\" until:\"2\"`\n}\n```\n\n## Example\n\n```go\npackage main\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/hashicorp/go-version\"\n\t\"github.com/liip/sheriff/v2\"\n)\n\ntype User struct {\n\tUsername string   `json:\"username\" groups:\"api\"`\n\tEmail    string   `json:\"email\" groups:\"personal\"`\n\tName     string   `json:\"name\" groups:\"api\"`\n\tRoles    []string `json:\"roles\" groups:\"api\" since:\"2\"`\n}\n\ntype UserList []User\n\nfunc MarshalUsers(version *version.Version, groups []string, users UserList) ([]byte, error) {\n\to := \u0026sheriff.Options{\n\t\tGroups:     groups,\n\t\tApiVersion: version,\n\t}\n\n\tdata, err := sheriff.Marshal(o, users)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn json.MarshalIndent(data, \"\", \"  \")\n}\n\nfunc main() {\n\tusers := UserList{\n\t\tUser{\n\t\t\tUsername: \"alice\",\n\t\t\tEmail:    \"alice@example.org\",\n\t\t\tName:     \"Alice\",\n\t\t\tRoles:    []string{\"user\", \"admin\"},\n\t\t},\n\t\tUser{\n\t\t\tUsername: \"bob\",\n\t\t\tEmail:    \"bob@example.org\",\n\t\t\tName:     \"Bob\",\n\t\t\tRoles:    []string{\"user\"},\n\t\t},\n\t}\n\n\tv1, err := version.NewVersion(\"1.0.0\")\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tv2, err := version.NewVersion(\"2.0.0\")\n\n\toutput, err := MarshalUsers(v1, []string{\"api\"}, users)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tfmt.Println(\"Version 1 output:\")\n\tfmt.Printf(\"%s\\n\\n\", output)\n\n\toutput, err = MarshalUsers(v2, []string{\"api\"}, users)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tfmt.Println(\"Version 2 output:\")\n\tfmt.Printf(\"%s\\n\\n\", output)\n\n\toutput, err = MarshalUsers(v2, []string{\"api\", \"personal\"}, users)\n\tif err != nil {\n\t\tlog.Panic(err)\n\t}\n\tfmt.Println(\"Version 2 output with personal group too:\")\n\tfmt.Printf(\"%s\\n\\n\", output)\n\n}\n\n// Output:\n// Version 1 output:\n// [\n//   {\n//     \"name\": \"Alice\",\n//     \"username\": \"alice\"\n//   },\n//   {\n//     \"name\": \"Bob\",\n//     \"username\": \"bob\"\n//   }\n// ]\n//\n// Version 2 output:\n// [\n//   {\n//     \"name\": \"Alice\",\n//     \"roles\": [\n//       \"user\",\n//       \"admin\"\n//     ],\n//     \"username\": \"alice\"\n//   },\n//   {\n//     \"name\": \"Bob\",\n//     \"roles\": [\n//       \"user\"\n//     ],\n//     \"username\": \"bob\"\n//   }\n// ]\n//\n// Version 2 output with personal group too:\n// [\n//   {\n//     \"email\": \"alice@example.org\",\n//     \"name\": \"Alice\",\n//     \"roles\": [\n//       \"user\",\n//       \"admin\"\n//     ],\n//     \"username\": \"alice\"\n//   },\n//   {\n//     \"email\": \"bob@example.org\",\n//     \"name\": \"Bob\",\n//     \"roles\": [\n//       \"user\"\n//     ],\n//     \"username\": \"bob\"\n//   }\n// ]\n```\n\n## Output ordering\n\nSheriff converts the input struct into a basic structure using `map[string]interface{}`. This means that the generated \nJSON will not have the same ordering as the input struct. If you need to have a specific ordering then a custom \nimplementation of the `KVStoreFactory` can be passed as an option.\n\nProviding a custom KV Store is likely to have a negative impact on performance, as such it should be used only when \nnecessary.\n\nFor example:\n```go\npackage main\n\nimport (\n\t\"github.com/liip/sheriff/v2\"\n\torderedmap \"github.com/wk8/go-ordered-map/v2\"\n)\n\ntype OrderedMap struct {\n\t*orderedmap.OrderedMap[string, interface{}]\n}\n\nfunc NewOrderedMap() *OrderedMap {\n\treturn \u0026OrderedMap{orderedmap.New[string, interface{}]()}\n}\n\nfunc (om *OrderedMap) Set(k string, v interface{}) {\n\tom.OrderedMap.Set(k, v)\n}\n\nfunc (om *OrderedMap) Each(f func(k string, v interface{})) {\n\tfor pair := om.Newest(); pair != nil; pair = pair.Prev() {\n\t\tf(pair.Key, pair.Value)\n\t}\n}\n\nfunc main() {\n\topt := \u0026sheriff.Options{\n\t\tKVStoreFactory: func() sheriff.KVStore {\n\t\t\treturn NewOrderedMap()\n\t\t},\n\t}\n\n\t// ...\n}\n```\n\n## Benchmarks\n\nThere's a simple benchmark in `bench_test.go` which compares running sheriff -\u003e JSON versus just marshalling into JSON \nand runs on every build. Just marshalling JSON itself takes usually between 3 and 5 times less nanoseconds per operation\ncompared to running sheriff and JSON.\n\nWant to make sheriff faster? Please send us your pull request or open an issue discussing a possible improvement 🚀!\n\n## Acknowledgements\n\n- This idea and code has been created partially during a [Liip](https://liip.ch) hackday.\n- Thanks to [@basgys](https://github.com/basgys) for reviews \u0026 improvements.\n\n## Related projects\n\n[mweibel/php-to-go](https://github.com/mweibel/php-to-go) is a code generator translating PHP models (using [JMS serializer](https://jmsyst.com/libs/serializer)) to Go structs with sheriff tags. The two projects were initially developed together and just recently php-to-go has been split out and published too. \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliip%2Fsheriff","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fliip%2Fsheriff","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliip%2Fsheriff/lists"}