{"id":13786686,"url":"https://github.com/faceair/jio","last_synced_at":"2025-06-17T01:05:43.253Z","repository":{"id":57495508,"uuid":"155061556","full_name":"faceair/jio","owner":"faceair","description":"jio is a json schema validator similar to joi","archived":false,"fork":false,"pushed_at":"2024-07-04T05:28:47.000Z","size":94,"stargazers_count":116,"open_issues_count":2,"forks_count":12,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-19T05:21:10.376Z","etag":null,"topics":["golang","joi","schema","validation"],"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/faceair.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-10-28T11:02:45.000Z","updated_at":"2025-03-15T23:20:45.000Z","dependencies_parsed_at":"2024-06-18T20:10:39.514Z","dependency_job_id":"abc7ff22-5eab-4a97-9938-745920957d33","html_url":"https://github.com/faceair/jio","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/faceair/jio","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faceair%2Fjio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faceair%2Fjio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faceair%2Fjio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faceair%2Fjio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/faceair","download_url":"https://codeload.github.com/faceair/jio/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/faceair%2Fjio/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260269429,"owners_count":22983645,"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":["golang","joi","schema","validation"],"created_at":"2024-08-03T19:01:28.494Z","updated_at":"2025-06-17T01:05:43.231Z","avatar_url":"https://github.com/faceair.png","language":"Go","readme":"\u003ch1 align=\"center\"\u003ejio\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"jio.jpg\" width=\"240\" height=\"240\" border=\"0\" alt=\"jio\"\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003eMake validation simple and efficient !\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://travis-ci.org/faceair/jio\"\u003e\u003cimg src=\"https://img.shields.io/travis/faceair/jio/master.svg\" alt=\"Travis branch\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://coveralls.io/github/faceair/jio?branch=master\"\u003e\u003cimg src=\"https://coveralls.io/repos/github/faceair/jio/badge.svg?branch=master\" alt=\"Coverage Status\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://goreportcard.com/report/github.com/faceair/jio\"\u003e\u003cimg src=\"https://goreportcard.com/badge/github.com/faceair/jio\" alt=\"Go Report Card\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://github.com/avelino/awesome-go\"\u003e\u003cimg src=\"https://awesome.re/mentioned-badge.svg\" alt=\"License\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://godoc.org/github.com/faceair/jio\"\u003e\u003cimg src=\"https://godoc.org/github.com/faceair/jio?status.svg\" alt=\"GoDoc\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://raw.githubusercontent.com/faceair/jio/master/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/License-MIT-blue.svg\" alt=\"License\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n[中文文档](README.zh.md)\n\n## Why use jio?\n\nParameter validation in Golang is really a cursing problem. Defining tags on structs is not easy to extend rules, handwritten validation code makes logic code cumbersome, and the initial zero value of the struct field will also interfere with the validation.\n\njio tries validate json raw data before deserialization to avoid these problems. Defining validation rules as Schema is easy to read and easy to extend (Inspired by Hapi.js joi library). Rules within Schema can be validated in the order of registration, and context can be used to exchange data between rules, and can access other field data even within a single rule, etc.\n\njio provides a flexible enough way to make your validation simple and efficient!\n\n## How to use?\n\n### Validate json string\n\n```go\npackage main\n\nimport (\n    \"log\"\n\n    \"github.com/faceair/jio\"\n)\n\nfunc main() {\n    data := []byte(`{\n        \"debug\": \"on\",\n        \"window\": {\n            \"title\": \"Sample Widget\",\n            \"size\": [500, 500]\n        }\n    }`)\n    _, err := jio.ValidateJSON(\u0026data, jio.Object().Keys(jio.K{\n        \"debug\": jio.Bool().Truthy(\"on\").Required(),\n        \"window\": jio.Object().Keys(jio.K{\n            \"title\": jio.String().Min(3).Max(18),\n            \"size\":  jio.Array().Items(jio.Number().Integer()).Length(2).Required(),\n        }).Without(\"name\", \"title\").Required(),\n    }))\n    if err != nil {\n        panic(err)\n    }\n    log.Printf(\"%s\", data) // {\"debug\":true,\"window\":{\"size\":[500,500],\"title\":\"Sample Widget\"}}\n}\n```\n\nThe above schema defines the following constraints:\n* `debug`\n    * not empty, must be a boolean value when validation end\n    * allow `on` string instead of `true`\n* `window`\n    * not empty, object\n    * not allowed for both `name` and `title`\n    * The following elements exist\n        * `title`\n            * string, can be empty\n            * length is between 3 and 18 when not empty\n        * `size`\n            * array, not empty\n            * there are two child elements of the integer type\n\n### Using middleware to validate request body\n\nTake [chi](https://github.com/go-chi/chi) as an example, the other frameworks are similar.\n\n```go\npackage main\n\nimport (\n    \"io/ioutil\"\n    \"net/http\"\n\n    \"github.com/faceair/jio\"\n    \"github.com/go-chi/chi\"\n)\n\nfunc main() {\n    r := chi.NewRouter()\n    r.Route(\"/people\", func(r chi.Router) {\n        r.With(jio.ValidateBody(jio.Object().Keys(jio.K{\n            \"name\":  jio.String().Min(3).Max(10).Required(),\n            \"age\":   jio.Number().Integer().Min(0).Max(100).Required(),\n            \"phone\": jio.String().Regex(`^1[34578]\\d{9}$`).Required(),\n        }), jio.DefaultErrorHandler)).Post(\"/\", func(w http.ResponseWriter, r *http.Request) {\n            body, err := ioutil.ReadAll(r.Body)\n            if err != nil {\n                panic(err)\n            }\n            w.Header().Set(\"Content-Type\", \"application/json; charset=utf-8\")\n            w.WriteHeader(http.StatusOK)\n            w.Write(body)\n        })\n    })\n    http.ListenAndServe(\":8080\", r)\n}\n```\nThe second parameter of `jio.ValidateBody` is called for error handling when the validation fails.\n\n### Validate the query parameter with middleware\n\n```go\npackage main\n\nimport (\n    \"encoding/json\"\n    \"net/http\"\n\n    \"github.com/faceair/jio\"\n    \"github.com/go-chi/chi\"\n)\n\nfunc main() {\n    r := chi.NewRouter()\n    r.Route(\"/people\", func(r chi.Router) {\n        r.With(jio.ValidateQuery(jio.Object().Keys(jio.K{\n            \"keyword\":  jio.String(),\n            \"is_adult\": jio.Bool().Truthy(\"true\", \"yes\").Falsy(\"false\", \"no\"),\n            \"starts_with\": jio.Number().ParseString().Integer(),\n        }), jio.DefaultErrorHandler)).Get(\"/\", func(w http.ResponseWriter, r *http.Request) {\n            query := r.Context().Value(jio.ContextKeyQuery).(map[string]interface{})\n            body, err := json.Marshal(query)\n            if err != nil {\n                panic(err)\n            }\n            w.Header().Set(\"Content-Type\", \"application/json; charset=utf-8\")\n            w.WriteHeader(http.StatusOK)\n            w.Write(body)\n        })\n    })\n    http.ListenAndServe(\":8080\", r)\n}\n```\nNote that the original value of the query parameter is string, you may need to convert the value type first (for example, `jio.Number().ParseString()` or `jio.Bool().Truthy(values)`).\n\n## API Documentation\n\n[https://godoc.org/github.com/faceair/jio](https://godoc.org/github.com/faceair/jio)\n\n## Advanced usage\n\n### Workflow\n\nEach Schema is made up of a series of rules, for example:\n\n```go\njio.String().Min(5).Max(10).Alphanum().Lowercase()\n```\n\nIn this example, String Schema has 4 rules, which are `Min(5)` `Max(10)` `Alphanum()` `Lowercase()`, will also validate in order `Min(5) ` `Max(10)` `Alphanum()` `Lowercase()`. If a rule validation fails, the Schema's validation stops and throws an error.\n\nIn order to improve the readability of the code, these three built-in rules will validate first.\n\n* `Required()`\n* `Optional()`\n* `Default(value)`\n\nFor example:\n\n```go\njio.String().Min(5).Max(10).Alphanum().Lowercase().Required()\n```\n\nThe actual validation order will be `Required()` `Min(5)` `Max(10)` `Alphanum()` `Lowercase()`.\n\nAfter validate all the rules, finally we check if the basic type of the data is the type of Schema. If not, the Schema will throw an error.\n\n### Validator Context\n\nData transfer in the workflow depends on context, the structure is like this:\n\n```go\nType Context struct {\n    Value interface{} // Raw data, you can also reassign to change the result\n}\nfunc (ctx *Context) Ref(refPath string) (value interface{}, ok bool) { // Reference other field data\n}\nfunc (ctx *Context) Abort(err error) { // Terminate the validation and throw an error\n  ...\n}\nfunc (ctx *Context) Skip() { // Skip subsequent rules\n  ...\n}\n```\n\nLet's try to customize a validation rule. Add a rule to use the `Transform` method:\n\n```go\njio.String().Transform(func(ctx *jio.Context) {\n    If ctx.Value != \"faceair\" {\n        ctx.Abort(errors.New(\"you are not faceair\"))\n    }\n})\n```\n\nThe custom rule we added means throwing a `you are not faceair` error when the original data is not equal to `faceair`.\n\nIn fact, the built-in validation rules work in a similar way. For example, the core code of `Optional()` is:\n\n```go\nIf ctx.Value == nil {\n  ctx.Skip()\n}\n```\n\nYou can also reassign ctx.Value to change the original data. For example, the built-in `Lowercase()` converts the original string to lowercase. The core code is:\n\n```go\nctx.Value = strings.ToLower(ctx.Value)\n```\n\n### References and Priority\n\nIn most cases, the rules only use the data of the current field, but sometimes it needs to work with other fields. For example:\n\n```\n{\n    \"type\": \"ip\", // enumeration value, `ip` or `domain`\n    \"value\": \"8.8.8.8\"\n}\n```\n\nThe validation rules of this `value` is determined by the value of `type` and can be written as\n\n```go\njio.Object().Keys(jio.K{\n        \"type\": jio.String().Valid(\"ip\", \"domain\").SetPriority(1).Default(\"ip\"),\n        \"value\": jio.String().\n            When(\"type\", \"ip\", jio.String().Regex(`^\\d+\\.\\d+\\.\\d+\\.\\d+$`)).\n            When(\"type\", \"domain\", jio.String().Regex(`^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0 -9]\\.[a-zA-Z]{2,}$`)).Required(),\n})\n```\n\nThe `When` function can reference other field data, and if it is successful, apply the new validation rule to the current data.\n\nIn addition, you may notice that there is a `SetPriority` method in the rules of `type`. If the input data is:\n\n```json\n{\n    \"value\": \"8.8.8.8\"\n}\n```\n\nWhen the priority is not set, the validation rule of `value` may be executed first. At this time, the value of the reference `type` will be null, and the validation will fail.\nBecause there are validation rules that refer to each other, there may be a validation sequence requirement. When we want a field under the same Object to be validated first, we can set it to a larger priority value (default value 0).\n\nIf you want to reference data from other fields in your custom rules, you can use the `Ref` method on the context. If the referenced data is a nested object, the path to the referenced field needs to be concatenated with `.` . For example, if you want to reference `name` under `people` object then the reference path is `people.name`:\n\n```json\n{\n    \"type\": \"people\",\n    \"people\": {\n        \"name\": \"faceair\"\n    }\n}\n```\n\n## License\n\n[MIT](LICENSE)\n","funding_links":[],"categories":["Validation","Utility","Go","校验库`用于校验的库`","验证","校验库"],"sub_categories":["Utility/Miscellaneous","HTTP Clients","查询语","实用程序/Miscellaneous","Fail injection"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffaceair%2Fjio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffaceair%2Fjio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffaceair%2Fjio/lists"}