{"id":37214826,"url":"https://github.com/karimagnusson/go-picker","last_synced_at":"2026-01-15T00:51:16.790Z","repository":{"id":321996671,"uuid":"1087860756","full_name":"karimagnusson/go-picker","owner":"karimagnusson","description":"Json data picker for Go","archived":false,"fork":false,"pushed_at":"2026-01-12T00:02:35.000Z","size":37,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-01-12T04:19:58.214Z","etag":null,"topics":["api","configuration","golang","json","json-parser","strict-parsing","type-safety","validation"],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/karimagnusson.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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-01T19:46:30.000Z","updated_at":"2026-01-12T00:02:39.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/karimagnusson/go-picker","commit_stats":null,"previous_names":["karimagnusson/go-picker"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/karimagnusson/go-picker","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karimagnusson%2Fgo-picker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karimagnusson%2Fgo-picker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karimagnusson%2Fgo-picker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karimagnusson%2Fgo-picker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/karimagnusson","download_url":"https://codeload.github.com/karimagnusson/go-picker/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karimagnusson%2Fgo-picker/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28440568,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-15T00:34:46.850Z","status":"ssl_error","status_checked_at":"2026-01-15T00:34:46.551Z","response_time":107,"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":["api","configuration","golang","json","json-parser","strict-parsing","type-safety","validation"],"created_at":"2026-01-15T00:51:16.166Z","updated_at":"2026-01-15T00:51:16.777Z","avatar_url":"https://github.com/karimagnusson.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# go-picker\n\n**Type-safe JSON validation for Go with clear error messages**\n\nParse JSON data and validate all required fields in one operation. No repetitive error checking, no silent failures with zero values.\n\n## Installation\n\n```bash\ngo get github.com/karimagnusson/go-picker\n```\n\n## Quick Example\n\n```go\ntype User struct {\n    Name  string\n    Email string\n    Age   int64\n}\n\njsonStr := `{\"name\": \"John\", \"email\": \"john@example.com\", \"age\": 30}`\n\nuser, err := picker.PickFromJson(jsonStr, func(p *picker.Picker) User {\n    return User{\n        Name:  p.GetString(\"name\"),\n        Email: p.GetString(\"email\"),\n        Age:   p.GetInt(\"age\"),\n    }\n})\n```\n\n## The Problem\n\nTraditional JSON parsing in Go has two options, both problematic:\n\n**Option 1: `json.Unmarshal`** - Silent failures\n\n```go\nvar user User\njson.Unmarshal([]byte(jsonStr), \u0026user)\n// Missing fields become zero values: \"\", 0, nil\n// No way to know if data was actually validated\n```\n\n**Option 2: Manual validation** - Verbose and repetitive\n\n```go\nname, ok := data[\"name\"].(string)\nif !ok {\n    return errors.New(\"invalid name\")\n}\nemail, ok := data[\"email\"].(string)\nif !ok {\n    return errors.New(\"invalid email\")\n}\n// ... repeat for every field\n```\n\n## The Solution\n\ngo-picker validates **all required fields** and collects **all errors** in one pass:\n\n```go\nuser, err := picker.PickFromJson(jsonStr, func(p *picker.Picker) User {\n    return User{\n        Name:  p.GetString(\"name\"),\n        Email: p.GetString(\"email\"),\n        Age:   p.GetInt(\"age\"),\n    }\n})\n// Automatically validates - no forgotten Confirm() calls\n// Returns detailed errors for ALL missing/invalid fields\n```\n\n## Features\n\n-   **Automatic validation** - All fields validated in one operation, no manual error checking\n-   **Detailed error reporting** - Returns a map of all validation errors with field paths\n-   **Type-safe parsing** - Direct conversion from JSON to your structs\n-   **Nested objects and arrays** - Clean API for complex JSON structures\n-   **Customizable error messages** - Built-in support for internationalization\n\n## API\n\n### Pick Functions\n\n```go\n// Pick from parsed data\npicker.Pick(data, func(p *picker.Picker) T { ... })\n\n// Pick from JSON string\npicker.PickFromJson(jsonStr, func(p *picker.Picker) T { ... })\n\n// Pick from HTTP request body\npicker.PickFromRequestBody(r, func(p *picker.Picker) T { ... })\n```\n\n### Helper Functions\n\n```go\n// Parse JSON string into map\ndata, err := picker.ParseJson(jsonStr)  // returns map[string]interface{}\n\n// Parse HTTP request body into map\ndata, err := picker.ParseRequestBody(r)  // returns map[string]interface{}\n```\n\n### Getter Methods\n\n#### Required Fields\n\n```go\np.GetString(\"name\")                    // string\np.GetInt(\"age\")                        // int64 (from JSON number)\np.GetFloat(\"price\")                    // float64\np.GetBool(\"active\")                    // bool\np.GetDate(\"created_at\")                // time.Time (supports RFC3339, date-only, and RFC3339 without timezone)\np.GetObject(\"metadata\")                // map[string]interface{}\np.GetArray(\"items\")                    // []interface{}\n```\n\n#### Optional Fields with Fallbacks\n\n```go\np.GetStringOr(\"email\", \"default@example.com\")\np.GetIntOr(\"age\", 18)\np.GetFloatOr(\"price\", 0.0)\np.GetBoolOr(\"active\", false)\np.GetDateOr(\"updated\", time.Now())\np.GetObjectOr(\"metadata\", map[string]interface{}{})\np.GetArrayOr(\"tags\", []interface{}{})\n```\n\n#### Nested Objects and Arrays\n\n```go\np.Nested(\"user\")                            // *Picker for nested object\narray := p.NestedArray(\"users\")             // *NestedPickerArray for array of objects\npicker.GetTypedArray[T](p, \"items\")         // []T for typed arrays\npicker.Map[T](array, func(*Picker) T)       // []T - map array items through a function\narray.At(index)                             // *Picker - get item at index with bounds checking\n```\n\n### Error Handling\n\nUse `HasDetail(err)` to check if the error is a validation error, then `Detail(err)` to get the error map:\n\n```go\nuser, err := picker.PickFromJson(jsonStr, func(p *picker.Picker) User {\n    return User{\n        Name: p.GetString(\"name\"),\n        Age:  p.GetInt(\"age\"),\n    }\n})\n\nif err != nil {\n    // Check if it's a validation error\n    if picker.HasDetail(err) {\n        // Get detailed error map\n        errors := picker.Detail(err)\n        // errors = map[string]string{\n        //     \"name\": \"missing\",\n        //     \"age\": \"invalid\"\n        // }\n\n        for field, reason := range errors {\n            fmt.Printf(\"%s: %s\\n\", field, reason)\n        }\n        return\n    }\n\n    // Otherwise, it's a JSON parse error\n    fmt.Printf(\"Parse error: %v\\n\", err)\n    return\n}\n```\n\n### Nested Objects\n\nUse `Nested(key)` to access nested objects. Errors will include the full path:\n\n```go\njsonStr := `{\n    \"user\": {\n        \"name\": \"John\",\n        \"profile\": {\n            \"email\": \"john@example.com\"\n        }\n    }\n}`\n\nresult, err := picker.PickFromJson(jsonStr, func(p *picker.Picker) Result {\n    user := p.Nested(\"user\")\n    profile := user.Nested(\"profile\")\n\n    return Result{\n        Name:  user.GetString(\"name\"),\n        Email: profile.GetString(\"email\"),\n    }\n})\n\n// Errors from nested fields show full path: \"user.profile.email\"\n```\n\n### Arrays\n\nUse `GetTypedArray[T]` for arrays of primitive types, or `NestedArray` with `Map` to transform arrays of objects:\n\n```go\n// Typed arrays of primitives\ntags := picker.GetTypedArray[string](p, \"tags\")     // []string\nscores := picker.GetTypedArray[float64](p, \"scores\") // []float64\n\n// Array of objects - using Map\njsonStr := `{\n    \"users\": [\n        {\"name\": \"John\", \"age\": 30},\n        {\"name\": \"Jane\", \"age\": 25}\n    ]\n}`\n\nresult, err := picker.PickFromJson(jsonStr, func(p *picker.Picker) Result {\n    users := p.NestedArray(\"users\")\n\n    // Map to extract just the names\n    names := picker.Map(users, func(user *picker.Picker) string {\n        return user.GetString(\"name\")\n    })\n\n    return Result{Names: names}\n})\n\n// Or map to structs\nuserList := picker.Map(users, func(user *picker.Picker) User {\n    return User{\n        Name: user.GetString(\"name\"),\n        Age:  user.GetInt(\"age\"),\n    }\n})\n\n// Access specific item by index with bounds checking\nfirstUser := users.At(0).GetString(\"name\")  // \"John\"\n```\n\n### Custom Validation\n\nUse `SetError(key, message)` to add custom validation errors or `SetInvalid(key)` to mark a field as invalid:\n\n```go\nuser, err := picker.Pick(data, func(p *picker.Picker) User {\n    age := p.GetInt(\"age\")\n    if age \u003c 18 {\n        p.SetError(\"age\", \"must be 18 or older\")\n    }\n\n    email := p.GetString(\"email\")\n    if !strings.Contains(email, \"@\") {\n        p.SetInvalid(\"email\")\n    }\n\n    return User{Age: age, Email: email}\n})\n```\n\n### Customizable Error Messages\n\nBy default, validation errors will be either `\"missing\"` (field not present in JSON) or `\"invalid\"` (field has wrong type). You can customize these messages:\n\n```go\n// Default values\npicker.ErrorMissing = \"missing\"\npicker.ErrorInvalid = \"invalid\"\n\n// Customize for your application\npicker.ErrorMissing = \"required\"\npicker.ErrorInvalid = \"wrong type\"\n\n// Or for internationalization\npicker.ErrorMissing = \"faltante\"  // Spanish\npicker.ErrorInvalid = \"inválido\"\n```\n\n## HTTP Handler Example\n\n```go\nfunc CreateUserHandler(w http.ResponseWriter, r *http.Request) {\n    user, err := picker.PickFromRequestBody(r, func(p *picker.Picker) User {\n        return User{\n            Name:  p.GetString(\"name\"),\n            Email: p.GetString(\"email\"),\n            Age:   p.GetInt(\"age\"),\n        }\n    })\n\n    if err != nil {\n        if picker.HasDetail(err) {\n            w.WriteHeader(http.StatusBadRequest)\n            json.NewEncoder(w).Encode(map[string]interface{}{\n                \"errors\": picker.Detail(err),\n            })\n            return\n        }\n        http.Error(w, \"Invalid JSON\", http.StatusBadRequest)\n        return\n    }\n\n    // user is validated and ready to save\n    saveUser(user)\n    w.WriteHeader(http.StatusCreated)\n}\n```\n\n## License\n\nMIT License - see LICENSE file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarimagnusson%2Fgo-picker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkarimagnusson%2Fgo-picker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarimagnusson%2Fgo-picker/lists"}