{"id":29569228,"url":"https://github.com/roserocket/gopartial","last_synced_at":"2025-07-30T13:04:36.236Z","repository":{"id":31441580,"uuid":"117856765","full_name":"RoseRocket/gopartial","owner":"RoseRocket","description":"This mini library provides a helper function to update go struct partially from incoming json data (e.g.: PATCH Request)","archived":false,"fork":false,"pushed_at":"2022-04-19T20:29:40.000Z","size":14,"stargazers_count":31,"open_issues_count":0,"forks_count":11,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-07-19T04:33:33.143Z","etag":null,"topics":[],"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/RoseRocket.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":"2018-01-17T15:43:34.000Z","updated_at":"2025-07-10T12:49:48.000Z","dependencies_parsed_at":"2022-08-07T16:16:05.165Z","dependency_job_id":null,"html_url":"https://github.com/RoseRocket/gopartial","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/RoseRocket/gopartial","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RoseRocket%2Fgopartial","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RoseRocket%2Fgopartial/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RoseRocket%2Fgopartial/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RoseRocket%2Fgopartial/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RoseRocket","download_url":"https://codeload.github.com/RoseRocket/gopartial/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RoseRocket%2Fgopartial/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267873960,"owners_count":24158748,"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","status":"online","status_checked_at":"2025-07-30T02:00:09.044Z","response_time":70,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":[],"created_at":"2025-07-19T01:40:08.090Z","updated_at":"2025-07-30T13:04:36.229Z","avatar_url":"https://github.com/RoseRocket.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# gopartial - Golang Partial Struct Update\n\nThis mini library provides a helper function to update go struct partially from incoming json data.\n\n## Why?\n\nThe challenge with golang is it is hard to take a dynamic data such as json and apply partial update\nto a golang struct due to the 'unknown' type/value that you might receive from a field in a json data.\n\nThe challenge becomes even harder when dealing with nullable data because\nif you use json null value as a way to determine whether a struct field should be updated or not,\nyou'll lose ability to update the struct to null value.\n\nA good practical example of this problem is when you want to implement HTTP `PATCH` Request. \n\n## Installation\n\n```\n$ go get github.com/roserocket/gopartial\n```\n\n## Dependencies\n\n```\n$ go get github.com/guregu/null\n```\n\n## Example\n\n```go\nimport (\n    \"time\"\n    \"log\"\n\n    \"github.com/roserocket/gopartial\"\n)\n\n// User struct\ntype User struct {\n    ID          string          `json:\"id\"`\n    Name        string          `json:\"name\"`\n    Age         *int            `json:\"age\"` // Can be null\n    DeletedAt   *time.Time      `json:\"deleted_at\"` // Can be null\n}\n\n// Imagine you have a Web API that can partially update an existing User in database\nfunc UpdateUserPartially(user *User, partialDataJSON json.RawMessage) (*User, error) {\n\n\n    var partialData map[string]interface{}\n    if err := json.Unmarshal(partialDataJSON, \u0026partialData); err != nil {\n        log.Fatal(err)\n    }\n\n    updatedFields, err := gopartial.PartialUpdate(user, partialData, \"json\", gopartial.SkipConditions, gopartial.Updaters)\n    log.Println(\"Updated fields: \", updatedFields)\n\n    return user, err\n}\n\nfunc main() {\n    t := time.Now()\n    // Existing user data\n    user := \u0026User{\n        ID:         \"1\",\n        Name:       \"John\",\n        Age:        nil,\n        DeletedAt:  \u0026t,\n    }\n\n    log.Printf(\"Initial user object: %+v\", user)\n\n    // You want to update just name, age and deleted_at\n    partialDataJSON := json.RawMessage(`{\"name\": \"Johnson\", \"age\": 21, \"deleted_at\": null}`)\n\n    var err error\n    user, err = UpdateUserPartially(user, partialDataJSON)\n    if err != nil {\n        log.Fatal(err)\n        return\n    }\n\n    // Updated user data should now be:\n    // User{\n    //     ID:         \"1\",\n    //     Name:       \"Johnson\",\n    //     Age:        21,\n    //     DeletedAt:  nil,\n    // }\n    log.Printf(\"Updated user object: %+v\", user)\n}\n```\n\n## Methods\n\n#### `func PartialUpdate(dest interface{}, partial map[string]interface{}, tagName string, skipConditions []func(reflect.StructField) bool, updaters []func(reflect.Value, reflect.Value) bool) ([]string, error)`\n\n|    Argument    |                    Type                     |                                         Description                                         |\n| :------------: | :-----------------------------------------: | :-----------------------------------------------------------------------------------------: |\n|      dest      |                `interface{}`                |                      Destination struct (Must be a pointer to struct)                       |\n|    partial     |          `map[string]interface{}`           |                     Partial data in the form of map[string]interface{}                      |\n|    tagName     |                  `string`                   | The struct tag name that you'll be mapping the struct field to based on the json field name |\n| skipConditions |     `[]func(reflect.StructField) bool`      |                              Array of skip condition functions                              |\n|    updaters    | `[]func(reflect.Value, reflect.Value) bool` |                                 Array of updater functions                                  |\n\nThis function can be easily extended if you have certain skip conditions while updating the struct.\nFor example you want to skip all the struct field that has tagname `props` with value of `readonly`, then you can create a function as follow:\n\n```go\n// SkipReadOnly skips all field that has tag readonly\nfunc SkipReadOnly(field reflect.StructField) bool {\n\tprops := strings.Split(field.Tag.Get(\"props\"), \",\")\n\treturn utils.IndexOf(props, \"readonly\") \u003e= 0\n}\n```\n\nYou can also extend this function to update a certain custom type within your application.\nExample:\n\n```go\ntype MyType string\n\n// MyTypeUpdater update MyType\nfunc MyTypeUpdater(fieldValue reflect.Value, v reflect.Value) bool {\n\tswitch fieldValue.Interface().(type) {\n\tcase MyType:\n\t\t// if its null value\n\t\tif !v.IsValid() {\n\t\t\tnewValue := reflect.ValueOf(MyType{}})\n\t\t\tfieldValue.Set(newValue)\n\t\t\treturn true\n\t\t}\n\t\t// only set if underlying type is a string\n\t\tif v.Kind() == reflect.String {\n\t\t\tnewValue := reflect.ValueOf(MyType(v.String()))\n\t\t\tfieldValue.Set(newValue)\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n```\n\n### Why do we need updatedFields returned?\n\nThe idea is using the list of updated fields, you can dynamically build the sql query to update the record in the database.\n\nHint: use `reflect.Type.FieldByName` function to get the `reflect.StructField` and use `reflect.StructField.Tag.Get(\"db\")`\nto get the db field name.\n\n## TODO\n\nCurrently this library does not support nested partial update.\n\n## License\n\nThis code is free to use under the terms of the MIT license.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Froserocket%2Fgopartial","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Froserocket%2Fgopartial","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Froserocket%2Fgopartial/lists"}