├── .codeclimate.yml ├── .github ├── CODE_OF_CONDUCT.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ └── go.yml ├── .gitignore ├── LICENSE ├── README.md ├── Taskfile.yml ├── accessors.go ├── accessors_test.go ├── codegen ├── array-access.txt ├── index.html ├── template.txt ├── template_test.txt └── types_list.txt ├── conversions.go ├── conversions_test.go ├── doc.go ├── fixture_test.go ├── go.mod ├── go.sum ├── map.go ├── map_test.go ├── mutations.go ├── mutations_test.go ├── security.go ├── security_test.go ├── simple_example_test.go ├── tests.go ├── tests_test.go ├── type_specific.go ├── type_specific_codegen.go ├── type_specific_codegen_test.go ├── type_specific_test.go ├── value.go └── value_test.go /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | gofmt: 3 | enabled: true 4 | golint: 5 | enabled: true 6 | govet: 7 | enabled: true 8 | 9 | exclude_patterns: 10 | - ".github/" 11 | - "vendor/" 12 | - "codegen/" 13 | - "*.yml" 14 | - ".*.yml" 15 | - "*.md" 16 | - "Gopkg.*" 17 | - "doc.go" 18 | - "type_specific_codegen_test.go" 19 | - "type_specific_codegen.go" 20 | - ".gitignore" 21 | - "LICENSE" 22 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at hanzei@mailbox.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | #### Summary 4 | 5 | 6 | #### Checklist 7 | [Place an '[x]' (no spaces) in all applicable fields. Please remove unrelated fields.] 8 | - [ ] Tests are passing: `task test` 9 | - [ ] Code style is correct: `task lint` 10 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: gomod 4 | directory: / 5 | schedule: 6 | interval: daily 7 | - package-ecosystem: github-actions 8 | directory: / 9 | schedule: 10 | interval: daily 11 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | 11 | build: 12 | env: 13 | CC_TEST_REPORTER_ID: 68feaa3410049ce73e145287acbcdacc525087a30627f96f04e579e75bd71c00 14 | runs-on: ubuntu-latest 15 | strategy: 16 | matrix: 17 | go: [ '1.22', '1.21', '1.20' ] 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - name: Pre-run 22 | run: | 23 | curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 24 | chmod +x ./cc-test-reporter 25 | ./cc-test-reporter before-build 26 | curl -sL https://taskfile.dev/install.sh | sh 27 | 28 | - name: Set up Go 29 | uses: actions/setup-go@v5 30 | with: 31 | go-version: ${{ matrix.go }} 32 | 33 | - name: Setup go module cache 34 | uses: actions/cache@v4 35 | with: 36 | path: | 37 | ~/.cache/go-build 38 | ~/go/pkg/mod 39 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 40 | restore-keys: | 41 | ${{ runner.os }}-go- 42 | 43 | - name: Lint 44 | run: diff -u <(echo -n) <(./bin/task lint) 45 | 46 | - name: Test 47 | run: ./bin/task test-coverage 48 | 49 | - name: Post run 50 | run: ./cc-test-reporter after-build format-coverage -t gocov --prefix github.com/stretchr/objx .cover/c.out --exit-code $? 51 | 52 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Test binary, build with `go test -c` 8 | *.test 9 | 10 | # Output of the go coverage tool, specifically when used with LiteIDE 11 | *.out 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2014 Stretchr, Inc. 4 | Copyright (c) 2017-2018 objx contributors 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Objx 2 | [![Build Status](https://travis-ci.org/stretchr/objx.svg?branch=master)](https://travis-ci.org/stretchr/objx) 3 | [![Go Report Card](https://goreportcard.com/badge/github.com/stretchr/objx)](https://goreportcard.com/report/github.com/stretchr/objx) 4 | [![Maintainability](https://api.codeclimate.com/v1/badges/1d64bc6c8474c2074f2b/maintainability)](https://codeclimate.com/github/stretchr/objx/maintainability) 5 | [![Test Coverage](https://api.codeclimate.com/v1/badges/1d64bc6c8474c2074f2b/test_coverage)](https://codeclimate.com/github/stretchr/objx/test_coverage) 6 | [![Sourcegraph](https://sourcegraph.com/github.com/stretchr/objx/-/badge.svg)](https://sourcegraph.com/github.com/stretchr/objx) 7 | [![GoDoc](https://pkg.go.dev/badge/github.com/stretchr/objx?utm_source=godoc)](https://pkg.go.dev/github.com/stretchr/objx) 8 | 9 | Objx - Go package for dealing with maps, slices, JSON and other data. 10 | 11 | Get started: 12 | 13 | - Install Objx with [one line of code](#installation), or [update it with another](#staying-up-to-date) 14 | - Check out the API Documentation http://pkg.go.dev/github.com/stretchr/objx 15 | 16 | ## Overview 17 | Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes a powerful `Get` method (among others) that allows you to easily and quickly get access to data within the map, without having to worry too much about type assertions, missing data, default values etc. 18 | 19 | ### Pattern 20 | Objx uses a predictable pattern to make access data from within `map[string]interface{}` easy. Call one of the `objx.` functions to create your `objx.Map` to get going: 21 | 22 | ```go 23 | m, err := objx.FromJSON(json) 24 | ``` 25 | 26 | NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong, the rest will be optimistic and try to figure things out without panicking. 27 | 28 | Use `Get` to access the value you're interested in. You can use dot and array 29 | notation too: 30 | 31 | ```go 32 | m.Get("places[0].latlng") 33 | ``` 34 | 35 | Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type. 36 | 37 | ```go 38 | if m.Get("code").IsStr() { // Your code... } 39 | ``` 40 | 41 | Or you can just assume the type, and use one of the strong type methods to extract the real value: 42 | 43 | ```go 44 | m.Get("code").Int() 45 | ``` 46 | 47 | If there's no value there (or if it's the wrong type) then a default value will be returned, or you can be explicit about the default value. 48 | 49 | ```go 50 | Get("code").Int(-1) 51 | ``` 52 | If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating, manipulating and selecting that data. You can find out more by exploring the index below. 53 | 54 | ### Reading data 55 | A simple example of how to use Objx: 56 | 57 | ```go 58 | // Use MustFromJSON to make an objx.Map from some JSON 59 | m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`) 60 | 61 | // Get the details 62 | name := m.Get("name").Str() 63 | age := m.Get("age").Int() 64 | 65 | // Get their nickname (or use their name if they don't have one) 66 | nickname := m.Get("nickname").Str(name) 67 | ``` 68 | 69 | ### Ranging 70 | Since `objx.Map` is a `map[string]interface{}` you can treat it as such. For example, to `range` the data, do what you would expect: 71 | 72 | ```go 73 | m := objx.MustFromJSON(json) 74 | for key, value := range m { 75 | // Your code... 76 | } 77 | ``` 78 | 79 | ## Installation 80 | To install Objx, use go get: 81 | 82 | go get github.com/stretchr/objx 83 | 84 | ### Staying up to date 85 | To update Objx to the latest version, run: 86 | 87 | go get -u github.com/stretchr/objx 88 | 89 | ### Supported go versions 90 | We currently support the three recent major Go versions. 91 | 92 | ## Contributing 93 | Please feel free to submit issues, fork the repository and send pull requests! 94 | -------------------------------------------------------------------------------- /Taskfile.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | tasks: 4 | default: 5 | deps: [test] 6 | 7 | lint: 8 | desc: Checks code style 9 | cmds: 10 | - gofmt -d -s *.go 11 | - go vet ./... 12 | silent: true 13 | 14 | lint-fix: 15 | desc: Fixes code style 16 | cmds: 17 | - gofmt -w -s *.go 18 | 19 | test: 20 | desc: Runs go tests 21 | cmds: 22 | - go test -race ./... 23 | 24 | test-coverage: 25 | desc: Runs go tests and calculates test coverage 26 | cmds: 27 | - go test -race -coverprofile=c.out ./... 28 | -------------------------------------------------------------------------------- /accessors.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "reflect" 5 | "regexp" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | const ( 11 | // PathSeparator is the character used to separate the elements 12 | // of the keypath. 13 | // 14 | // For example, `location.address.city` 15 | PathSeparator string = "." 16 | 17 | // arrayAccessRegexString is the regex used to extract the array number 18 | // from the access path 19 | arrayAccessRegexString = `^(.+)\[([0-9]+)\]$` 20 | 21 | // mapAccessRegexString is the regex used to extract the map key 22 | // from the access path 23 | mapAccessRegexString = `^([^\[]*)\[([^\]]+)\](.*)$` 24 | ) 25 | 26 | // arrayAccessRegex is the compiled arrayAccessRegexString 27 | var arrayAccessRegex = regexp.MustCompile(arrayAccessRegexString) 28 | 29 | // mapAccessRegex is the compiled mapAccessRegexString 30 | var mapAccessRegex = regexp.MustCompile(mapAccessRegexString) 31 | 32 | // Get gets the value using the specified selector and 33 | // returns it inside a new Obj object. 34 | // 35 | // If it cannot find the value, Get will return a nil 36 | // value inside an instance of Obj. 37 | // 38 | // Get can only operate directly on map[string]interface{} and []interface. 39 | // 40 | // # Example 41 | // 42 | // To access the title of the third chapter of the second book, do: 43 | // 44 | // o.Get("books[1].chapters[2].title") 45 | func (m Map) Get(selector string) *Value { 46 | rawObj := access(m, selector, nil, false) 47 | return &Value{data: rawObj} 48 | } 49 | 50 | // Set sets the value using the specified selector and 51 | // returns the object on which Set was called. 52 | // 53 | // Set can only operate directly on map[string]interface{} and []interface 54 | // 55 | // # Example 56 | // 57 | // To set the title of the third chapter of the second book, do: 58 | // 59 | // o.Set("books[1].chapters[2].title","Time to Go") 60 | func (m Map) Set(selector string, value interface{}) Map { 61 | access(m, selector, value, true) 62 | return m 63 | } 64 | 65 | // getIndex returns the index, which is hold in s by two branches. 66 | // It also returns s without the index part, e.g. name[1] will return (1, name). 67 | // If no index is found, -1 is returned 68 | func getIndex(s string) (int, string) { 69 | arrayMatches := arrayAccessRegex.FindStringSubmatch(s) 70 | if len(arrayMatches) > 0 { 71 | // Get the key into the map 72 | selector := arrayMatches[1] 73 | // Get the index into the array at the key 74 | // We know this can't fail because arrayMatches[2] is an int for sure 75 | index, _ := strconv.Atoi(arrayMatches[2]) 76 | return index, selector 77 | } 78 | return -1, s 79 | } 80 | 81 | // getKey returns the key which is held in s by two brackets. 82 | // It also returns the next selector. 83 | func getKey(s string) (string, string) { 84 | selSegs := strings.SplitN(s, PathSeparator, 2) 85 | thisSel := selSegs[0] 86 | nextSel := "" 87 | 88 | if len(selSegs) > 1 { 89 | nextSel = selSegs[1] 90 | } 91 | 92 | mapMatches := mapAccessRegex.FindStringSubmatch(s) 93 | if len(mapMatches) > 0 { 94 | if _, err := strconv.Atoi(mapMatches[2]); err != nil { 95 | thisSel = mapMatches[1] 96 | nextSel = "[" + mapMatches[2] + "]" + mapMatches[3] 97 | 98 | if thisSel == "" { 99 | thisSel = mapMatches[2] 100 | nextSel = mapMatches[3] 101 | } 102 | 103 | if nextSel == "" { 104 | selSegs = []string{"", ""} 105 | } else if nextSel[0] == '.' { 106 | nextSel = nextSel[1:] 107 | } 108 | } 109 | } 110 | 111 | return thisSel, nextSel 112 | } 113 | 114 | // access accesses the object using the selector and performs the 115 | // appropriate action. 116 | func access(current interface{}, selector string, value interface{}, isSet bool) interface{} { 117 | thisSel, nextSel := getKey(selector) 118 | 119 | indexes := []int{} 120 | for strings.Contains(thisSel, "[") { 121 | prevSel := thisSel 122 | index := -1 123 | index, thisSel = getIndex(thisSel) 124 | indexes = append(indexes, index) 125 | if prevSel == thisSel { 126 | break 127 | } 128 | } 129 | 130 | if curMap, ok := current.(Map); ok { 131 | current = map[string]interface{}(curMap) 132 | } 133 | // get the object in question 134 | switch current.(type) { 135 | case map[string]interface{}: 136 | curMSI := current.(map[string]interface{}) 137 | if nextSel == "" && isSet { 138 | curMSI[thisSel] = value 139 | return nil 140 | } 141 | 142 | _, ok := curMSI[thisSel].(map[string]interface{}) 143 | if !ok { 144 | _, ok = curMSI[thisSel].(Map) 145 | } 146 | 147 | if (curMSI[thisSel] == nil || !ok) && len(indexes) == 0 && isSet { 148 | curMSI[thisSel] = map[string]interface{}{} 149 | } 150 | 151 | current = curMSI[thisSel] 152 | default: 153 | current = nil 154 | } 155 | 156 | // do we need to access the item of an array? 157 | if len(indexes) > 0 { 158 | num := len(indexes) 159 | for num > 0 { 160 | num-- 161 | index := indexes[num] 162 | indexes = indexes[:num] 163 | if array, ok := interSlice(current); ok { 164 | if index < len(array) { 165 | current = array[index] 166 | } else { 167 | current = nil 168 | break 169 | } 170 | } 171 | } 172 | } 173 | 174 | if nextSel != "" { 175 | current = access(current, nextSel, value, isSet) 176 | } 177 | return current 178 | } 179 | 180 | func interSlice(slice interface{}) ([]interface{}, bool) { 181 | if array, ok := slice.([]interface{}); ok { 182 | return array, ok 183 | } 184 | 185 | s := reflect.ValueOf(slice) 186 | if s.Kind() != reflect.Slice { 187 | return nil, false 188 | } 189 | 190 | ret := make([]interface{}, s.Len()) 191 | 192 | for i := 0; i < s.Len(); i++ { 193 | ret[i] = s.Index(i).Interface() 194 | } 195 | 196 | return ret, true 197 | } 198 | -------------------------------------------------------------------------------- /accessors_test.go: -------------------------------------------------------------------------------- 1 | package objx_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/objx" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestAccessorsAccessGetSingleField(t *testing.T) { 11 | m := objx.Map{"name": "Tyler"} 12 | 13 | assert.Equal(t, "Tyler", m.Get("name").Data()) 14 | } 15 | 16 | func TestAccessorsAccessGetSingleFieldInt(t *testing.T) { 17 | m := objx.Map{"name": 10} 18 | 19 | assert.Equal(t, 10, m.Get("name").Data()) 20 | } 21 | 22 | func TestAccessorsAccessGetDeep(t *testing.T) { 23 | m := objx.Map{ 24 | "name": objx.Map{ 25 | "first": "Tyler", 26 | "last": "Bunnell", 27 | "friends": []string{ 28 | "Capitol", 29 | "Bollocks", 30 | }, 31 | "ifriends": []interface{}{ 32 | "Capitol", 33 | "Bollocks", 34 | }, 35 | }, 36 | } 37 | 38 | assert.Equal(t, "Tyler", m.Get("name.first").Data()) 39 | assert.Equal(t, "Bunnell", m.Get("name.last").Data()) 40 | assert.Equal(t, "Capitol", m.Get("name.friends[0]").Data()) 41 | assert.Equal(t, "Capitol", m.Get("name.ifriends[0]").Data()) 42 | } 43 | 44 | func TestAccessorsAccessGetDeepDeep(t *testing.T) { 45 | m := objx.Map{ 46 | "one": objx.Map{ 47 | "two": objx.Map{ 48 | "three": objx.Map{ 49 | "four": 4, 50 | }, 51 | }, 52 | }, 53 | } 54 | 55 | assert.Equal(t, 4, m.Get("one.two.three.four").Data()) 56 | assert.Equal(t, 4, m.Get("one[two][three][four]").Data()) 57 | } 58 | 59 | func TestAccessorsGetWithComplexKey(t *testing.T) { 60 | m := objx.Map{ 61 | "domains": objx.Map{ 62 | "example-dot-com": objx.Map{ 63 | "apex": "example", 64 | }, 65 | "example.com": objx.Map{ 66 | "apex": "example", 67 | }, 68 | }, 69 | } 70 | 71 | assert.Equal(t, "example", m.Get("domains.example-dot-com.apex").Data()) 72 | 73 | assert.Equal(t, "example", m.Get("domains[example.com].apex").Data()) 74 | assert.Equal(t, "example", m.Get("domains[example.com][apex]").Data()) 75 | } 76 | 77 | func TestAccessorsAccessGetInsideArray(t *testing.T) { 78 | m := objx.Map{ 79 | "names": []interface{}{ 80 | objx.Map{ 81 | "first": "Tyler", 82 | "last": "Bunnell", 83 | }, 84 | objx.Map{ 85 | "first": "Capitol", 86 | "last": "Bollocks", 87 | }, 88 | }, 89 | } 90 | 91 | assert.Equal(t, "Tyler", m.Get("names[0].first").Data()) 92 | assert.Equal(t, "Bunnell", m.Get("names[0].last").Data()) 93 | assert.Equal(t, "Capitol", m.Get("names[1].first").Data()) 94 | assert.Equal(t, "Bollocks", m.Get("names[1].last").Data()) 95 | 96 | assert.Nil(t, m.Get("names[2]").Data()) 97 | assert.Nil(t, m.Get("names[]").Data()) 98 | assert.Nil(t, m.Get("names1]]").Data()) 99 | assert.Nil(t, m.Get("names[1]]").Data()) 100 | assert.Nil(t, m.Get("names[[1]]").Data()) 101 | assert.Nil(t, m.Get("names[[1]").Data()) 102 | assert.Nil(t, m.Get("names[[1").Data()) 103 | } 104 | 105 | func TestAccessorsGet(t *testing.T) { 106 | m := objx.Map{"name": "Tyler"} 107 | 108 | assert.Equal(t, "Tyler", m.Get("name").Data()) 109 | } 110 | 111 | func TestAccessorsAccessSetSingleField(t *testing.T) { 112 | m := objx.Map{"name": "Tyler"} 113 | 114 | m.Set("name", "Mat") 115 | m.Set("age", 29) 116 | 117 | assert.Equal(t, m.Get("name").Data(), "Mat") 118 | assert.Equal(t, m.Get("age").Data(), 29) 119 | } 120 | 121 | func TestAccessorsAccessSetSingleFieldNotExisting(t *testing.T) { 122 | m := objx.Map{ 123 | "first": "Tyler", 124 | "last": "Bunnell", 125 | } 126 | 127 | m.Set("name", "Mat") 128 | 129 | assert.Equal(t, m.Get("name").Data(), "Mat") 130 | } 131 | 132 | func TestAccessorsAccessSetDeep(t *testing.T) { 133 | m := objx.Map{ 134 | "name": objx.Map{ 135 | "first": "Tyler", 136 | "last": "Bunnell", 137 | }, 138 | } 139 | 140 | m.Set("name.first", "Mat") 141 | m.Set("name.last", "Ryer") 142 | 143 | assert.Equal(t, "Mat", m.Get("name.first").Data()) 144 | assert.Equal(t, "Ryer", m.Get("name.last").Data()) 145 | } 146 | 147 | func TestAccessorsAccessSetDeepDeep(t *testing.T) { 148 | m := objx.Map{ 149 | "one": objx.Map{ 150 | "two": objx.Map{ 151 | "three": objx.Map{ 152 | "four": 4, 153 | }, 154 | }, 155 | }, 156 | } 157 | 158 | m.Set("one.two.three.four", 5) 159 | 160 | assert.Equal(t, 5, m.Get("one.two.three.four").Data()) 161 | } 162 | 163 | func TestAccessorsAccessSetDeepDeepWithoutExisting(t *testing.T) { 164 | m := objx.Map{} 165 | 166 | m.Set("one.two.three.four", 5) 167 | m.Set("one.two.three.five", 6) 168 | 169 | assert.Equal(t, 5, m.Get("one.two.three.four").Data()) 170 | assert.Equal(t, 6, m.Get("one.two.three.five").Data()) 171 | 172 | m.Set("one.two", 7) 173 | assert.Equal(t, 7, m.Get("one.two").Data()) 174 | assert.Equal(t, nil, m.Get("one.two.three.four").Data()) 175 | 176 | m.Set("one.two.three", 8) 177 | assert.Equal(t, 8, m.Get("one.two.three").Data()) 178 | } 179 | 180 | func TestAccessorsAccessSetArray(t *testing.T) { 181 | m := objx.Map{ 182 | "names": []interface{}{"Tyler"}, 183 | } 184 | m.Set("names[0]", "Mat") 185 | 186 | assert.Equal(t, "Mat", m.Get("names[0]").Data()) 187 | } 188 | 189 | func TestAccessorsAccessSetInsideArray(t *testing.T) { 190 | m := objx.Map{ 191 | "names": []interface{}{ 192 | objx.Map{ 193 | "first": "Tyler", 194 | "last": "Bunnell", 195 | }, 196 | objx.Map{ 197 | "first": "Capitol", 198 | "last": "Bollocks", 199 | }, 200 | }, 201 | } 202 | 203 | m.Set("names[0].first", "Mat") 204 | m.Set("names[0].last", "Ryer") 205 | m.Set("names[1].first", "Captain") 206 | m.Set("names[1].last", "Underpants") 207 | 208 | assert.Equal(t, "Mat", m.Get("names[0].first").Data()) 209 | assert.Equal(t, "Ryer", m.Get("names[0].last").Data()) 210 | assert.Equal(t, "Captain", m.Get("names[1].first").Data()) 211 | assert.Equal(t, "Underpants", m.Get("names[1].last").Data()) 212 | } 213 | 214 | func TestAccessorsSet(t *testing.T) { 215 | m := objx.Map{"name": "Tyler"} 216 | 217 | m.Set("name", "Mat") 218 | 219 | assert.Equal(t, "Mat", m.Get("name").Data()) 220 | } 221 | 222 | func TestAccessorsSetWithinObjxMapChild(t *testing.T) { 223 | m, err := objx.FromJSON(`{"a": {"b": 1}}`) 224 | assert.NoError(t, err) 225 | 226 | m.Set("a.c", 2) 227 | jsonConverted, err := m.JSON() 228 | assert.NoError(t, err) 229 | 230 | m = objx.New(map[string]interface{}{ 231 | "a": map[string]interface{}{ 232 | "b": 1, 233 | }, 234 | }) 235 | m.Set("a.c", 2) 236 | jsonNewObj, err := m.JSON() 237 | 238 | assert.NoError(t, err) 239 | assert.Equal(t, jsonConverted, jsonNewObj) 240 | } 241 | 242 | func TestAccessorsNested(t *testing.T) { 243 | d := objx.MustFromJSON(`{"values":[["test", "test1"], ["test2", {"name":"Mat"}, {"names": ["Captain", "Mat"]}]]}`) 244 | 245 | value := d.Get("values[0][0]").String() 246 | assert.Equal(t, "test", value) 247 | 248 | value = d.Get("values[0][1]").String() 249 | assert.Equal(t, "test1", value) 250 | 251 | value = d.Get("values[1][0]").String() 252 | assert.Equal(t, "test2", value) 253 | 254 | value = d.Get("values[1][1].name").String() 255 | assert.Equal(t, "Mat", value) 256 | 257 | value = d.Get("values[1][2].names[0]").String() 258 | assert.Equal(t, "Captain", value) 259 | } 260 | -------------------------------------------------------------------------------- /codegen/array-access.txt: -------------------------------------------------------------------------------- 1 | case []{1}: 2 | a := object.([]{1}) 3 | if isSet { 4 | a[index] = value.({1}) 5 | } else { 6 | if index >= len(a) { 7 | if panics { 8 | panic(fmt.Sprintf("objx: Index %d is out of range because the []{1} only contains %d items.", index, len(a))) 9 | } 10 | return nil 11 | } else { 12 | return a[index] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /codegen/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Codegen 5 | 6 | 17 | 18 | 19 | 20 |

21 | Template 22 |

23 |

24 | Use {x} as a placeholder for each argument. 25 |

26 | 27 | 28 |

29 | Arguments (comma separated) 30 |

31 |

32 | One block per line 33 |

34 | 35 | 36 |

37 | Output 38 |

39 | 40 | 41 | 42 | 43 | 44 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /codegen/template.txt: -------------------------------------------------------------------------------- 1 | /* 2 | {4} ({1} and []{1}) 3 | */ 4 | 5 | // {4} gets the value as a {1}, returns the optionalDefault 6 | // value or a system default object if the value is the wrong type. 7 | func (v *Value) {4}(optionalDefault ...{1}) {1} { 8 | if s, ok := v.data.({1}); ok { 9 | return s 10 | } 11 | if len(optionalDefault) == 1 { 12 | return optionalDefault[0] 13 | } 14 | return {3} 15 | } 16 | 17 | // Must{4} gets the value as a {1}. 18 | // 19 | // Panics if the object is not a {1}. 20 | func (v *Value) Must{4}() {1} { 21 | return v.data.({1}) 22 | } 23 | 24 | // {4}Slice gets the value as a []{1}, returns the optionalDefault 25 | // value or nil if the value is not a []{1}. 26 | func (v *Value) {4}Slice(optionalDefault ...[]{1}) []{1} { 27 | if s, ok := v.data.([]{1}); ok { 28 | return s 29 | } 30 | if len(optionalDefault) == 1 { 31 | return optionalDefault[0] 32 | } 33 | return nil 34 | } 35 | 36 | // Must{4}Slice gets the value as a []{1}. 37 | // 38 | // Panics if the object is not a []{1}. 39 | func (v *Value) Must{4}Slice() []{1} { 40 | return v.data.([]{1}) 41 | } 42 | 43 | // Is{4} gets whether the object contained is a {1} or not. 44 | func (v *Value) Is{4}() bool { 45 | _, ok := v.data.({1}) 46 | return ok 47 | } 48 | 49 | // Is{4}Slice gets whether the object contained is a []{1} or not. 50 | func (v *Value) Is{4}Slice() bool { 51 | _, ok := v.data.([]{1}) 52 | return ok 53 | } 54 | 55 | // Each{4} calls the specified callback for each object 56 | // in the []{1}. 57 | // 58 | // Panics if the object is the wrong type. 59 | func (v *Value) Each{4}(callback func(int, {1}) bool) *Value { 60 | for index, val := range v.Must{4}Slice() { 61 | carryon := callback(index, val) 62 | if !carryon { 63 | break 64 | } 65 | } 66 | return v 67 | } 68 | 69 | // Where{4} uses the specified decider function to select items 70 | // from the []{1}. The object contained in the result will contain 71 | // only the selected items. 72 | func (v *Value) Where{4}(decider func(int, {1}) bool) *Value { 73 | var selected []{1} 74 | v.Each{4}(func(index int, val {1}) bool { 75 | shouldSelect := decider(index, val) 76 | if !shouldSelect { 77 | selected = append(selected, val) 78 | } 79 | return true 80 | }) 81 | return &Value{data:selected} 82 | } 83 | 84 | // Group{4} uses the specified grouper function to group the items 85 | // keyed by the return of the grouper. The object contained in the 86 | // result will contain a map[string][]{1}. 87 | func (v *Value) Group{4}(grouper func(int, {1}) string) *Value { 88 | groups := make(map[string][]{1}) 89 | v.Each{4}(func(index int, val {1}) bool { 90 | group := grouper(index, val) 91 | if _, ok := groups[group]; !ok { 92 | groups[group] = make([]{1}, 0) 93 | } 94 | groups[group] = append(groups[group], val) 95 | return true 96 | }) 97 | return &Value{data:groups} 98 | } 99 | 100 | // Replace{4} uses the specified function to replace each {1}s 101 | // by iterating each item. The data in the returned result will be a 102 | // []{1} containing the replaced items. 103 | func (v *Value) Replace{4}(replacer func(int, {1}) {1}) *Value { 104 | arr := v.Must{4}Slice() 105 | replaced := make([]{1}, len(arr)) 106 | v.Each{4}(func(index int, val {1}) bool { 107 | replaced[index] = replacer(index, val) 108 | return true 109 | }) 110 | return &Value{data:replaced} 111 | } 112 | 113 | // Collect{4} uses the specified collector function to collect a value 114 | // for each of the {1}s in the slice. The data returned will be a 115 | // []interface{}. 116 | func (v *Value) Collect{4}(collector func(int, {1}) interface{}) *Value { 117 | arr := v.Must{4}Slice() 118 | collected := make([]interface{}, len(arr)) 119 | v.Each{4}(func(index int, val {1}) bool { 120 | collected[index] = collector(index, val) 121 | return true 122 | }) 123 | return &Value{data:collected} 124 | } 125 | -------------------------------------------------------------------------------- /codegen/template_test.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Tests for {4} ({1} and []{1}) 3 | */ 4 | func Test{4}(t *testing.T) { 5 | val := {1}({2}) 6 | m := objx.Map{"value": val, "nothing": nil} 7 | 8 | assert.Equal(t, val, m.Get("value").{4}()) 9 | assert.Equal(t, val, m.Get("value").Must{4}()) 10 | assert.Equal(t, {1}({3}), m.Get("nothing").{4}()) 11 | assert.Equal(t, val, m.Get("nothing").{4}({2})) 12 | assert.Panics(t, func() { 13 | m.Get("age").Must{4}() 14 | }) 15 | } 16 | 17 | func Test{4}Slice(t *testing.T) { 18 | val := {1}({2}) 19 | m := objx.Map{"value": []{1}{ val }, "nothing": nil} 20 | 21 | assert.Equal(t, val, m.Get("value").{4}Slice()[0]) 22 | assert.Equal(t, val, m.Get("value").Must{4}Slice()[0]) 23 | assert.Equal(t, []{1}(nil), m.Get("nothing").{4}Slice()) 24 | assert.Equal(t, val, m.Get("nothing").{4}Slice([]{1}{{1}({2})})[0]) 25 | assert.Panics(t, func() { 26 | m.Get("nothing").Must{4}Slice() 27 | }) 28 | } 29 | 30 | func TestIs{4}(t *testing.T) { 31 | m := objx.Map{"data": {1}({2})} 32 | 33 | assert.True(t, m.Get("data").Is{4}()) 34 | } 35 | 36 | func TestIs{4}Slice(t *testing.T) { 37 | m := objx.Map{"data": []{1}{{1}({2})}} 38 | 39 | assert.True(t, m.Get("data").Is{4}Slice()) 40 | } 41 | 42 | func TestEach{4}(t *testing.T) { 43 | m := objx.Map{"data": []{1}{{1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2})}} 44 | count := 0 45 | replacedVals := make([]{1}, 0) 46 | assert.Equal(t, m.Get("data"), m.Get("data").Each{4}(func(i int, val {1}) bool { 47 | count++ 48 | replacedVals = append(replacedVals, val) 49 | 50 | // abort early 51 | return i != 2 52 | })) 53 | 54 | assert.Equal(t, count, 3) 55 | assert.Equal(t, replacedVals[0], m.Get("data").Must{4}Slice()[0]) 56 | assert.Equal(t, replacedVals[1], m.Get("data").Must{4}Slice()[1]) 57 | assert.Equal(t, replacedVals[2], m.Get("data").Must{4}Slice()[2]) 58 | } 59 | 60 | func TestWhere{4}(t *testing.T) { 61 | m := objx.Map{"data": []{1}{{1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2})}} 62 | 63 | selected := m.Get("data").Where{4}(func(i int, val {1}) bool { 64 | return i%2 == 0 65 | }).Must{4}Slice() 66 | 67 | assert.Equal(t, 3, len(selected)) 68 | } 69 | 70 | func TestGroup{4}(t *testing.T) { 71 | m := objx.Map{"data": []{1}{{1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2})}} 72 | 73 | grouped := m.Get("data").Group{4}(func(i int, val {1}) string { 74 | return fmt.Sprintf("%v", i%2==0) 75 | }).Data().(map[string][]{1}) 76 | 77 | assert.Equal(t, 2, len(grouped)) 78 | assert.Equal(t, 3, len(grouped["true"])) 79 | assert.Equal(t, 3, len(grouped["false"])) 80 | } 81 | 82 | func TestReplace{4}(t *testing.T) { 83 | m := objx.Map{"data": []{1}{{1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2})}} 84 | rawArr := m.Get("data").Must{4}Slice() 85 | 86 | replaced := m.Get("data").Replace{4}(func(index int, val {1}) {1} { 87 | if index < len(rawArr)-1 { 88 | return rawArr[index+1] 89 | } 90 | return rawArr[0] 91 | }) 92 | replacedArr := replaced.Must{4}Slice() 93 | 94 | if assert.Equal(t, 6, len(replacedArr)) { 95 | assert.Equal(t, replacedArr[0], rawArr[1]) 96 | assert.Equal(t, replacedArr[1], rawArr[2]) 97 | assert.Equal(t, replacedArr[2], rawArr[3]) 98 | assert.Equal(t, replacedArr[3], rawArr[4]) 99 | assert.Equal(t, replacedArr[4], rawArr[5]) 100 | assert.Equal(t, replacedArr[5], rawArr[0]) 101 | } 102 | } 103 | 104 | func TestCollect{4}(t *testing.T) { 105 | m := objx.Map{"data": []{1}{{1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2}), {1}({2})}} 106 | 107 | collected := m.Get("data").Collect{4}(func(index int, val {1}) interface{} { 108 | return index 109 | }) 110 | collectedArr := collected.MustInterSlice() 111 | 112 | if assert.Equal(t, 6, len(collectedArr)) { 113 | assert.Equal(t, collectedArr[0], 0) 114 | assert.Equal(t, collectedArr[1], 1) 115 | assert.Equal(t, collectedArr[2], 2) 116 | assert.Equal(t, collectedArr[3], 3) 117 | assert.Equal(t, collectedArr[4], 4) 118 | assert.Equal(t, collectedArr[5], 5) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /codegen/types_list.txt: -------------------------------------------------------------------------------- 1 | Interface,interface{},"something",nil,Inter 2 | ObjxMap,(objx.Map),objx.New(1),objx.New(nil),ObjxMap 3 | Bool,bool,true,false,Bool 4 | String,string,"hello","",Str 5 | Int,int,1,0,Int 6 | Int8,int8,1,0,Int8 7 | Int16,int16,1,0,Int16 8 | Int32,int32,1,0,Int32 9 | Int64,int64,1,0,Int64 10 | Uint,uint,1,0,Uint 11 | Uint8,uint8,1,0,Uint8 12 | Uint16,uint16,1,0,Uint16 13 | Uint32,uint32,1,0,Uint32 14 | Uint64,uint64,1,0,Uint64 15 | Uintptr,uintptr,1,0,Uintptr 16 | Float32,float32,1,0,Float32 17 | Float64,float64,1,0,Float64 18 | Complex64,complex64,1,0,Complex64 19 | Complex128,complex128,1,0,Complex128 20 | -------------------------------------------------------------------------------- /conversions.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "bytes" 5 | "encoding/base64" 6 | "encoding/json" 7 | "errors" 8 | "fmt" 9 | "net/url" 10 | "strconv" 11 | ) 12 | 13 | // SignatureSeparator is the character that is used to 14 | // separate the Base64 string from the security signature. 15 | const SignatureSeparator = "_" 16 | 17 | // URLValuesSliceKeySuffix is the character that is used to 18 | // specify a suffix for slices parsed by URLValues. 19 | // If the suffix is set to "[i]", then the index of the slice 20 | // is used in place of i 21 | // Ex: Suffix "[]" would have the form a[]=b&a[]=c 22 | // OR Suffix "[i]" would have the form a[0]=b&a[1]=c 23 | // OR Suffix "" would have the form a=b&a=c 24 | var urlValuesSliceKeySuffix = "[]" 25 | 26 | const ( 27 | URLValuesSliceKeySuffixEmpty = "" 28 | URLValuesSliceKeySuffixArray = "[]" 29 | URLValuesSliceKeySuffixIndex = "[i]" 30 | ) 31 | 32 | // SetURLValuesSliceKeySuffix sets the character that is used to 33 | // specify a suffix for slices parsed by URLValues. 34 | // If the suffix is set to "[i]", then the index of the slice 35 | // is used in place of i 36 | // Ex: Suffix "[]" would have the form a[]=b&a[]=c 37 | // OR Suffix "[i]" would have the form a[0]=b&a[1]=c 38 | // OR Suffix "" would have the form a=b&a=c 39 | func SetURLValuesSliceKeySuffix(s string) error { 40 | if s == URLValuesSliceKeySuffixEmpty || s == URLValuesSliceKeySuffixArray || s == URLValuesSliceKeySuffixIndex { 41 | urlValuesSliceKeySuffix = s 42 | return nil 43 | } 44 | 45 | return errors.New("objx: Invalid URLValuesSliceKeySuffix provided.") 46 | } 47 | 48 | // JSON converts the contained object to a JSON string 49 | // representation 50 | func (m Map) JSON() (string, error) { 51 | for k, v := range m { 52 | m[k] = cleanUp(v) 53 | } 54 | 55 | result, err := json.Marshal(m) 56 | if err != nil { 57 | err = errors.New("objx: JSON encode failed with: " + err.Error()) 58 | } 59 | return string(result), err 60 | } 61 | 62 | func cleanUpInterfaceArray(in []interface{}) []interface{} { 63 | result := make([]interface{}, len(in)) 64 | for i, v := range in { 65 | result[i] = cleanUp(v) 66 | } 67 | return result 68 | } 69 | 70 | func cleanUpInterfaceMap(in map[interface{}]interface{}) Map { 71 | result := Map{} 72 | for k, v := range in { 73 | result[fmt.Sprintf("%v", k)] = cleanUp(v) 74 | } 75 | return result 76 | } 77 | 78 | func cleanUpStringMap(in map[string]interface{}) Map { 79 | result := Map{} 80 | for k, v := range in { 81 | result[k] = cleanUp(v) 82 | } 83 | return result 84 | } 85 | 86 | func cleanUpMSIArray(in []map[string]interface{}) []Map { 87 | result := make([]Map, len(in)) 88 | for i, v := range in { 89 | result[i] = cleanUpStringMap(v) 90 | } 91 | return result 92 | } 93 | 94 | func cleanUpMapArray(in []Map) []Map { 95 | result := make([]Map, len(in)) 96 | for i, v := range in { 97 | result[i] = cleanUpStringMap(v) 98 | } 99 | return result 100 | } 101 | 102 | func cleanUp(v interface{}) interface{} { 103 | switch v := v.(type) { 104 | case []interface{}: 105 | return cleanUpInterfaceArray(v) 106 | case []map[string]interface{}: 107 | return cleanUpMSIArray(v) 108 | case map[interface{}]interface{}: 109 | return cleanUpInterfaceMap(v) 110 | case Map: 111 | return cleanUpStringMap(v) 112 | case []Map: 113 | return cleanUpMapArray(v) 114 | default: 115 | return v 116 | } 117 | } 118 | 119 | // MustJSON converts the contained object to a JSON string 120 | // representation and panics if there is an error 121 | func (m Map) MustJSON() string { 122 | result, err := m.JSON() 123 | if err != nil { 124 | panic(err.Error()) 125 | } 126 | return result 127 | } 128 | 129 | // Base64 converts the contained object to a Base64 string 130 | // representation of the JSON string representation 131 | func (m Map) Base64() (string, error) { 132 | var buf bytes.Buffer 133 | 134 | jsonData, err := m.JSON() 135 | if err != nil { 136 | return "", err 137 | } 138 | 139 | encoder := base64.NewEncoder(base64.StdEncoding, &buf) 140 | _, _ = encoder.Write([]byte(jsonData)) 141 | _ = encoder.Close() 142 | 143 | return buf.String(), nil 144 | } 145 | 146 | // MustBase64 converts the contained object to a Base64 string 147 | // representation of the JSON string representation and panics 148 | // if there is an error 149 | func (m Map) MustBase64() string { 150 | result, err := m.Base64() 151 | if err != nil { 152 | panic(err.Error()) 153 | } 154 | return result 155 | } 156 | 157 | // SignedBase64 converts the contained object to a Base64 string 158 | // representation of the JSON string representation and signs it 159 | // using the provided key. 160 | func (m Map) SignedBase64(key string) (string, error) { 161 | base64, err := m.Base64() 162 | if err != nil { 163 | return "", err 164 | } 165 | 166 | sig := HashWithKey(base64, key) 167 | return base64 + SignatureSeparator + sig, nil 168 | } 169 | 170 | // MustSignedBase64 converts the contained object to a Base64 string 171 | // representation of the JSON string representation and signs it 172 | // using the provided key and panics if there is an error 173 | func (m Map) MustSignedBase64(key string) string { 174 | result, err := m.SignedBase64(key) 175 | if err != nil { 176 | panic(err.Error()) 177 | } 178 | return result 179 | } 180 | 181 | /* 182 | URL Query 183 | ------------------------------------------------ 184 | */ 185 | 186 | // URLValues creates a url.Values object from an Obj. This 187 | // function requires that the wrapped object be a map[string]interface{} 188 | func (m Map) URLValues() url.Values { 189 | vals := make(url.Values) 190 | 191 | m.parseURLValues(m, vals, "") 192 | 193 | return vals 194 | } 195 | 196 | func (m Map) parseURLValues(queryMap Map, vals url.Values, key string) { 197 | useSliceIndex := false 198 | if urlValuesSliceKeySuffix == "[i]" { 199 | useSliceIndex = true 200 | } 201 | 202 | for k, v := range queryMap { 203 | val := &Value{data: v} 204 | switch { 205 | case val.IsObjxMap(): 206 | if key == "" { 207 | m.parseURLValues(val.ObjxMap(), vals, k) 208 | } else { 209 | m.parseURLValues(val.ObjxMap(), vals, key+"["+k+"]") 210 | } 211 | case val.IsObjxMapSlice(): 212 | sliceKey := k 213 | if key != "" { 214 | sliceKey = key + "[" + k + "]" 215 | } 216 | 217 | if useSliceIndex { 218 | for i, sv := range val.MustObjxMapSlice() { 219 | sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]" 220 | m.parseURLValues(sv, vals, sk) 221 | } 222 | } else { 223 | sliceKey = sliceKey + urlValuesSliceKeySuffix 224 | for _, sv := range val.MustObjxMapSlice() { 225 | m.parseURLValues(sv, vals, sliceKey) 226 | } 227 | } 228 | case val.IsMSISlice(): 229 | sliceKey := k 230 | if key != "" { 231 | sliceKey = key + "[" + k + "]" 232 | } 233 | 234 | if useSliceIndex { 235 | for i, sv := range val.MustMSISlice() { 236 | sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]" 237 | m.parseURLValues(New(sv), vals, sk) 238 | } 239 | } else { 240 | sliceKey = sliceKey + urlValuesSliceKeySuffix 241 | for _, sv := range val.MustMSISlice() { 242 | m.parseURLValues(New(sv), vals, sliceKey) 243 | } 244 | } 245 | case val.IsStrSlice(), val.IsBoolSlice(), 246 | val.IsFloat32Slice(), val.IsFloat64Slice(), 247 | val.IsIntSlice(), val.IsInt8Slice(), val.IsInt16Slice(), val.IsInt32Slice(), val.IsInt64Slice(), 248 | val.IsUintSlice(), val.IsUint8Slice(), val.IsUint16Slice(), val.IsUint32Slice(), val.IsUint64Slice(): 249 | 250 | sliceKey := k 251 | if key != "" { 252 | sliceKey = key + "[" + k + "]" 253 | } 254 | 255 | if useSliceIndex { 256 | for i, sv := range val.StringSlice() { 257 | sk := sliceKey + "[" + strconv.FormatInt(int64(i), 10) + "]" 258 | vals.Set(sk, sv) 259 | } 260 | } else { 261 | sliceKey = sliceKey + urlValuesSliceKeySuffix 262 | vals[sliceKey] = val.StringSlice() 263 | } 264 | 265 | default: 266 | if key == "" { 267 | vals.Set(k, val.String()) 268 | } else { 269 | vals.Set(key+"["+k+"]", val.String()) 270 | } 271 | } 272 | } 273 | } 274 | 275 | // URLQuery gets an encoded URL query representing the given 276 | // Obj. This function requires that the wrapped object be a 277 | // map[string]interface{} 278 | func (m Map) URLQuery() (string, error) { 279 | return m.URLValues().Encode(), nil 280 | } 281 | -------------------------------------------------------------------------------- /conversions_test.go: -------------------------------------------------------------------------------- 1 | package objx_test 2 | 3 | import ( 4 | "net/url" 5 | "testing" 6 | 7 | "github.com/stretchr/objx" 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestConversionJSON(t *testing.T) { 13 | jsonString := `{"name":"Mat"}` 14 | o := objx.MustFromJSON(jsonString) 15 | 16 | result, err := o.JSON() 17 | 18 | require.NoError(t, err) 19 | assert.Equal(t, jsonString, result) 20 | assert.Equal(t, jsonString, o.MustJSON()) 21 | 22 | i := objx.Map{ 23 | "a": map[interface{}]interface{}{"b": objx.Map{"c": map[interface{}]interface{}{"d": "e"}}, 24 | "f": []objx.Map{{"g": map[interface{}]interface{}{"h": "i"}}}, 25 | "j": []map[string]interface{}{{"k": map[interface{}]interface{}{"l": "m"}}}, 26 | "n": []interface{}{objx.Map{"o": "p"}}, 27 | }, 28 | } 29 | 30 | jsonString = `{"a":{"b":{"c":{"d":"e"}},"f":[{"g":{"h":"i"}}],"j":[{"k":{"l":"m"}}],"n":[{"o":"p"}]}}` 31 | result, err = i.JSON() 32 | require.NoError(t, err) 33 | assert.Equal(t, jsonString, result) 34 | assert.Equal(t, jsonString, i.MustJSON()) 35 | } 36 | 37 | func TestConversionJSONWithError(t *testing.T) { 38 | o := objx.MSI() 39 | o["test"] = func() {} 40 | 41 | assert.Panics(t, func() { 42 | o.MustJSON() 43 | }) 44 | 45 | _, err := o.JSON() 46 | 47 | assert.Error(t, err) 48 | } 49 | 50 | func TestConversionBase64(t *testing.T) { 51 | o := objx.Map{"name": "Mat"} 52 | 53 | result, err := o.Base64() 54 | 55 | require.NoError(t, err) 56 | assert.Equal(t, "eyJuYW1lIjoiTWF0In0=", result) 57 | assert.Equal(t, "eyJuYW1lIjoiTWF0In0=", o.MustBase64()) 58 | } 59 | 60 | func TestConversionBase64WithError(t *testing.T) { 61 | o := objx.MSI() 62 | o["test"] = func() {} 63 | 64 | assert.Panics(t, func() { 65 | o.MustBase64() 66 | }) 67 | 68 | _, err := o.Base64() 69 | 70 | assert.Error(t, err) 71 | } 72 | 73 | func TestConversionSignedBase64(t *testing.T) { 74 | o := objx.Map{"name": "Mat"} 75 | 76 | result, err := o.SignedBase64("key") 77 | 78 | require.NoError(t, err) 79 | assert.Equal(t, "eyJuYW1lIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6", result) 80 | assert.Equal(t, "eyJuYW1lIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6", o.MustSignedBase64("key")) 81 | } 82 | 83 | func TestConversionSignedBase64WithError(t *testing.T) { 84 | o := objx.MSI() 85 | o["test"] = func() {} 86 | 87 | assert.Panics(t, func() { 88 | o.MustSignedBase64("key") 89 | }) 90 | 91 | _, err := o.SignedBase64("key") 92 | 93 | assert.Error(t, err) 94 | } 95 | 96 | func TestConversionURLValues(t *testing.T) { 97 | m := getURLQueryMap() 98 | u := m.URLValues() 99 | 100 | assert.Equal(t, url.Values{ 101 | "abc": []string{"123"}, 102 | "name": []string{"Mat"}, 103 | "data[age]": []string{"30"}, 104 | "data[height]": []string{"162"}, 105 | "data[arr][]": []string{"1", "2"}, 106 | "stats[]": []string{"1", "2"}, 107 | "bools[]": []string{"true", "false"}, 108 | "mapSlice[][age]": []string{"40"}, 109 | "mapSlice[][height]": []string{"152"}, 110 | "msiData[age]": []string{"30"}, 111 | "msiData[height]": []string{"162"}, 112 | "msiData[arr][]": []string{"1", "2"}, 113 | "msiSlice[][age]": []string{"40"}, 114 | "msiSlice[][height]": []string{"152"}, 115 | }, u) 116 | } 117 | 118 | func TestConversionURLQuery(t *testing.T) { 119 | m := getURLQueryMap() 120 | u, err := m.URLQuery() 121 | 122 | assert.Nil(t, err) 123 | require.NotNil(t, u) 124 | 125 | ue, err := url.QueryUnescape(u) 126 | assert.Nil(t, err) 127 | require.NotNil(t, ue) 128 | 129 | assert.Equal(t, "abc=123&bools[]=true&bools[]=false&data[age]=30&data[arr][]=1&data[arr][]=2&data[height]=162&mapSlice[][age]=40&mapSlice[][height]=152&msiData[age]=30&msiData[arr][]=1&msiData[arr][]=2&msiData[height]=162&msiSlice[][age]=40&msiSlice[][height]=152&name=Mat&stats[]=1&stats[]=2", ue) 130 | } 131 | 132 | func TestConversionURLQueryNoSliceKeySuffix(t *testing.T) { 133 | m := getURLQueryMap() 134 | err := objx.SetURLValuesSliceKeySuffix(objx.URLValuesSliceKeySuffixEmpty) 135 | require.Nil(t, err) 136 | u, err := m.URLQuery() 137 | 138 | assert.Nil(t, err) 139 | require.NotNil(t, u) 140 | 141 | ue, err := url.QueryUnescape(u) 142 | assert.Nil(t, err) 143 | require.NotNil(t, ue) 144 | 145 | assert.Equal(t, "abc=123&bools=true&bools=false&data[age]=30&data[arr]=1&data[arr]=2&data[height]=162&mapSlice[age]=40&mapSlice[height]=152&msiData[age]=30&msiData[arr]=1&msiData[arr]=2&msiData[height]=162&msiSlice[age]=40&msiSlice[height]=152&name=Mat&stats=1&stats=2", ue) 146 | } 147 | 148 | func TestConversionURLQueryIndexSliceKeySuffix(t *testing.T) { 149 | m := getURLQueryMap() 150 | m.Set("mapSlice", []objx.Map{{"age": 40, "sex": "male"}, {"height": 152}}) 151 | err := objx.SetURLValuesSliceKeySuffix(objx.URLValuesSliceKeySuffixIndex) 152 | require.Nil(t, err) 153 | u, err := m.URLQuery() 154 | 155 | assert.Nil(t, err) 156 | require.NotNil(t, u) 157 | 158 | ue, err := url.QueryUnescape(u) 159 | assert.Nil(t, err) 160 | require.NotNil(t, ue) 161 | 162 | assert.Equal(t, "abc=123&bools[0]=true&bools[1]=false&data[age]=30&data[arr][0]=1&data[arr][1]=2&data[height]=162&mapSlice[0][age]=40&mapSlice[0][sex]=male&mapSlice[1][height]=152&msiData[age]=30&msiData[arr][0]=1&msiData[arr][1]=2&msiData[height]=162&msiSlice[0][age]=40&msiSlice[1][height]=152&name=Mat&stats[0]=1&stats[1]=2", ue) 163 | } 164 | 165 | func TestValidityURLQuerySliceKeySuffix(t *testing.T) { 166 | err := objx.SetURLValuesSliceKeySuffix("") 167 | assert.Nil(t, err) 168 | err = objx.SetURLValuesSliceKeySuffix("[]") 169 | assert.Nil(t, err) 170 | err = objx.SetURLValuesSliceKeySuffix("[i]") 171 | assert.Nil(t, err) 172 | err = objx.SetURLValuesSliceKeySuffix("{}") 173 | assert.Error(t, err) 174 | } 175 | 176 | func getURLQueryMap() objx.Map { 177 | return objx.Map{ 178 | "abc": 123, 179 | "name": "Mat", 180 | "data": objx.Map{"age": 30, "height": 162, "arr": []int{1, 2}}, 181 | "mapSlice": []objx.Map{{"age": 40}, {"height": 152}}, 182 | "msiData": map[string]interface{}{"age": 30, "height": 162, "arr": []int{1, 2}}, 183 | "msiSlice": []map[string]interface{}{{"age": 40}, {"height": 152}}, 184 | "stats": []string{"1", "2"}, 185 | "bools": []bool{true, false}, 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package objx provides utilities for dealing with maps, slices, JSON and other data. 3 | 4 | # Overview 5 | 6 | Objx provides the `objx.Map` type, which is a `map[string]interface{}` that exposes 7 | a powerful `Get` method (among others) that allows you to easily and quickly get 8 | access to data within the map, without having to worry too much about type assertions, 9 | missing data, default values etc. 10 | 11 | # Pattern 12 | 13 | Objx uses a predictable pattern to make access data from within `map[string]interface{}` easy. 14 | Call one of the `objx.` functions to create your `objx.Map` to get going: 15 | 16 | m, err := objx.FromJSON(json) 17 | 18 | NOTE: Any methods or functions with the `Must` prefix will panic if something goes wrong, 19 | the rest will be optimistic and try to figure things out without panicking. 20 | 21 | Use `Get` to access the value you're interested in. You can use dot and array 22 | notation too: 23 | 24 | m.Get("places[0].latlng") 25 | 26 | Once you have sought the `Value` you're interested in, you can use the `Is*` methods to determine its type. 27 | 28 | if m.Get("code").IsStr() { // Your code... } 29 | 30 | Or you can just assume the type, and use one of the strong type methods to extract the real value: 31 | 32 | m.Get("code").Int() 33 | 34 | If there's no value there (or if it's the wrong type) then a default value will be returned, 35 | or you can be explicit about the default value. 36 | 37 | Get("code").Int(-1) 38 | 39 | If you're dealing with a slice of data as a value, Objx provides many useful methods for iterating, 40 | manipulating and selecting that data. You can find out more by exploring the index below. 41 | 42 | # Reading data 43 | 44 | A simple example of how to use Objx: 45 | 46 | // Use MustFromJSON to make an objx.Map from some JSON 47 | m := objx.MustFromJSON(`{"name": "Mat", "age": 30}`) 48 | 49 | // Get the details 50 | name := m.Get("name").Str() 51 | age := m.Get("age").Int() 52 | 53 | // Get their nickname (or use their name if they don't have one) 54 | nickname := m.Get("nickname").Str(name) 55 | 56 | # Ranging 57 | 58 | Since `objx.Map` is a `map[string]interface{}` you can treat it as such. 59 | For example, to `range` the data, do what you would expect: 60 | 61 | m := objx.MustFromJSON(json) 62 | for key, value := range m { 63 | // Your code... 64 | } 65 | */ 66 | package objx 67 | -------------------------------------------------------------------------------- /fixture_test.go: -------------------------------------------------------------------------------- 1 | package objx_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/objx" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | var fixtures = []struct { 11 | // name is the name of the fixture (used for reporting 12 | // failures) 13 | name string 14 | // data is the JSON data to be worked on 15 | data string 16 | // get is the argument(s) to pass to Get 17 | get interface{} 18 | // output is the expected output 19 | output interface{} 20 | }{ 21 | { 22 | name: "Simple get", 23 | data: `{"name": "Mat"}`, 24 | get: "name", 25 | output: "Mat", 26 | }, 27 | { 28 | name: "Get with dot notation", 29 | data: `{"address": {"city": "Boulder"}}`, 30 | get: "address.city", 31 | output: "Boulder", 32 | }, 33 | { 34 | name: "Deep get with dot notation", 35 | data: `{"one": {"two": {"three": {"four": "hello"}}}}`, 36 | get: "one.two.three.four", 37 | output: "hello", 38 | }, 39 | { 40 | name: "Get missing with dot notation", 41 | data: `{"one": {"two": {"three": {"four": "hello"}}}}`, 42 | get: "one.ten", 43 | output: nil, 44 | }, 45 | { 46 | name: "Get with array notation", 47 | data: `{"tags": ["one", "two", "three"]}`, 48 | get: "tags[1]", 49 | output: "two", 50 | }, 51 | { 52 | name: "Get with array and dot notation", 53 | data: `{"types": { "tags": ["one", "two", "three"]}}`, 54 | get: "types.tags[1]", 55 | output: "two", 56 | }, 57 | { 58 | name: "Get with array and dot notation - field after array", 59 | data: `{"tags": [{"name":"one"}, {"name":"two"}, {"name":"three"}]}`, 60 | get: "tags[1].name", 61 | output: "two", 62 | }, 63 | { 64 | name: "Complex get with array and dot notation", 65 | data: `{"tags": [{"list": [{"one":"pizza"}]}]}`, 66 | get: "tags[0].list[0].one", 67 | output: "pizza", 68 | }, 69 | { 70 | name: "Get field from within string should be nil", 71 | data: `{"name":"Tyler"}`, 72 | get: "name.something", 73 | output: nil, 74 | }, 75 | { 76 | name: "Get field from within string (using array accessor) should be nil", 77 | data: `{"numbers":["one", "two", "three"]}`, 78 | get: "numbers[0].nope", 79 | output: nil, 80 | }, 81 | } 82 | 83 | func TestFixtures(t *testing.T) { 84 | for _, fixture := range fixtures { 85 | m := objx.MustFromJSON(fixture.data) 86 | 87 | // get the value 88 | t.Logf("Running get fixture: \"%s\" (%v)", fixture.name, fixture) 89 | value := m.Get(fixture.get.(string)) 90 | 91 | // make sure it matches 92 | assert.Equal(t, fixture.output, value.Data(), 93 | "Get fixture \"%s\" failed: %v", fixture.name, fixture, 94 | ) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/stretchr/objx 2 | 3 | go 1.20 4 | 5 | require github.com/stretchr/testify v1.10.0 6 | 7 | require ( 8 | github.com/davecgh/go-spew v1.1.1 // indirect 9 | github.com/pmezard/go-difflib v1.0.0 // indirect 10 | gopkg.in/yaml.v3 v3.0.1 // indirect 11 | ) 12 | 13 | exclude github.com/stretchr/testify v1.8.0 14 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 6 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 7 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 8 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 9 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 10 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 11 | -------------------------------------------------------------------------------- /map.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "encoding/base64" 5 | "encoding/json" 6 | "errors" 7 | "io/ioutil" 8 | "net/url" 9 | "strings" 10 | ) 11 | 12 | // MSIConvertable is an interface that defines methods for converting your 13 | // custom types to a map[string]interface{} representation. 14 | type MSIConvertable interface { 15 | // MSI gets a map[string]interface{} (msi) representing the 16 | // object. 17 | MSI() map[string]interface{} 18 | } 19 | 20 | // Map provides extended functionality for working with 21 | // untyped data, in particular map[string]interface (msi). 22 | type Map map[string]interface{} 23 | 24 | // Value returns the internal value instance 25 | func (m Map) Value() *Value { 26 | return &Value{data: m} 27 | } 28 | 29 | // Nil represents a nil Map. 30 | var Nil = New(nil) 31 | 32 | // New creates a new Map containing the map[string]interface{} in the data argument. 33 | // If the data argument is not a map[string]interface, New attempts to call the 34 | // MSI() method on the MSIConvertable interface to create one. 35 | func New(data interface{}) Map { 36 | if _, ok := data.(map[string]interface{}); !ok { 37 | if converter, ok := data.(MSIConvertable); ok { 38 | data = converter.MSI() 39 | } else { 40 | return nil 41 | } 42 | } 43 | return Map(data.(map[string]interface{})) 44 | } 45 | 46 | // MSI creates a map[string]interface{} and puts it inside a new Map. 47 | // 48 | // The arguments follow a key, value pattern. 49 | // 50 | // Returns nil if any key argument is non-string or if there are an odd number of arguments. 51 | // 52 | // # Example 53 | // 54 | // To easily create Maps: 55 | // 56 | // m := objx.MSI("name", "Mat", "age", 29, "subobj", objx.MSI("active", true)) 57 | // 58 | // // creates an Map equivalent to 59 | // m := objx.Map{"name": "Mat", "age": 29, "subobj": objx.Map{"active": true}} 60 | func MSI(keyAndValuePairs ...interface{}) Map { 61 | newMap := Map{} 62 | keyAndValuePairsLen := len(keyAndValuePairs) 63 | if keyAndValuePairsLen%2 != 0 { 64 | return nil 65 | } 66 | for i := 0; i < keyAndValuePairsLen; i = i + 2 { 67 | key := keyAndValuePairs[i] 68 | value := keyAndValuePairs[i+1] 69 | 70 | // make sure the key is a string 71 | keyString, keyStringOK := key.(string) 72 | if !keyStringOK { 73 | return nil 74 | } 75 | newMap[keyString] = value 76 | } 77 | return newMap 78 | } 79 | 80 | // ****** Conversion Constructors 81 | 82 | // MustFromJSON creates a new Map containing the data specified in the 83 | // jsonString. 84 | // 85 | // Panics if the JSON is invalid. 86 | func MustFromJSON(jsonString string) Map { 87 | o, err := FromJSON(jsonString) 88 | if err != nil { 89 | panic("objx: MustFromJSON failed with error: " + err.Error()) 90 | } 91 | return o 92 | } 93 | 94 | // MustFromJSONSlice creates a new slice of Map containing the data specified in the 95 | // jsonString. Works with jsons with a top level array 96 | // 97 | // Panics if the JSON is invalid. 98 | func MustFromJSONSlice(jsonString string) []Map { 99 | slice, err := FromJSONSlice(jsonString) 100 | if err != nil { 101 | panic("objx: MustFromJSONSlice failed with error: " + err.Error()) 102 | } 103 | return slice 104 | } 105 | 106 | // FromJSON creates a new Map containing the data specified in the 107 | // jsonString. 108 | // 109 | // Returns an error if the JSON is invalid. 110 | func FromJSON(jsonString string) (Map, error) { 111 | var m Map 112 | err := json.Unmarshal([]byte(jsonString), &m) 113 | if err != nil { 114 | return Nil, err 115 | } 116 | return m, nil 117 | } 118 | 119 | // FromJSONSlice creates a new slice of Map containing the data specified in the 120 | // jsonString. Works with jsons with a top level array 121 | // 122 | // Returns an error if the JSON is invalid. 123 | func FromJSONSlice(jsonString string) ([]Map, error) { 124 | var slice []Map 125 | err := json.Unmarshal([]byte(jsonString), &slice) 126 | if err != nil { 127 | return nil, err 128 | } 129 | return slice, nil 130 | } 131 | 132 | // FromBase64 creates a new Obj containing the data specified 133 | // in the Base64 string. 134 | // 135 | // The string is an encoded JSON string returned by Base64 136 | func FromBase64(base64String string) (Map, error) { 137 | decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(base64String)) 138 | decoded, err := ioutil.ReadAll(decoder) 139 | if err != nil { 140 | return nil, err 141 | } 142 | return FromJSON(string(decoded)) 143 | } 144 | 145 | // MustFromBase64 creates a new Obj containing the data specified 146 | // in the Base64 string and panics if there is an error. 147 | // 148 | // The string is an encoded JSON string returned by Base64 149 | func MustFromBase64(base64String string) Map { 150 | result, err := FromBase64(base64String) 151 | if err != nil { 152 | panic("objx: MustFromBase64 failed with error: " + err.Error()) 153 | } 154 | return result 155 | } 156 | 157 | // FromSignedBase64 creates a new Obj containing the data specified 158 | // in the Base64 string. 159 | // 160 | // The string is an encoded JSON string returned by SignedBase64 161 | func FromSignedBase64(base64String, key string) (Map, error) { 162 | parts := strings.Split(base64String, SignatureSeparator) 163 | if len(parts) != 2 { 164 | return nil, errors.New("objx: Signed base64 string is malformed") 165 | } 166 | 167 | sig := HashWithKey(parts[0], key) 168 | if parts[1] != sig { 169 | return nil, errors.New("objx: Signature for base64 data does not match") 170 | } 171 | return FromBase64(parts[0]) 172 | } 173 | 174 | // MustFromSignedBase64 creates a new Obj containing the data specified 175 | // in the Base64 string and panics if there is an error. 176 | // 177 | // The string is an encoded JSON string returned by Base64 178 | func MustFromSignedBase64(base64String, key string) Map { 179 | result, err := FromSignedBase64(base64String, key) 180 | if err != nil { 181 | panic("objx: MustFromSignedBase64 failed with error: " + err.Error()) 182 | } 183 | return result 184 | } 185 | 186 | // FromURLQuery generates a new Obj by parsing the specified 187 | // query. 188 | // 189 | // For queries with multiple values, the first value is selected. 190 | func FromURLQuery(query string) (Map, error) { 191 | vals, err := url.ParseQuery(query) 192 | if err != nil { 193 | return nil, err 194 | } 195 | m := Map{} 196 | for k, vals := range vals { 197 | m[k] = vals[0] 198 | } 199 | return m, nil 200 | } 201 | 202 | // MustFromURLQuery generates a new Obj by parsing the specified 203 | // query. 204 | // 205 | // For queries with multiple values, the first value is selected. 206 | // 207 | // Panics if it encounters an error 208 | func MustFromURLQuery(query string) Map { 209 | o, err := FromURLQuery(query) 210 | if err != nil { 211 | panic("objx: MustFromURLQuery failed with error: " + err.Error()) 212 | } 213 | return o 214 | } 215 | -------------------------------------------------------------------------------- /map_test.go: -------------------------------------------------------------------------------- 1 | package objx_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/objx" 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | var TestMap = objx.Map{ 12 | "name": "Tyler", 13 | "address": objx.Map{ 14 | "city": "Salt Lake City", 15 | "state": "UT", 16 | }, 17 | "numbers": []interface{}{"one", "two", "three", "four", "five"}, 18 | } 19 | 20 | type Convertable struct { 21 | name string 22 | } 23 | 24 | type Unconvertable struct { 25 | name string 26 | } 27 | 28 | func (c *Convertable) MSI() map[string]interface{} { 29 | return objx.Map{"name": c.name} 30 | } 31 | 32 | func TestMapCreation(t *testing.T) { 33 | o := objx.New(nil) 34 | assert.Nil(t, o) 35 | 36 | o = objx.New("Tyler") 37 | assert.Nil(t, o) 38 | 39 | unconvertable := &Unconvertable{name: "Tyler"} 40 | o = objx.New(unconvertable) 41 | assert.Nil(t, o) 42 | 43 | convertable := &Convertable{name: "Tyler"} 44 | o = objx.New(convertable) 45 | require.NotNil(t, convertable) 46 | assert.Equal(t, "Tyler", o["name"]) 47 | 48 | o = objx.MSI() 49 | assert.NotNil(t, o) 50 | 51 | o = objx.MSI("name", "Tyler") 52 | require.NotNil(t, o) 53 | assert.Equal(t, o["name"], "Tyler") 54 | 55 | o = objx.MSI(1, "a") 56 | assert.Nil(t, o) 57 | 58 | o = objx.MSI("a") 59 | assert.Nil(t, o) 60 | 61 | o = objx.MSI("a", "b", "c") 62 | assert.Nil(t, o) 63 | } 64 | 65 | func TestMapValue(t *testing.T) { 66 | m := objx.Map{ 67 | "a": 1, 68 | } 69 | v := m.Value() 70 | 71 | assert.Equal(t, m, v.ObjxMap()) 72 | } 73 | 74 | func TestMapMustFromJSONWithError(t *testing.T) { 75 | _, err := objx.FromJSON(`"name":"Mat"}`) 76 | assert.Error(t, err) 77 | } 78 | 79 | func TestMapFromJSON(t *testing.T) { 80 | o := objx.MustFromJSON(`{"name":"Mat"}`) 81 | 82 | require.NotNil(t, o) 83 | assert.Equal(t, "Mat", o["name"]) 84 | } 85 | 86 | func TestMapFromJSONWithError(t *testing.T) { 87 | var m objx.Map 88 | 89 | assert.Panics(t, func() { 90 | m = objx.MustFromJSON(`"name":"Mat"}`) 91 | }) 92 | assert.Nil(t, m) 93 | } 94 | 95 | func TestConversionJSONInt(t *testing.T) { 96 | jsonString := 97 | `{ 98 | "a": 1, 99 | "b": { 100 | "data": 1 101 | }, 102 | "c": [1], 103 | "d": [[1]] 104 | }` 105 | m, err := objx.FromJSON(jsonString) 106 | 107 | assert.Nil(t, err) 108 | require.NotNil(t, m) 109 | assert.Equal(t, 1, m.Get("a").Int()) 110 | assert.Equal(t, 1, m.Get("b.data").Int()) 111 | 112 | assert.True(t, m.Get("c").IsInterSlice()) 113 | assert.Equal(t, float64(1), m.Get("c").InterSlice()[0]) 114 | 115 | assert.True(t, m.Get("d").IsInterSlice()) 116 | assert.Equal(t, []interface{}{float64(1)}, m.Get("d").InterSlice()[0]) 117 | } 118 | 119 | func TestJSONSliceInt(t *testing.T) { 120 | jsonString := 121 | `{ 122 | "a": [ 123 | {"b": 1}, 124 | {"c": 2} 125 | ] 126 | }` 127 | m, err := objx.FromJSON(jsonString) 128 | 129 | assert.Nil(t, err) 130 | require.NotNil(t, m) 131 | assert.Equal(t, []objx.Map{{"b": float64(1)}, {"c": float64(2)}}, m.Get("a").ObjxMapSlice()) 132 | } 133 | 134 | func TestJSONSliceMixed(t *testing.T) { 135 | jsonString := 136 | `{ 137 | "a": [ 138 | {"b": 1}, 139 | "a" 140 | ] 141 | }` 142 | m, err := objx.FromJSON(jsonString) 143 | 144 | assert.Nil(t, err) 145 | require.NotNil(t, m) 146 | 147 | assert.Nil(t, m.Get("a").ObjxMapSlice()) 148 | } 149 | 150 | func TestMapFromBase64String(t *testing.T) { 151 | base64String := "eyJuYW1lIjoiTWF0In0=" 152 | o, err := objx.FromBase64(base64String) 153 | 154 | require.NoError(t, err) 155 | assert.Equal(t, o.Get("name").Str(), "Mat") 156 | assert.Equal(t, objx.MustFromBase64(base64String).Get("name").Str(), "Mat") 157 | } 158 | 159 | func TestMapFromBase64StringWithError(t *testing.T) { 160 | base64String := "eyJuYW1lIjoiTWFasd0In0=" 161 | _, err := objx.FromBase64(base64String) 162 | 163 | assert.Error(t, err) 164 | assert.Panics(t, func() { 165 | objx.MustFromBase64(base64String) 166 | }) 167 | } 168 | 169 | func TestMapFromSignedBase64String(t *testing.T) { 170 | base64String := "eyJuYW1lIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6" 171 | 172 | o, err := objx.FromSignedBase64(base64String, "key") 173 | 174 | require.NoError(t, err) 175 | assert.Equal(t, o.Get("name").Str(), "Mat") 176 | assert.Equal(t, objx.MustFromSignedBase64(base64String, "key").Get("name").Str(), "Mat") 177 | } 178 | 179 | func TestMapFromSignedBase64StringWithError(t *testing.T) { 180 | base64String := "eyJuYW1lasdIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6" 181 | _, err := objx.FromSignedBase64(base64String, "key") 182 | assert.Error(t, err) 183 | assert.Panics(t, func() { 184 | objx.MustFromSignedBase64(base64String, "key") 185 | }) 186 | 187 | base64String = "eyJuYW1lasdIjoiTWF0In0=67ee82916f90b2c0d68c903266e8998c9ef0c3d6" 188 | _, err = objx.FromSignedBase64(base64String, "key") 189 | assert.Error(t, err) 190 | assert.Panics(t, func() { 191 | objx.MustFromSignedBase64(base64String, "key") 192 | }) 193 | 194 | base64String = "eyJuYW1lIjoiTWF0In0=_67ee82916f90b2c0d68c903266e8998c9ef0c3d6_junk" 195 | _, err = objx.FromSignedBase64(base64String, "key") 196 | assert.Error(t, err) 197 | assert.Panics(t, func() { 198 | objx.MustFromSignedBase64(base64String, "key") 199 | }) 200 | } 201 | 202 | func TestMapFromURLQuery(t *testing.T) { 203 | m, err := objx.FromURLQuery("name=tyler&state=UT") 204 | 205 | assert.NoError(t, err) 206 | require.NotNil(t, m) 207 | assert.Equal(t, "tyler", m.Get("name").Str()) 208 | assert.Equal(t, "UT", m.Get("state").Str()) 209 | } 210 | 211 | func TestMapMustFromURLQuery(t *testing.T) { 212 | m := objx.MustFromURLQuery("name=tyler&state=UT") 213 | 214 | require.NotNil(t, m) 215 | assert.Equal(t, "tyler", m.Get("name").Str()) 216 | assert.Equal(t, "UT", m.Get("state").Str()) 217 | } 218 | 219 | func TestMapFromURLQueryWithError(t *testing.T) { 220 | m, err := objx.FromURLQuery("%") 221 | 222 | assert.Error(t, err) 223 | assert.Nil(t, m) 224 | assert.Panics(t, func() { 225 | objx.MustFromURLQuery("%") 226 | }) 227 | } 228 | 229 | func TestJSONTopLevelSlice(t *testing.T) { 230 | slice, err := objx.FromJSONSlice(`[{"id": 10000001}, {"id": 42}]`) 231 | 232 | assert.NoError(t, err) 233 | require.Len(t, slice, 2) 234 | assert.Equal(t, 10000001, slice[0].Get("id").MustInt()) 235 | assert.Equal(t, 42, slice[1].Get("id").MustInt()) 236 | } 237 | 238 | func TestJSONTopLevelSliceWithError(t *testing.T) { 239 | slice, err := objx.FromJSONSlice(`{"id": 10000001}`) 240 | 241 | assert.Error(t, err) 242 | assert.Nil(t, slice) 243 | assert.Panics(t, func() { 244 | _ = objx.MustFromJSONSlice(`{"id": 10000001}`) 245 | }) 246 | } 247 | -------------------------------------------------------------------------------- /mutations.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | // Exclude returns a new Map with the keys in the specified []string 4 | // excluded. 5 | func (m Map) Exclude(exclude []string) Map { 6 | excluded := make(Map) 7 | for k, v := range m { 8 | if !contains(exclude, k) { 9 | excluded[k] = v 10 | } 11 | } 12 | return excluded 13 | } 14 | 15 | // Copy creates a shallow copy of the Obj. 16 | func (m Map) Copy() Map { 17 | copied := Map{} 18 | for k, v := range m { 19 | copied[k] = v 20 | } 21 | return copied 22 | } 23 | 24 | // Merge blends the specified map with a copy of this map and returns the result. 25 | // 26 | // Keys that appear in both will be selected from the specified map. 27 | // This method requires that the wrapped object be a map[string]interface{} 28 | func (m Map) Merge(merge Map) Map { 29 | return m.Copy().MergeHere(merge) 30 | } 31 | 32 | // MergeHere blends the specified map with this map and returns the current map. 33 | // 34 | // Keys that appear in both will be selected from the specified map. The original map 35 | // will be modified. This method requires that 36 | // the wrapped object be a map[string]interface{} 37 | func (m Map) MergeHere(merge Map) Map { 38 | for k, v := range merge { 39 | m[k] = v 40 | } 41 | return m 42 | } 43 | 44 | // Transform builds a new Obj giving the transformer a chance 45 | // to change the keys and values as it goes. This method requires that 46 | // the wrapped object be a map[string]interface{} 47 | func (m Map) Transform(transformer func(key string, value interface{}) (string, interface{})) Map { 48 | newMap := Map{} 49 | for k, v := range m { 50 | modifiedKey, modifiedVal := transformer(k, v) 51 | newMap[modifiedKey] = modifiedVal 52 | } 53 | return newMap 54 | } 55 | 56 | // TransformKeys builds a new map using the specified key mapping. 57 | // 58 | // Unspecified keys will be unaltered. 59 | // This method requires that the wrapped object be a map[string]interface{} 60 | func (m Map) TransformKeys(mapping map[string]string) Map { 61 | return m.Transform(func(key string, value interface{}) (string, interface{}) { 62 | if newKey, ok := mapping[key]; ok { 63 | return newKey, value 64 | } 65 | return key, value 66 | }) 67 | } 68 | 69 | // Checks if a string slice contains a string 70 | func contains(s []string, e string) bool { 71 | for _, a := range s { 72 | if a == e { 73 | return true 74 | } 75 | } 76 | return false 77 | } 78 | -------------------------------------------------------------------------------- /mutations_test.go: -------------------------------------------------------------------------------- 1 | package objx_test 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/stretchr/objx" 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestExclude(t *testing.T) { 13 | m := objx.Map{ 14 | "name": "Mat", 15 | "age": 29, 16 | "secret": "ABC", 17 | } 18 | 19 | excluded := m.Exclude([]string{"secret"}) 20 | 21 | assert.Equal(t, m["name"], excluded["name"]) 22 | assert.Equal(t, m["age"], excluded["age"]) 23 | assert.False(t, excluded.Has("secret"), "secret should be excluded") 24 | } 25 | 26 | func TestCopy(t *testing.T) { 27 | m1 := objx.Map{ 28 | "name": "Tyler", 29 | "location": "UT", 30 | } 31 | 32 | m2 := m1.Copy() 33 | require.NotNil(t, m2) 34 | m2["name"] = "Mat" 35 | 36 | assert.Equal(t, m1.Get("name").Str(), "Tyler") 37 | assert.Equal(t, m2.Get("name").Str(), "Mat") 38 | 39 | } 40 | 41 | func TestMerge(t *testing.T) { 42 | m1 := objx.Map{ 43 | "name": "Mat", 44 | } 45 | m2 := objx.Map{ 46 | "name": "Tyler", 47 | "location": "UT", 48 | } 49 | 50 | merged := m1.Merge(m2) 51 | 52 | assert.Equal(t, merged.Get("name").Str(), m2.Get("name").Str()) 53 | assert.Equal(t, merged.Get("location").Str(), m2.Get("location").Str()) 54 | assert.Empty(t, m1.Get("location").Str()) 55 | } 56 | 57 | func TestMergeHere(t *testing.T) { 58 | m1 := objx.Map{ 59 | "name": "Mat", 60 | } 61 | m2 := objx.Map{ 62 | "name": "Tyler", 63 | "location": "UT", 64 | } 65 | 66 | merged := m1.MergeHere(m2) 67 | 68 | assert.Equal(t, m1, merged, "With MergeHere, it should return the first modified map") 69 | assert.Equal(t, merged.Get("name").Str(), m2.Get("name").Str()) 70 | assert.Equal(t, merged.Get("location").Str(), m2.Get("location").Str()) 71 | assert.Equal(t, merged.Get("location").Str(), m1.Get("location").Str()) 72 | } 73 | 74 | func TestTransform(t *testing.T) { 75 | m := objx.Map{ 76 | "name": "Mat", 77 | "location": "UK", 78 | } 79 | r := m.Transform(keyToUpper) 80 | assert.Equal(t, objx.Map{ 81 | "NAME": "Mat", 82 | "LOCATION": "UK", 83 | }, r) 84 | } 85 | 86 | func TestTransformKeys(t *testing.T) { 87 | m := objx.Map{ 88 | "a": "1", 89 | "b": "2", 90 | "c": "3", 91 | } 92 | mapping := map[string]string{ 93 | "a": "d", 94 | "b": "e", 95 | } 96 | r := m.TransformKeys(mapping) 97 | assert.Equal(t, objx.Map{ 98 | "c": "3", 99 | "d": "1", 100 | "e": "2", 101 | }, r) 102 | } 103 | 104 | func keyToUpper(s string, v interface{}) (string, interface{}) { 105 | return strings.ToUpper(s), v 106 | } 107 | -------------------------------------------------------------------------------- /security.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "crypto/sha1" 5 | "encoding/hex" 6 | ) 7 | 8 | // HashWithKey hashes the specified string using the security key 9 | func HashWithKey(data, key string) string { 10 | d := sha1.Sum([]byte(data + ":" + key)) 11 | return hex.EncodeToString(d[:]) 12 | } 13 | -------------------------------------------------------------------------------- /security_test.go: -------------------------------------------------------------------------------- 1 | package objx_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/objx" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestHashWithKey(t *testing.T) { 11 | assert.Equal(t, "0ce84d8d01f2c7b6e0882b784429c54d280ea2d9", objx.HashWithKey("abc", "def")) 12 | } 13 | -------------------------------------------------------------------------------- /simple_example_test.go: -------------------------------------------------------------------------------- 1 | package objx_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/objx" 7 | "github.com/stretchr/testify/assert" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestSimpleExample(t *testing.T) { 12 | // build a map from a JSON object 13 | o := objx.MustFromJSON(`{"name":"Mat","foods":["indian","chinese"], "location":{"county":"hobbiton","city":"the shire"}}`) 14 | 15 | // Map can be used as a straight map[string]interface{} 16 | assert.Equal(t, o["name"], "Mat") 17 | 18 | // Get an Value object 19 | v := o.Get("name") 20 | require.NotNil(t, v) 21 | 22 | // Test the contained value 23 | assert.False(t, v.IsInt()) 24 | assert.False(t, v.IsBool()) 25 | assert.True(t, v.IsStr()) 26 | 27 | // Get the contained value 28 | assert.Equal(t, v.Str(), "Mat") 29 | 30 | // Get a default value if the contained value is not of the expected type or does not exist 31 | assert.Equal(t, 1, v.Int(1)) 32 | 33 | // Get a value by using array notation 34 | assert.Equal(t, "indian", o.Get("foods[0]").Data()) 35 | 36 | // Set a value by using array notation 37 | o.Set("foods[0]", "italian") 38 | assert.Equal(t, "italian", o.Get("foods[0]").Str()) 39 | 40 | // Get a value by using dot notation 41 | assert.Equal(t, "hobbiton", o.Get("location.county").Str()) 42 | } 43 | -------------------------------------------------------------------------------- /tests.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | // Has gets whether there is something at the specified selector 4 | // or not. 5 | // 6 | // If m is nil, Has will always return false. 7 | func (m Map) Has(selector string) bool { 8 | if m == nil { 9 | return false 10 | } 11 | return !m.Get(selector).IsNil() 12 | } 13 | 14 | // IsNil gets whether the data is nil or not. 15 | func (v *Value) IsNil() bool { 16 | return v == nil || v.data == nil 17 | } 18 | -------------------------------------------------------------------------------- /tests_test.go: -------------------------------------------------------------------------------- 1 | package objx_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/objx" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestHas(t *testing.T) { 11 | m := objx.Map(TestMap) 12 | 13 | assert.True(t, m.Has("name")) 14 | assert.True(t, m.Has("address.state")) 15 | assert.True(t, m.Has("numbers[4]")) 16 | 17 | assert.False(t, m.Has("address.state.nope")) 18 | assert.False(t, m.Has("address.nope")) 19 | assert.False(t, m.Has("nope")) 20 | assert.False(t, m.Has("numbers[5]")) 21 | 22 | m = nil 23 | 24 | assert.False(t, m.Has("nothing")) 25 | } 26 | -------------------------------------------------------------------------------- /type_specific.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | /* 4 | MSI (map[string]interface{} and []map[string]interface{}) 5 | */ 6 | 7 | // MSI gets the value as a map[string]interface{}, returns the optionalDefault 8 | // value or a system default object if the value is the wrong type. 9 | func (v *Value) MSI(optionalDefault ...map[string]interface{}) map[string]interface{} { 10 | if s, ok := v.data.(map[string]interface{}); ok { 11 | return s 12 | } 13 | if s, ok := v.data.(Map); ok { 14 | return map[string]interface{}(s) 15 | } 16 | if len(optionalDefault) == 1 { 17 | return optionalDefault[0] 18 | } 19 | return nil 20 | } 21 | 22 | // MustMSI gets the value as a map[string]interface{}. 23 | // 24 | // Panics if the object is not a map[string]interface{}. 25 | func (v *Value) MustMSI() map[string]interface{} { 26 | if s, ok := v.data.(Map); ok { 27 | return map[string]interface{}(s) 28 | } 29 | return v.data.(map[string]interface{}) 30 | } 31 | 32 | // MSISlice gets the value as a []map[string]interface{}, returns the optionalDefault 33 | // value or nil if the value is not a []map[string]interface{}. 34 | func (v *Value) MSISlice(optionalDefault ...[]map[string]interface{}) []map[string]interface{} { 35 | if s, ok := v.data.([]map[string]interface{}); ok { 36 | return s 37 | } 38 | 39 | s := v.ObjxMapSlice() 40 | if s == nil { 41 | if len(optionalDefault) == 1 { 42 | return optionalDefault[0] 43 | } 44 | return nil 45 | } 46 | 47 | result := make([]map[string]interface{}, len(s)) 48 | for i := range s { 49 | result[i] = s[i].Value().MSI() 50 | } 51 | return result 52 | } 53 | 54 | // MustMSISlice gets the value as a []map[string]interface{}. 55 | // 56 | // Panics if the object is not a []map[string]interface{}. 57 | func (v *Value) MustMSISlice() []map[string]interface{} { 58 | if s := v.MSISlice(); s != nil { 59 | return s 60 | } 61 | 62 | return v.data.([]map[string]interface{}) 63 | } 64 | 65 | // IsMSI gets whether the object contained is a map[string]interface{} or not. 66 | func (v *Value) IsMSI() bool { 67 | _, ok := v.data.(map[string]interface{}) 68 | if !ok { 69 | _, ok = v.data.(Map) 70 | } 71 | return ok 72 | } 73 | 74 | // IsMSISlice gets whether the object contained is a []map[string]interface{} or not. 75 | func (v *Value) IsMSISlice() bool { 76 | _, ok := v.data.([]map[string]interface{}) 77 | if !ok { 78 | _, ok = v.data.([]Map) 79 | if !ok { 80 | s, ok := v.data.([]interface{}) 81 | if ok { 82 | for i := range s { 83 | switch s[i].(type) { 84 | case Map: 85 | case map[string]interface{}: 86 | default: 87 | return false 88 | } 89 | } 90 | return true 91 | } 92 | } 93 | } 94 | return ok 95 | } 96 | 97 | // EachMSI calls the specified callback for each object 98 | // in the []map[string]interface{}. 99 | // 100 | // Panics if the object is the wrong type. 101 | func (v *Value) EachMSI(callback func(int, map[string]interface{}) bool) *Value { 102 | for index, val := range v.MustMSISlice() { 103 | carryon := callback(index, val) 104 | if !carryon { 105 | break 106 | } 107 | } 108 | return v 109 | } 110 | 111 | // WhereMSI uses the specified decider function to select items 112 | // from the []map[string]interface{}. The object contained in the result will contain 113 | // only the selected items. 114 | func (v *Value) WhereMSI(decider func(int, map[string]interface{}) bool) *Value { 115 | var selected []map[string]interface{} 116 | v.EachMSI(func(index int, val map[string]interface{}) bool { 117 | shouldSelect := decider(index, val) 118 | if !shouldSelect { 119 | selected = append(selected, val) 120 | } 121 | return true 122 | }) 123 | return &Value{data: selected} 124 | } 125 | 126 | // GroupMSI uses the specified grouper function to group the items 127 | // keyed by the return of the grouper. The object contained in the 128 | // result will contain a map[string][]map[string]interface{}. 129 | func (v *Value) GroupMSI(grouper func(int, map[string]interface{}) string) *Value { 130 | groups := make(map[string][]map[string]interface{}) 131 | v.EachMSI(func(index int, val map[string]interface{}) bool { 132 | group := grouper(index, val) 133 | if _, ok := groups[group]; !ok { 134 | groups[group] = make([]map[string]interface{}, 0) 135 | } 136 | groups[group] = append(groups[group], val) 137 | return true 138 | }) 139 | return &Value{data: groups} 140 | } 141 | 142 | // ReplaceMSI uses the specified function to replace each map[string]interface{}s 143 | // by iterating each item. The data in the returned result will be a 144 | // []map[string]interface{} containing the replaced items. 145 | func (v *Value) ReplaceMSI(replacer func(int, map[string]interface{}) map[string]interface{}) *Value { 146 | arr := v.MustMSISlice() 147 | replaced := make([]map[string]interface{}, len(arr)) 148 | v.EachMSI(func(index int, val map[string]interface{}) bool { 149 | replaced[index] = replacer(index, val) 150 | return true 151 | }) 152 | return &Value{data: replaced} 153 | } 154 | 155 | // CollectMSI uses the specified collector function to collect a value 156 | // for each of the map[string]interface{}s in the slice. The data returned will be a 157 | // []interface{}. 158 | func (v *Value) CollectMSI(collector func(int, map[string]interface{}) interface{}) *Value { 159 | arr := v.MustMSISlice() 160 | collected := make([]interface{}, len(arr)) 161 | v.EachMSI(func(index int, val map[string]interface{}) bool { 162 | collected[index] = collector(index, val) 163 | return true 164 | }) 165 | return &Value{data: collected} 166 | } 167 | 168 | /* 169 | ObjxMap ((Map) and [](Map)) 170 | */ 171 | 172 | // ObjxMap gets the value as a (Map), returns the optionalDefault 173 | // value or a system default object if the value is the wrong type. 174 | func (v *Value) ObjxMap(optionalDefault ...(Map)) Map { 175 | if s, ok := v.data.((Map)); ok { 176 | return s 177 | } 178 | if s, ok := v.data.(map[string]interface{}); ok { 179 | return s 180 | } 181 | if len(optionalDefault) == 1 { 182 | return optionalDefault[0] 183 | } 184 | return New(nil) 185 | } 186 | 187 | // MustObjxMap gets the value as a (Map). 188 | // 189 | // Panics if the object is not a (Map). 190 | func (v *Value) MustObjxMap() Map { 191 | if s, ok := v.data.(map[string]interface{}); ok { 192 | return s 193 | } 194 | return v.data.((Map)) 195 | } 196 | 197 | // ObjxMapSlice gets the value as a [](Map), returns the optionalDefault 198 | // value or nil if the value is not a [](Map). 199 | func (v *Value) ObjxMapSlice(optionalDefault ...[](Map)) [](Map) { 200 | if s, ok := v.data.([]Map); ok { 201 | return s 202 | } 203 | 204 | if s, ok := v.data.([]map[string]interface{}); ok { 205 | result := make([]Map, len(s)) 206 | for i := range s { 207 | result[i] = s[i] 208 | } 209 | return result 210 | } 211 | 212 | s, ok := v.data.([]interface{}) 213 | if !ok { 214 | if len(optionalDefault) == 1 { 215 | return optionalDefault[0] 216 | } 217 | return nil 218 | } 219 | 220 | result := make([]Map, len(s)) 221 | for i := range s { 222 | switch s[i].(type) { 223 | case Map: 224 | result[i] = s[i].(Map) 225 | case map[string]interface{}: 226 | result[i] = New(s[i]) 227 | default: 228 | return nil 229 | } 230 | } 231 | return result 232 | } 233 | 234 | // MustObjxMapSlice gets the value as a [](Map). 235 | // 236 | // Panics if the object is not a [](Map). 237 | func (v *Value) MustObjxMapSlice() [](Map) { 238 | if s := v.ObjxMapSlice(); s != nil { 239 | return s 240 | } 241 | return v.data.([](Map)) 242 | } 243 | 244 | // IsObjxMap gets whether the object contained is a (Map) or not. 245 | func (v *Value) IsObjxMap() bool { 246 | _, ok := v.data.((Map)) 247 | if !ok { 248 | _, ok = v.data.(map[string]interface{}) 249 | } 250 | return ok 251 | } 252 | 253 | // IsObjxMapSlice gets whether the object contained is a [](Map) or not. 254 | func (v *Value) IsObjxMapSlice() bool { 255 | _, ok := v.data.([](Map)) 256 | if !ok { 257 | _, ok = v.data.([]map[string]interface{}) 258 | if !ok { 259 | s, ok := v.data.([]interface{}) 260 | if ok { 261 | for i := range s { 262 | switch s[i].(type) { 263 | case Map: 264 | case map[string]interface{}: 265 | default: 266 | return false 267 | } 268 | } 269 | return true 270 | } 271 | } 272 | } 273 | 274 | return ok 275 | } 276 | 277 | // EachObjxMap calls the specified callback for each object 278 | // in the [](Map). 279 | // 280 | // Panics if the object is the wrong type. 281 | func (v *Value) EachObjxMap(callback func(int, Map) bool) *Value { 282 | for index, val := range v.MustObjxMapSlice() { 283 | carryon := callback(index, val) 284 | if !carryon { 285 | break 286 | } 287 | } 288 | return v 289 | } 290 | 291 | // WhereObjxMap uses the specified decider function to select items 292 | // from the [](Map). The object contained in the result will contain 293 | // only the selected items. 294 | func (v *Value) WhereObjxMap(decider func(int, Map) bool) *Value { 295 | var selected [](Map) 296 | v.EachObjxMap(func(index int, val Map) bool { 297 | shouldSelect := decider(index, val) 298 | if !shouldSelect { 299 | selected = append(selected, val) 300 | } 301 | return true 302 | }) 303 | return &Value{data: selected} 304 | } 305 | 306 | // GroupObjxMap uses the specified grouper function to group the items 307 | // keyed by the return of the grouper. The object contained in the 308 | // result will contain a map[string][](Map). 309 | func (v *Value) GroupObjxMap(grouper func(int, Map) string) *Value { 310 | groups := make(map[string][](Map)) 311 | v.EachObjxMap(func(index int, val Map) bool { 312 | group := grouper(index, val) 313 | if _, ok := groups[group]; !ok { 314 | groups[group] = make([](Map), 0) 315 | } 316 | groups[group] = append(groups[group], val) 317 | return true 318 | }) 319 | return &Value{data: groups} 320 | } 321 | 322 | // ReplaceObjxMap uses the specified function to replace each (Map)s 323 | // by iterating each item. The data in the returned result will be a 324 | // [](Map) containing the replaced items. 325 | func (v *Value) ReplaceObjxMap(replacer func(int, Map) Map) *Value { 326 | arr := v.MustObjxMapSlice() 327 | replaced := make([](Map), len(arr)) 328 | v.EachObjxMap(func(index int, val Map) bool { 329 | replaced[index] = replacer(index, val) 330 | return true 331 | }) 332 | return &Value{data: replaced} 333 | } 334 | 335 | // CollectObjxMap uses the specified collector function to collect a value 336 | // for each of the (Map)s in the slice. The data returned will be a 337 | // []interface{}. 338 | func (v *Value) CollectObjxMap(collector func(int, Map) interface{}) *Value { 339 | arr := v.MustObjxMapSlice() 340 | collected := make([]interface{}, len(arr)) 341 | v.EachObjxMap(func(index int, val Map) bool { 342 | collected[index] = collector(index, val) 343 | return true 344 | }) 345 | return &Value{data: collected} 346 | } 347 | -------------------------------------------------------------------------------- /type_specific_codegen.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | /* 4 | Inter (interface{} and []interface{}) 5 | */ 6 | 7 | // Inter gets the value as a interface{}, returns the optionalDefault 8 | // value or a system default object if the value is the wrong type. 9 | func (v *Value) Inter(optionalDefault ...interface{}) interface{} { 10 | if s, ok := v.data.(interface{}); ok { 11 | return s 12 | } 13 | if len(optionalDefault) == 1 { 14 | return optionalDefault[0] 15 | } 16 | return nil 17 | } 18 | 19 | // MustInter gets the value as a interface{}. 20 | // 21 | // Panics if the object is not a interface{}. 22 | func (v *Value) MustInter() interface{} { 23 | return v.data.(interface{}) 24 | } 25 | 26 | // InterSlice gets the value as a []interface{}, returns the optionalDefault 27 | // value or nil if the value is not a []interface{}. 28 | func (v *Value) InterSlice(optionalDefault ...[]interface{}) []interface{} { 29 | if s, ok := v.data.([]interface{}); ok { 30 | return s 31 | } 32 | if len(optionalDefault) == 1 { 33 | return optionalDefault[0] 34 | } 35 | return nil 36 | } 37 | 38 | // MustInterSlice gets the value as a []interface{}. 39 | // 40 | // Panics if the object is not a []interface{}. 41 | func (v *Value) MustInterSlice() []interface{} { 42 | return v.data.([]interface{}) 43 | } 44 | 45 | // IsInter gets whether the object contained is a interface{} or not. 46 | func (v *Value) IsInter() bool { 47 | _, ok := v.data.(interface{}) 48 | return ok 49 | } 50 | 51 | // IsInterSlice gets whether the object contained is a []interface{} or not. 52 | func (v *Value) IsInterSlice() bool { 53 | _, ok := v.data.([]interface{}) 54 | return ok 55 | } 56 | 57 | // EachInter calls the specified callback for each object 58 | // in the []interface{}. 59 | // 60 | // Panics if the object is the wrong type. 61 | func (v *Value) EachInter(callback func(int, interface{}) bool) *Value { 62 | for index, val := range v.MustInterSlice() { 63 | carryon := callback(index, val) 64 | if !carryon { 65 | break 66 | } 67 | } 68 | return v 69 | } 70 | 71 | // WhereInter uses the specified decider function to select items 72 | // from the []interface{}. The object contained in the result will contain 73 | // only the selected items. 74 | func (v *Value) WhereInter(decider func(int, interface{}) bool) *Value { 75 | var selected []interface{} 76 | v.EachInter(func(index int, val interface{}) bool { 77 | shouldSelect := decider(index, val) 78 | if !shouldSelect { 79 | selected = append(selected, val) 80 | } 81 | return true 82 | }) 83 | return &Value{data: selected} 84 | } 85 | 86 | // GroupInter uses the specified grouper function to group the items 87 | // keyed by the return of the grouper. The object contained in the 88 | // result will contain a map[string][]interface{}. 89 | func (v *Value) GroupInter(grouper func(int, interface{}) string) *Value { 90 | groups := make(map[string][]interface{}) 91 | v.EachInter(func(index int, val interface{}) bool { 92 | group := grouper(index, val) 93 | if _, ok := groups[group]; !ok { 94 | groups[group] = make([]interface{}, 0) 95 | } 96 | groups[group] = append(groups[group], val) 97 | return true 98 | }) 99 | return &Value{data: groups} 100 | } 101 | 102 | // ReplaceInter uses the specified function to replace each interface{}s 103 | // by iterating each item. The data in the returned result will be a 104 | // []interface{} containing the replaced items. 105 | func (v *Value) ReplaceInter(replacer func(int, interface{}) interface{}) *Value { 106 | arr := v.MustInterSlice() 107 | replaced := make([]interface{}, len(arr)) 108 | v.EachInter(func(index int, val interface{}) bool { 109 | replaced[index] = replacer(index, val) 110 | return true 111 | }) 112 | return &Value{data: replaced} 113 | } 114 | 115 | // CollectInter uses the specified collector function to collect a value 116 | // for each of the interface{}s in the slice. The data returned will be a 117 | // []interface{}. 118 | func (v *Value) CollectInter(collector func(int, interface{}) interface{}) *Value { 119 | arr := v.MustInterSlice() 120 | collected := make([]interface{}, len(arr)) 121 | v.EachInter(func(index int, val interface{}) bool { 122 | collected[index] = collector(index, val) 123 | return true 124 | }) 125 | return &Value{data: collected} 126 | } 127 | 128 | /* 129 | Bool (bool and []bool) 130 | */ 131 | 132 | // Bool gets the value as a bool, returns the optionalDefault 133 | // value or a system default object if the value is the wrong type. 134 | func (v *Value) Bool(optionalDefault ...bool) bool { 135 | if s, ok := v.data.(bool); ok { 136 | return s 137 | } 138 | if len(optionalDefault) == 1 { 139 | return optionalDefault[0] 140 | } 141 | return false 142 | } 143 | 144 | // MustBool gets the value as a bool. 145 | // 146 | // Panics if the object is not a bool. 147 | func (v *Value) MustBool() bool { 148 | return v.data.(bool) 149 | } 150 | 151 | // BoolSlice gets the value as a []bool, returns the optionalDefault 152 | // value or nil if the value is not a []bool. 153 | func (v *Value) BoolSlice(optionalDefault ...[]bool) []bool { 154 | if s, ok := v.data.([]bool); ok { 155 | return s 156 | } 157 | if len(optionalDefault) == 1 { 158 | return optionalDefault[0] 159 | } 160 | return nil 161 | } 162 | 163 | // MustBoolSlice gets the value as a []bool. 164 | // 165 | // Panics if the object is not a []bool. 166 | func (v *Value) MustBoolSlice() []bool { 167 | return v.data.([]bool) 168 | } 169 | 170 | // IsBool gets whether the object contained is a bool or not. 171 | func (v *Value) IsBool() bool { 172 | _, ok := v.data.(bool) 173 | return ok 174 | } 175 | 176 | // IsBoolSlice gets whether the object contained is a []bool or not. 177 | func (v *Value) IsBoolSlice() bool { 178 | _, ok := v.data.([]bool) 179 | return ok 180 | } 181 | 182 | // EachBool calls the specified callback for each object 183 | // in the []bool. 184 | // 185 | // Panics if the object is the wrong type. 186 | func (v *Value) EachBool(callback func(int, bool) bool) *Value { 187 | for index, val := range v.MustBoolSlice() { 188 | carryon := callback(index, val) 189 | if !carryon { 190 | break 191 | } 192 | } 193 | return v 194 | } 195 | 196 | // WhereBool uses the specified decider function to select items 197 | // from the []bool. The object contained in the result will contain 198 | // only the selected items. 199 | func (v *Value) WhereBool(decider func(int, bool) bool) *Value { 200 | var selected []bool 201 | v.EachBool(func(index int, val bool) bool { 202 | shouldSelect := decider(index, val) 203 | if !shouldSelect { 204 | selected = append(selected, val) 205 | } 206 | return true 207 | }) 208 | return &Value{data: selected} 209 | } 210 | 211 | // GroupBool uses the specified grouper function to group the items 212 | // keyed by the return of the grouper. The object contained in the 213 | // result will contain a map[string][]bool. 214 | func (v *Value) GroupBool(grouper func(int, bool) string) *Value { 215 | groups := make(map[string][]bool) 216 | v.EachBool(func(index int, val bool) bool { 217 | group := grouper(index, val) 218 | if _, ok := groups[group]; !ok { 219 | groups[group] = make([]bool, 0) 220 | } 221 | groups[group] = append(groups[group], val) 222 | return true 223 | }) 224 | return &Value{data: groups} 225 | } 226 | 227 | // ReplaceBool uses the specified function to replace each bools 228 | // by iterating each item. The data in the returned result will be a 229 | // []bool containing the replaced items. 230 | func (v *Value) ReplaceBool(replacer func(int, bool) bool) *Value { 231 | arr := v.MustBoolSlice() 232 | replaced := make([]bool, len(arr)) 233 | v.EachBool(func(index int, val bool) bool { 234 | replaced[index] = replacer(index, val) 235 | return true 236 | }) 237 | return &Value{data: replaced} 238 | } 239 | 240 | // CollectBool uses the specified collector function to collect a value 241 | // for each of the bools in the slice. The data returned will be a 242 | // []interface{}. 243 | func (v *Value) CollectBool(collector func(int, bool) interface{}) *Value { 244 | arr := v.MustBoolSlice() 245 | collected := make([]interface{}, len(arr)) 246 | v.EachBool(func(index int, val bool) bool { 247 | collected[index] = collector(index, val) 248 | return true 249 | }) 250 | return &Value{data: collected} 251 | } 252 | 253 | /* 254 | Str (string and []string) 255 | */ 256 | 257 | // Str gets the value as a string, returns the optionalDefault 258 | // value or a system default object if the value is the wrong type. 259 | func (v *Value) Str(optionalDefault ...string) string { 260 | if s, ok := v.data.(string); ok { 261 | return s 262 | } 263 | if len(optionalDefault) == 1 { 264 | return optionalDefault[0] 265 | } 266 | return "" 267 | } 268 | 269 | // MustStr gets the value as a string. 270 | // 271 | // Panics if the object is not a string. 272 | func (v *Value) MustStr() string { 273 | return v.data.(string) 274 | } 275 | 276 | // StrSlice gets the value as a []string, returns the optionalDefault 277 | // value or nil if the value is not a []string. 278 | func (v *Value) StrSlice(optionalDefault ...[]string) []string { 279 | if s, ok := v.data.([]string); ok { 280 | return s 281 | } 282 | if len(optionalDefault) == 1 { 283 | return optionalDefault[0] 284 | } 285 | return nil 286 | } 287 | 288 | // MustStrSlice gets the value as a []string. 289 | // 290 | // Panics if the object is not a []string. 291 | func (v *Value) MustStrSlice() []string { 292 | return v.data.([]string) 293 | } 294 | 295 | // IsStr gets whether the object contained is a string or not. 296 | func (v *Value) IsStr() bool { 297 | _, ok := v.data.(string) 298 | return ok 299 | } 300 | 301 | // IsStrSlice gets whether the object contained is a []string or not. 302 | func (v *Value) IsStrSlice() bool { 303 | _, ok := v.data.([]string) 304 | return ok 305 | } 306 | 307 | // EachStr calls the specified callback for each object 308 | // in the []string. 309 | // 310 | // Panics if the object is the wrong type. 311 | func (v *Value) EachStr(callback func(int, string) bool) *Value { 312 | for index, val := range v.MustStrSlice() { 313 | carryon := callback(index, val) 314 | if !carryon { 315 | break 316 | } 317 | } 318 | return v 319 | } 320 | 321 | // WhereStr uses the specified decider function to select items 322 | // from the []string. The object contained in the result will contain 323 | // only the selected items. 324 | func (v *Value) WhereStr(decider func(int, string) bool) *Value { 325 | var selected []string 326 | v.EachStr(func(index int, val string) bool { 327 | shouldSelect := decider(index, val) 328 | if !shouldSelect { 329 | selected = append(selected, val) 330 | } 331 | return true 332 | }) 333 | return &Value{data: selected} 334 | } 335 | 336 | // GroupStr uses the specified grouper function to group the items 337 | // keyed by the return of the grouper. The object contained in the 338 | // result will contain a map[string][]string. 339 | func (v *Value) GroupStr(grouper func(int, string) string) *Value { 340 | groups := make(map[string][]string) 341 | v.EachStr(func(index int, val string) bool { 342 | group := grouper(index, val) 343 | if _, ok := groups[group]; !ok { 344 | groups[group] = make([]string, 0) 345 | } 346 | groups[group] = append(groups[group], val) 347 | return true 348 | }) 349 | return &Value{data: groups} 350 | } 351 | 352 | // ReplaceStr uses the specified function to replace each strings 353 | // by iterating each item. The data in the returned result will be a 354 | // []string containing the replaced items. 355 | func (v *Value) ReplaceStr(replacer func(int, string) string) *Value { 356 | arr := v.MustStrSlice() 357 | replaced := make([]string, len(arr)) 358 | v.EachStr(func(index int, val string) bool { 359 | replaced[index] = replacer(index, val) 360 | return true 361 | }) 362 | return &Value{data: replaced} 363 | } 364 | 365 | // CollectStr uses the specified collector function to collect a value 366 | // for each of the strings in the slice. The data returned will be a 367 | // []interface{}. 368 | func (v *Value) CollectStr(collector func(int, string) interface{}) *Value { 369 | arr := v.MustStrSlice() 370 | collected := make([]interface{}, len(arr)) 371 | v.EachStr(func(index int, val string) bool { 372 | collected[index] = collector(index, val) 373 | return true 374 | }) 375 | return &Value{data: collected} 376 | } 377 | 378 | /* 379 | Int (int and []int) 380 | */ 381 | 382 | // Int gets the value as a int, returns the optionalDefault 383 | // value or a system default object if the value is the wrong type. 384 | func (v *Value) Int(optionalDefault ...int) int { 385 | if s, ok := v.data.(int); ok { 386 | return s 387 | } 388 | if s, ok := v.data.(float64); ok { 389 | if float64(int(s)) == s { 390 | return int(s) 391 | } 392 | } 393 | if len(optionalDefault) == 1 { 394 | return optionalDefault[0] 395 | } 396 | return 0 397 | } 398 | 399 | // MustInt gets the value as a int. 400 | // 401 | // Panics if the object is not a int. 402 | func (v *Value) MustInt() int { 403 | if s, ok := v.data.(float64); ok { 404 | if float64(int(s)) == s { 405 | return int(s) 406 | } 407 | } 408 | return v.data.(int) 409 | } 410 | 411 | // IntSlice gets the value as a []int, returns the optionalDefault 412 | // value or nil if the value is not a []int. 413 | func (v *Value) IntSlice(optionalDefault ...[]int) []int { 414 | if s, ok := v.data.([]int); ok { 415 | return s 416 | } 417 | if len(optionalDefault) == 1 { 418 | return optionalDefault[0] 419 | } 420 | return nil 421 | } 422 | 423 | // MustIntSlice gets the value as a []int. 424 | // 425 | // Panics if the object is not a []int. 426 | func (v *Value) MustIntSlice() []int { 427 | return v.data.([]int) 428 | } 429 | 430 | // IsInt gets whether the object contained is a int or not. 431 | func (v *Value) IsInt() bool { 432 | _, ok := v.data.(int) 433 | return ok 434 | } 435 | 436 | // IsIntSlice gets whether the object contained is a []int or not. 437 | func (v *Value) IsIntSlice() bool { 438 | _, ok := v.data.([]int) 439 | return ok 440 | } 441 | 442 | // EachInt calls the specified callback for each object 443 | // in the []int. 444 | // 445 | // Panics if the object is the wrong type. 446 | func (v *Value) EachInt(callback func(int, int) bool) *Value { 447 | for index, val := range v.MustIntSlice() { 448 | carryon := callback(index, val) 449 | if !carryon { 450 | break 451 | } 452 | } 453 | return v 454 | } 455 | 456 | // WhereInt uses the specified decider function to select items 457 | // from the []int. The object contained in the result will contain 458 | // only the selected items. 459 | func (v *Value) WhereInt(decider func(int, int) bool) *Value { 460 | var selected []int 461 | v.EachInt(func(index int, val int) bool { 462 | shouldSelect := decider(index, val) 463 | if !shouldSelect { 464 | selected = append(selected, val) 465 | } 466 | return true 467 | }) 468 | return &Value{data: selected} 469 | } 470 | 471 | // GroupInt uses the specified grouper function to group the items 472 | // keyed by the return of the grouper. The object contained in the 473 | // result will contain a map[string][]int. 474 | func (v *Value) GroupInt(grouper func(int, int) string) *Value { 475 | groups := make(map[string][]int) 476 | v.EachInt(func(index int, val int) bool { 477 | group := grouper(index, val) 478 | if _, ok := groups[group]; !ok { 479 | groups[group] = make([]int, 0) 480 | } 481 | groups[group] = append(groups[group], val) 482 | return true 483 | }) 484 | return &Value{data: groups} 485 | } 486 | 487 | // ReplaceInt uses the specified function to replace each ints 488 | // by iterating each item. The data in the returned result will be a 489 | // []int containing the replaced items. 490 | func (v *Value) ReplaceInt(replacer func(int, int) int) *Value { 491 | arr := v.MustIntSlice() 492 | replaced := make([]int, len(arr)) 493 | v.EachInt(func(index int, val int) bool { 494 | replaced[index] = replacer(index, val) 495 | return true 496 | }) 497 | return &Value{data: replaced} 498 | } 499 | 500 | // CollectInt uses the specified collector function to collect a value 501 | // for each of the ints in the slice. The data returned will be a 502 | // []interface{}. 503 | func (v *Value) CollectInt(collector func(int, int) interface{}) *Value { 504 | arr := v.MustIntSlice() 505 | collected := make([]interface{}, len(arr)) 506 | v.EachInt(func(index int, val int) bool { 507 | collected[index] = collector(index, val) 508 | return true 509 | }) 510 | return &Value{data: collected} 511 | } 512 | 513 | /* 514 | Int8 (int8 and []int8) 515 | */ 516 | 517 | // Int8 gets the value as a int8, returns the optionalDefault 518 | // value or a system default object if the value is the wrong type. 519 | func (v *Value) Int8(optionalDefault ...int8) int8 { 520 | if s, ok := v.data.(int8); ok { 521 | return s 522 | } 523 | if len(optionalDefault) == 1 { 524 | return optionalDefault[0] 525 | } 526 | return 0 527 | } 528 | 529 | // MustInt8 gets the value as a int8. 530 | // 531 | // Panics if the object is not a int8. 532 | func (v *Value) MustInt8() int8 { 533 | return v.data.(int8) 534 | } 535 | 536 | // Int8Slice gets the value as a []int8, returns the optionalDefault 537 | // value or nil if the value is not a []int8. 538 | func (v *Value) Int8Slice(optionalDefault ...[]int8) []int8 { 539 | if s, ok := v.data.([]int8); ok { 540 | return s 541 | } 542 | if len(optionalDefault) == 1 { 543 | return optionalDefault[0] 544 | } 545 | return nil 546 | } 547 | 548 | // MustInt8Slice gets the value as a []int8. 549 | // 550 | // Panics if the object is not a []int8. 551 | func (v *Value) MustInt8Slice() []int8 { 552 | return v.data.([]int8) 553 | } 554 | 555 | // IsInt8 gets whether the object contained is a int8 or not. 556 | func (v *Value) IsInt8() bool { 557 | _, ok := v.data.(int8) 558 | return ok 559 | } 560 | 561 | // IsInt8Slice gets whether the object contained is a []int8 or not. 562 | func (v *Value) IsInt8Slice() bool { 563 | _, ok := v.data.([]int8) 564 | return ok 565 | } 566 | 567 | // EachInt8 calls the specified callback for each object 568 | // in the []int8. 569 | // 570 | // Panics if the object is the wrong type. 571 | func (v *Value) EachInt8(callback func(int, int8) bool) *Value { 572 | for index, val := range v.MustInt8Slice() { 573 | carryon := callback(index, val) 574 | if !carryon { 575 | break 576 | } 577 | } 578 | return v 579 | } 580 | 581 | // WhereInt8 uses the specified decider function to select items 582 | // from the []int8. The object contained in the result will contain 583 | // only the selected items. 584 | func (v *Value) WhereInt8(decider func(int, int8) bool) *Value { 585 | var selected []int8 586 | v.EachInt8(func(index int, val int8) bool { 587 | shouldSelect := decider(index, val) 588 | if !shouldSelect { 589 | selected = append(selected, val) 590 | } 591 | return true 592 | }) 593 | return &Value{data: selected} 594 | } 595 | 596 | // GroupInt8 uses the specified grouper function to group the items 597 | // keyed by the return of the grouper. The object contained in the 598 | // result will contain a map[string][]int8. 599 | func (v *Value) GroupInt8(grouper func(int, int8) string) *Value { 600 | groups := make(map[string][]int8) 601 | v.EachInt8(func(index int, val int8) bool { 602 | group := grouper(index, val) 603 | if _, ok := groups[group]; !ok { 604 | groups[group] = make([]int8, 0) 605 | } 606 | groups[group] = append(groups[group], val) 607 | return true 608 | }) 609 | return &Value{data: groups} 610 | } 611 | 612 | // ReplaceInt8 uses the specified function to replace each int8s 613 | // by iterating each item. The data in the returned result will be a 614 | // []int8 containing the replaced items. 615 | func (v *Value) ReplaceInt8(replacer func(int, int8) int8) *Value { 616 | arr := v.MustInt8Slice() 617 | replaced := make([]int8, len(arr)) 618 | v.EachInt8(func(index int, val int8) bool { 619 | replaced[index] = replacer(index, val) 620 | return true 621 | }) 622 | return &Value{data: replaced} 623 | } 624 | 625 | // CollectInt8 uses the specified collector function to collect a value 626 | // for each of the int8s in the slice. The data returned will be a 627 | // []interface{}. 628 | func (v *Value) CollectInt8(collector func(int, int8) interface{}) *Value { 629 | arr := v.MustInt8Slice() 630 | collected := make([]interface{}, len(arr)) 631 | v.EachInt8(func(index int, val int8) bool { 632 | collected[index] = collector(index, val) 633 | return true 634 | }) 635 | return &Value{data: collected} 636 | } 637 | 638 | /* 639 | Int16 (int16 and []int16) 640 | */ 641 | 642 | // Int16 gets the value as a int16, returns the optionalDefault 643 | // value or a system default object if the value is the wrong type. 644 | func (v *Value) Int16(optionalDefault ...int16) int16 { 645 | if s, ok := v.data.(int16); ok { 646 | return s 647 | } 648 | if len(optionalDefault) == 1 { 649 | return optionalDefault[0] 650 | } 651 | return 0 652 | } 653 | 654 | // MustInt16 gets the value as a int16. 655 | // 656 | // Panics if the object is not a int16. 657 | func (v *Value) MustInt16() int16 { 658 | return v.data.(int16) 659 | } 660 | 661 | // Int16Slice gets the value as a []int16, returns the optionalDefault 662 | // value or nil if the value is not a []int16. 663 | func (v *Value) Int16Slice(optionalDefault ...[]int16) []int16 { 664 | if s, ok := v.data.([]int16); ok { 665 | return s 666 | } 667 | if len(optionalDefault) == 1 { 668 | return optionalDefault[0] 669 | } 670 | return nil 671 | } 672 | 673 | // MustInt16Slice gets the value as a []int16. 674 | // 675 | // Panics if the object is not a []int16. 676 | func (v *Value) MustInt16Slice() []int16 { 677 | return v.data.([]int16) 678 | } 679 | 680 | // IsInt16 gets whether the object contained is a int16 or not. 681 | func (v *Value) IsInt16() bool { 682 | _, ok := v.data.(int16) 683 | return ok 684 | } 685 | 686 | // IsInt16Slice gets whether the object contained is a []int16 or not. 687 | func (v *Value) IsInt16Slice() bool { 688 | _, ok := v.data.([]int16) 689 | return ok 690 | } 691 | 692 | // EachInt16 calls the specified callback for each object 693 | // in the []int16. 694 | // 695 | // Panics if the object is the wrong type. 696 | func (v *Value) EachInt16(callback func(int, int16) bool) *Value { 697 | for index, val := range v.MustInt16Slice() { 698 | carryon := callback(index, val) 699 | if !carryon { 700 | break 701 | } 702 | } 703 | return v 704 | } 705 | 706 | // WhereInt16 uses the specified decider function to select items 707 | // from the []int16. The object contained in the result will contain 708 | // only the selected items. 709 | func (v *Value) WhereInt16(decider func(int, int16) bool) *Value { 710 | var selected []int16 711 | v.EachInt16(func(index int, val int16) bool { 712 | shouldSelect := decider(index, val) 713 | if !shouldSelect { 714 | selected = append(selected, val) 715 | } 716 | return true 717 | }) 718 | return &Value{data: selected} 719 | } 720 | 721 | // GroupInt16 uses the specified grouper function to group the items 722 | // keyed by the return of the grouper. The object contained in the 723 | // result will contain a map[string][]int16. 724 | func (v *Value) GroupInt16(grouper func(int, int16) string) *Value { 725 | groups := make(map[string][]int16) 726 | v.EachInt16(func(index int, val int16) bool { 727 | group := grouper(index, val) 728 | if _, ok := groups[group]; !ok { 729 | groups[group] = make([]int16, 0) 730 | } 731 | groups[group] = append(groups[group], val) 732 | return true 733 | }) 734 | return &Value{data: groups} 735 | } 736 | 737 | // ReplaceInt16 uses the specified function to replace each int16s 738 | // by iterating each item. The data in the returned result will be a 739 | // []int16 containing the replaced items. 740 | func (v *Value) ReplaceInt16(replacer func(int, int16) int16) *Value { 741 | arr := v.MustInt16Slice() 742 | replaced := make([]int16, len(arr)) 743 | v.EachInt16(func(index int, val int16) bool { 744 | replaced[index] = replacer(index, val) 745 | return true 746 | }) 747 | return &Value{data: replaced} 748 | } 749 | 750 | // CollectInt16 uses the specified collector function to collect a value 751 | // for each of the int16s in the slice. The data returned will be a 752 | // []interface{}. 753 | func (v *Value) CollectInt16(collector func(int, int16) interface{}) *Value { 754 | arr := v.MustInt16Slice() 755 | collected := make([]interface{}, len(arr)) 756 | v.EachInt16(func(index int, val int16) bool { 757 | collected[index] = collector(index, val) 758 | return true 759 | }) 760 | return &Value{data: collected} 761 | } 762 | 763 | /* 764 | Int32 (int32 and []int32) 765 | */ 766 | 767 | // Int32 gets the value as a int32, returns the optionalDefault 768 | // value or a system default object if the value is the wrong type. 769 | func (v *Value) Int32(optionalDefault ...int32) int32 { 770 | if s, ok := v.data.(int32); ok { 771 | return s 772 | } 773 | if len(optionalDefault) == 1 { 774 | return optionalDefault[0] 775 | } 776 | return 0 777 | } 778 | 779 | // MustInt32 gets the value as a int32. 780 | // 781 | // Panics if the object is not a int32. 782 | func (v *Value) MustInt32() int32 { 783 | return v.data.(int32) 784 | } 785 | 786 | // Int32Slice gets the value as a []int32, returns the optionalDefault 787 | // value or nil if the value is not a []int32. 788 | func (v *Value) Int32Slice(optionalDefault ...[]int32) []int32 { 789 | if s, ok := v.data.([]int32); ok { 790 | return s 791 | } 792 | if len(optionalDefault) == 1 { 793 | return optionalDefault[0] 794 | } 795 | return nil 796 | } 797 | 798 | // MustInt32Slice gets the value as a []int32. 799 | // 800 | // Panics if the object is not a []int32. 801 | func (v *Value) MustInt32Slice() []int32 { 802 | return v.data.([]int32) 803 | } 804 | 805 | // IsInt32 gets whether the object contained is a int32 or not. 806 | func (v *Value) IsInt32() bool { 807 | _, ok := v.data.(int32) 808 | return ok 809 | } 810 | 811 | // IsInt32Slice gets whether the object contained is a []int32 or not. 812 | func (v *Value) IsInt32Slice() bool { 813 | _, ok := v.data.([]int32) 814 | return ok 815 | } 816 | 817 | // EachInt32 calls the specified callback for each object 818 | // in the []int32. 819 | // 820 | // Panics if the object is the wrong type. 821 | func (v *Value) EachInt32(callback func(int, int32) bool) *Value { 822 | for index, val := range v.MustInt32Slice() { 823 | carryon := callback(index, val) 824 | if !carryon { 825 | break 826 | } 827 | } 828 | return v 829 | } 830 | 831 | // WhereInt32 uses the specified decider function to select items 832 | // from the []int32. The object contained in the result will contain 833 | // only the selected items. 834 | func (v *Value) WhereInt32(decider func(int, int32) bool) *Value { 835 | var selected []int32 836 | v.EachInt32(func(index int, val int32) bool { 837 | shouldSelect := decider(index, val) 838 | if !shouldSelect { 839 | selected = append(selected, val) 840 | } 841 | return true 842 | }) 843 | return &Value{data: selected} 844 | } 845 | 846 | // GroupInt32 uses the specified grouper function to group the items 847 | // keyed by the return of the grouper. The object contained in the 848 | // result will contain a map[string][]int32. 849 | func (v *Value) GroupInt32(grouper func(int, int32) string) *Value { 850 | groups := make(map[string][]int32) 851 | v.EachInt32(func(index int, val int32) bool { 852 | group := grouper(index, val) 853 | if _, ok := groups[group]; !ok { 854 | groups[group] = make([]int32, 0) 855 | } 856 | groups[group] = append(groups[group], val) 857 | return true 858 | }) 859 | return &Value{data: groups} 860 | } 861 | 862 | // ReplaceInt32 uses the specified function to replace each int32s 863 | // by iterating each item. The data in the returned result will be a 864 | // []int32 containing the replaced items. 865 | func (v *Value) ReplaceInt32(replacer func(int, int32) int32) *Value { 866 | arr := v.MustInt32Slice() 867 | replaced := make([]int32, len(arr)) 868 | v.EachInt32(func(index int, val int32) bool { 869 | replaced[index] = replacer(index, val) 870 | return true 871 | }) 872 | return &Value{data: replaced} 873 | } 874 | 875 | // CollectInt32 uses the specified collector function to collect a value 876 | // for each of the int32s in the slice. The data returned will be a 877 | // []interface{}. 878 | func (v *Value) CollectInt32(collector func(int, int32) interface{}) *Value { 879 | arr := v.MustInt32Slice() 880 | collected := make([]interface{}, len(arr)) 881 | v.EachInt32(func(index int, val int32) bool { 882 | collected[index] = collector(index, val) 883 | return true 884 | }) 885 | return &Value{data: collected} 886 | } 887 | 888 | /* 889 | Int64 (int64 and []int64) 890 | */ 891 | 892 | // Int64 gets the value as a int64, returns the optionalDefault 893 | // value or a system default object if the value is the wrong type. 894 | func (v *Value) Int64(optionalDefault ...int64) int64 { 895 | if s, ok := v.data.(int64); ok { 896 | return s 897 | } 898 | if len(optionalDefault) == 1 { 899 | return optionalDefault[0] 900 | } 901 | return 0 902 | } 903 | 904 | // MustInt64 gets the value as a int64. 905 | // 906 | // Panics if the object is not a int64. 907 | func (v *Value) MustInt64() int64 { 908 | return v.data.(int64) 909 | } 910 | 911 | // Int64Slice gets the value as a []int64, returns the optionalDefault 912 | // value or nil if the value is not a []int64. 913 | func (v *Value) Int64Slice(optionalDefault ...[]int64) []int64 { 914 | if s, ok := v.data.([]int64); ok { 915 | return s 916 | } 917 | if len(optionalDefault) == 1 { 918 | return optionalDefault[0] 919 | } 920 | return nil 921 | } 922 | 923 | // MustInt64Slice gets the value as a []int64. 924 | // 925 | // Panics if the object is not a []int64. 926 | func (v *Value) MustInt64Slice() []int64 { 927 | return v.data.([]int64) 928 | } 929 | 930 | // IsInt64 gets whether the object contained is a int64 or not. 931 | func (v *Value) IsInt64() bool { 932 | _, ok := v.data.(int64) 933 | return ok 934 | } 935 | 936 | // IsInt64Slice gets whether the object contained is a []int64 or not. 937 | func (v *Value) IsInt64Slice() bool { 938 | _, ok := v.data.([]int64) 939 | return ok 940 | } 941 | 942 | // EachInt64 calls the specified callback for each object 943 | // in the []int64. 944 | // 945 | // Panics if the object is the wrong type. 946 | func (v *Value) EachInt64(callback func(int, int64) bool) *Value { 947 | for index, val := range v.MustInt64Slice() { 948 | carryon := callback(index, val) 949 | if !carryon { 950 | break 951 | } 952 | } 953 | return v 954 | } 955 | 956 | // WhereInt64 uses the specified decider function to select items 957 | // from the []int64. The object contained in the result will contain 958 | // only the selected items. 959 | func (v *Value) WhereInt64(decider func(int, int64) bool) *Value { 960 | var selected []int64 961 | v.EachInt64(func(index int, val int64) bool { 962 | shouldSelect := decider(index, val) 963 | if !shouldSelect { 964 | selected = append(selected, val) 965 | } 966 | return true 967 | }) 968 | return &Value{data: selected} 969 | } 970 | 971 | // GroupInt64 uses the specified grouper function to group the items 972 | // keyed by the return of the grouper. The object contained in the 973 | // result will contain a map[string][]int64. 974 | func (v *Value) GroupInt64(grouper func(int, int64) string) *Value { 975 | groups := make(map[string][]int64) 976 | v.EachInt64(func(index int, val int64) bool { 977 | group := grouper(index, val) 978 | if _, ok := groups[group]; !ok { 979 | groups[group] = make([]int64, 0) 980 | } 981 | groups[group] = append(groups[group], val) 982 | return true 983 | }) 984 | return &Value{data: groups} 985 | } 986 | 987 | // ReplaceInt64 uses the specified function to replace each int64s 988 | // by iterating each item. The data in the returned result will be a 989 | // []int64 containing the replaced items. 990 | func (v *Value) ReplaceInt64(replacer func(int, int64) int64) *Value { 991 | arr := v.MustInt64Slice() 992 | replaced := make([]int64, len(arr)) 993 | v.EachInt64(func(index int, val int64) bool { 994 | replaced[index] = replacer(index, val) 995 | return true 996 | }) 997 | return &Value{data: replaced} 998 | } 999 | 1000 | // CollectInt64 uses the specified collector function to collect a value 1001 | // for each of the int64s in the slice. The data returned will be a 1002 | // []interface{}. 1003 | func (v *Value) CollectInt64(collector func(int, int64) interface{}) *Value { 1004 | arr := v.MustInt64Slice() 1005 | collected := make([]interface{}, len(arr)) 1006 | v.EachInt64(func(index int, val int64) bool { 1007 | collected[index] = collector(index, val) 1008 | return true 1009 | }) 1010 | return &Value{data: collected} 1011 | } 1012 | 1013 | /* 1014 | Uint (uint and []uint) 1015 | */ 1016 | 1017 | // Uint gets the value as a uint, returns the optionalDefault 1018 | // value or a system default object if the value is the wrong type. 1019 | func (v *Value) Uint(optionalDefault ...uint) uint { 1020 | if s, ok := v.data.(uint); ok { 1021 | return s 1022 | } 1023 | if len(optionalDefault) == 1 { 1024 | return optionalDefault[0] 1025 | } 1026 | return 0 1027 | } 1028 | 1029 | // MustUint gets the value as a uint. 1030 | // 1031 | // Panics if the object is not a uint. 1032 | func (v *Value) MustUint() uint { 1033 | return v.data.(uint) 1034 | } 1035 | 1036 | // UintSlice gets the value as a []uint, returns the optionalDefault 1037 | // value or nil if the value is not a []uint. 1038 | func (v *Value) UintSlice(optionalDefault ...[]uint) []uint { 1039 | if s, ok := v.data.([]uint); ok { 1040 | return s 1041 | } 1042 | if len(optionalDefault) == 1 { 1043 | return optionalDefault[0] 1044 | } 1045 | return nil 1046 | } 1047 | 1048 | // MustUintSlice gets the value as a []uint. 1049 | // 1050 | // Panics if the object is not a []uint. 1051 | func (v *Value) MustUintSlice() []uint { 1052 | return v.data.([]uint) 1053 | } 1054 | 1055 | // IsUint gets whether the object contained is a uint or not. 1056 | func (v *Value) IsUint() bool { 1057 | _, ok := v.data.(uint) 1058 | return ok 1059 | } 1060 | 1061 | // IsUintSlice gets whether the object contained is a []uint or not. 1062 | func (v *Value) IsUintSlice() bool { 1063 | _, ok := v.data.([]uint) 1064 | return ok 1065 | } 1066 | 1067 | // EachUint calls the specified callback for each object 1068 | // in the []uint. 1069 | // 1070 | // Panics if the object is the wrong type. 1071 | func (v *Value) EachUint(callback func(int, uint) bool) *Value { 1072 | for index, val := range v.MustUintSlice() { 1073 | carryon := callback(index, val) 1074 | if !carryon { 1075 | break 1076 | } 1077 | } 1078 | return v 1079 | } 1080 | 1081 | // WhereUint uses the specified decider function to select items 1082 | // from the []uint. The object contained in the result will contain 1083 | // only the selected items. 1084 | func (v *Value) WhereUint(decider func(int, uint) bool) *Value { 1085 | var selected []uint 1086 | v.EachUint(func(index int, val uint) bool { 1087 | shouldSelect := decider(index, val) 1088 | if !shouldSelect { 1089 | selected = append(selected, val) 1090 | } 1091 | return true 1092 | }) 1093 | return &Value{data: selected} 1094 | } 1095 | 1096 | // GroupUint uses the specified grouper function to group the items 1097 | // keyed by the return of the grouper. The object contained in the 1098 | // result will contain a map[string][]uint. 1099 | func (v *Value) GroupUint(grouper func(int, uint) string) *Value { 1100 | groups := make(map[string][]uint) 1101 | v.EachUint(func(index int, val uint) bool { 1102 | group := grouper(index, val) 1103 | if _, ok := groups[group]; !ok { 1104 | groups[group] = make([]uint, 0) 1105 | } 1106 | groups[group] = append(groups[group], val) 1107 | return true 1108 | }) 1109 | return &Value{data: groups} 1110 | } 1111 | 1112 | // ReplaceUint uses the specified function to replace each uints 1113 | // by iterating each item. The data in the returned result will be a 1114 | // []uint containing the replaced items. 1115 | func (v *Value) ReplaceUint(replacer func(int, uint) uint) *Value { 1116 | arr := v.MustUintSlice() 1117 | replaced := make([]uint, len(arr)) 1118 | v.EachUint(func(index int, val uint) bool { 1119 | replaced[index] = replacer(index, val) 1120 | return true 1121 | }) 1122 | return &Value{data: replaced} 1123 | } 1124 | 1125 | // CollectUint uses the specified collector function to collect a value 1126 | // for each of the uints in the slice. The data returned will be a 1127 | // []interface{}. 1128 | func (v *Value) CollectUint(collector func(int, uint) interface{}) *Value { 1129 | arr := v.MustUintSlice() 1130 | collected := make([]interface{}, len(arr)) 1131 | v.EachUint(func(index int, val uint) bool { 1132 | collected[index] = collector(index, val) 1133 | return true 1134 | }) 1135 | return &Value{data: collected} 1136 | } 1137 | 1138 | /* 1139 | Uint8 (uint8 and []uint8) 1140 | */ 1141 | 1142 | // Uint8 gets the value as a uint8, returns the optionalDefault 1143 | // value or a system default object if the value is the wrong type. 1144 | func (v *Value) Uint8(optionalDefault ...uint8) uint8 { 1145 | if s, ok := v.data.(uint8); ok { 1146 | return s 1147 | } 1148 | if len(optionalDefault) == 1 { 1149 | return optionalDefault[0] 1150 | } 1151 | return 0 1152 | } 1153 | 1154 | // MustUint8 gets the value as a uint8. 1155 | // 1156 | // Panics if the object is not a uint8. 1157 | func (v *Value) MustUint8() uint8 { 1158 | return v.data.(uint8) 1159 | } 1160 | 1161 | // Uint8Slice gets the value as a []uint8, returns the optionalDefault 1162 | // value or nil if the value is not a []uint8. 1163 | func (v *Value) Uint8Slice(optionalDefault ...[]uint8) []uint8 { 1164 | if s, ok := v.data.([]uint8); ok { 1165 | return s 1166 | } 1167 | if len(optionalDefault) == 1 { 1168 | return optionalDefault[0] 1169 | } 1170 | return nil 1171 | } 1172 | 1173 | // MustUint8Slice gets the value as a []uint8. 1174 | // 1175 | // Panics if the object is not a []uint8. 1176 | func (v *Value) MustUint8Slice() []uint8 { 1177 | return v.data.([]uint8) 1178 | } 1179 | 1180 | // IsUint8 gets whether the object contained is a uint8 or not. 1181 | func (v *Value) IsUint8() bool { 1182 | _, ok := v.data.(uint8) 1183 | return ok 1184 | } 1185 | 1186 | // IsUint8Slice gets whether the object contained is a []uint8 or not. 1187 | func (v *Value) IsUint8Slice() bool { 1188 | _, ok := v.data.([]uint8) 1189 | return ok 1190 | } 1191 | 1192 | // EachUint8 calls the specified callback for each object 1193 | // in the []uint8. 1194 | // 1195 | // Panics if the object is the wrong type. 1196 | func (v *Value) EachUint8(callback func(int, uint8) bool) *Value { 1197 | for index, val := range v.MustUint8Slice() { 1198 | carryon := callback(index, val) 1199 | if !carryon { 1200 | break 1201 | } 1202 | } 1203 | return v 1204 | } 1205 | 1206 | // WhereUint8 uses the specified decider function to select items 1207 | // from the []uint8. The object contained in the result will contain 1208 | // only the selected items. 1209 | func (v *Value) WhereUint8(decider func(int, uint8) bool) *Value { 1210 | var selected []uint8 1211 | v.EachUint8(func(index int, val uint8) bool { 1212 | shouldSelect := decider(index, val) 1213 | if !shouldSelect { 1214 | selected = append(selected, val) 1215 | } 1216 | return true 1217 | }) 1218 | return &Value{data: selected} 1219 | } 1220 | 1221 | // GroupUint8 uses the specified grouper function to group the items 1222 | // keyed by the return of the grouper. The object contained in the 1223 | // result will contain a map[string][]uint8. 1224 | func (v *Value) GroupUint8(grouper func(int, uint8) string) *Value { 1225 | groups := make(map[string][]uint8) 1226 | v.EachUint8(func(index int, val uint8) bool { 1227 | group := grouper(index, val) 1228 | if _, ok := groups[group]; !ok { 1229 | groups[group] = make([]uint8, 0) 1230 | } 1231 | groups[group] = append(groups[group], val) 1232 | return true 1233 | }) 1234 | return &Value{data: groups} 1235 | } 1236 | 1237 | // ReplaceUint8 uses the specified function to replace each uint8s 1238 | // by iterating each item. The data in the returned result will be a 1239 | // []uint8 containing the replaced items. 1240 | func (v *Value) ReplaceUint8(replacer func(int, uint8) uint8) *Value { 1241 | arr := v.MustUint8Slice() 1242 | replaced := make([]uint8, len(arr)) 1243 | v.EachUint8(func(index int, val uint8) bool { 1244 | replaced[index] = replacer(index, val) 1245 | return true 1246 | }) 1247 | return &Value{data: replaced} 1248 | } 1249 | 1250 | // CollectUint8 uses the specified collector function to collect a value 1251 | // for each of the uint8s in the slice. The data returned will be a 1252 | // []interface{}. 1253 | func (v *Value) CollectUint8(collector func(int, uint8) interface{}) *Value { 1254 | arr := v.MustUint8Slice() 1255 | collected := make([]interface{}, len(arr)) 1256 | v.EachUint8(func(index int, val uint8) bool { 1257 | collected[index] = collector(index, val) 1258 | return true 1259 | }) 1260 | return &Value{data: collected} 1261 | } 1262 | 1263 | /* 1264 | Uint16 (uint16 and []uint16) 1265 | */ 1266 | 1267 | // Uint16 gets the value as a uint16, returns the optionalDefault 1268 | // value or a system default object if the value is the wrong type. 1269 | func (v *Value) Uint16(optionalDefault ...uint16) uint16 { 1270 | if s, ok := v.data.(uint16); ok { 1271 | return s 1272 | } 1273 | if len(optionalDefault) == 1 { 1274 | return optionalDefault[0] 1275 | } 1276 | return 0 1277 | } 1278 | 1279 | // MustUint16 gets the value as a uint16. 1280 | // 1281 | // Panics if the object is not a uint16. 1282 | func (v *Value) MustUint16() uint16 { 1283 | return v.data.(uint16) 1284 | } 1285 | 1286 | // Uint16Slice gets the value as a []uint16, returns the optionalDefault 1287 | // value or nil if the value is not a []uint16. 1288 | func (v *Value) Uint16Slice(optionalDefault ...[]uint16) []uint16 { 1289 | if s, ok := v.data.([]uint16); ok { 1290 | return s 1291 | } 1292 | if len(optionalDefault) == 1 { 1293 | return optionalDefault[0] 1294 | } 1295 | return nil 1296 | } 1297 | 1298 | // MustUint16Slice gets the value as a []uint16. 1299 | // 1300 | // Panics if the object is not a []uint16. 1301 | func (v *Value) MustUint16Slice() []uint16 { 1302 | return v.data.([]uint16) 1303 | } 1304 | 1305 | // IsUint16 gets whether the object contained is a uint16 or not. 1306 | func (v *Value) IsUint16() bool { 1307 | _, ok := v.data.(uint16) 1308 | return ok 1309 | } 1310 | 1311 | // IsUint16Slice gets whether the object contained is a []uint16 or not. 1312 | func (v *Value) IsUint16Slice() bool { 1313 | _, ok := v.data.([]uint16) 1314 | return ok 1315 | } 1316 | 1317 | // EachUint16 calls the specified callback for each object 1318 | // in the []uint16. 1319 | // 1320 | // Panics if the object is the wrong type. 1321 | func (v *Value) EachUint16(callback func(int, uint16) bool) *Value { 1322 | for index, val := range v.MustUint16Slice() { 1323 | carryon := callback(index, val) 1324 | if !carryon { 1325 | break 1326 | } 1327 | } 1328 | return v 1329 | } 1330 | 1331 | // WhereUint16 uses the specified decider function to select items 1332 | // from the []uint16. The object contained in the result will contain 1333 | // only the selected items. 1334 | func (v *Value) WhereUint16(decider func(int, uint16) bool) *Value { 1335 | var selected []uint16 1336 | v.EachUint16(func(index int, val uint16) bool { 1337 | shouldSelect := decider(index, val) 1338 | if !shouldSelect { 1339 | selected = append(selected, val) 1340 | } 1341 | return true 1342 | }) 1343 | return &Value{data: selected} 1344 | } 1345 | 1346 | // GroupUint16 uses the specified grouper function to group the items 1347 | // keyed by the return of the grouper. The object contained in the 1348 | // result will contain a map[string][]uint16. 1349 | func (v *Value) GroupUint16(grouper func(int, uint16) string) *Value { 1350 | groups := make(map[string][]uint16) 1351 | v.EachUint16(func(index int, val uint16) bool { 1352 | group := grouper(index, val) 1353 | if _, ok := groups[group]; !ok { 1354 | groups[group] = make([]uint16, 0) 1355 | } 1356 | groups[group] = append(groups[group], val) 1357 | return true 1358 | }) 1359 | return &Value{data: groups} 1360 | } 1361 | 1362 | // ReplaceUint16 uses the specified function to replace each uint16s 1363 | // by iterating each item. The data in the returned result will be a 1364 | // []uint16 containing the replaced items. 1365 | func (v *Value) ReplaceUint16(replacer func(int, uint16) uint16) *Value { 1366 | arr := v.MustUint16Slice() 1367 | replaced := make([]uint16, len(arr)) 1368 | v.EachUint16(func(index int, val uint16) bool { 1369 | replaced[index] = replacer(index, val) 1370 | return true 1371 | }) 1372 | return &Value{data: replaced} 1373 | } 1374 | 1375 | // CollectUint16 uses the specified collector function to collect a value 1376 | // for each of the uint16s in the slice. The data returned will be a 1377 | // []interface{}. 1378 | func (v *Value) CollectUint16(collector func(int, uint16) interface{}) *Value { 1379 | arr := v.MustUint16Slice() 1380 | collected := make([]interface{}, len(arr)) 1381 | v.EachUint16(func(index int, val uint16) bool { 1382 | collected[index] = collector(index, val) 1383 | return true 1384 | }) 1385 | return &Value{data: collected} 1386 | } 1387 | 1388 | /* 1389 | Uint32 (uint32 and []uint32) 1390 | */ 1391 | 1392 | // Uint32 gets the value as a uint32, returns the optionalDefault 1393 | // value or a system default object if the value is the wrong type. 1394 | func (v *Value) Uint32(optionalDefault ...uint32) uint32 { 1395 | if s, ok := v.data.(uint32); ok { 1396 | return s 1397 | } 1398 | if len(optionalDefault) == 1 { 1399 | return optionalDefault[0] 1400 | } 1401 | return 0 1402 | } 1403 | 1404 | // MustUint32 gets the value as a uint32. 1405 | // 1406 | // Panics if the object is not a uint32. 1407 | func (v *Value) MustUint32() uint32 { 1408 | return v.data.(uint32) 1409 | } 1410 | 1411 | // Uint32Slice gets the value as a []uint32, returns the optionalDefault 1412 | // value or nil if the value is not a []uint32. 1413 | func (v *Value) Uint32Slice(optionalDefault ...[]uint32) []uint32 { 1414 | if s, ok := v.data.([]uint32); ok { 1415 | return s 1416 | } 1417 | if len(optionalDefault) == 1 { 1418 | return optionalDefault[0] 1419 | } 1420 | return nil 1421 | } 1422 | 1423 | // MustUint32Slice gets the value as a []uint32. 1424 | // 1425 | // Panics if the object is not a []uint32. 1426 | func (v *Value) MustUint32Slice() []uint32 { 1427 | return v.data.([]uint32) 1428 | } 1429 | 1430 | // IsUint32 gets whether the object contained is a uint32 or not. 1431 | func (v *Value) IsUint32() bool { 1432 | _, ok := v.data.(uint32) 1433 | return ok 1434 | } 1435 | 1436 | // IsUint32Slice gets whether the object contained is a []uint32 or not. 1437 | func (v *Value) IsUint32Slice() bool { 1438 | _, ok := v.data.([]uint32) 1439 | return ok 1440 | } 1441 | 1442 | // EachUint32 calls the specified callback for each object 1443 | // in the []uint32. 1444 | // 1445 | // Panics if the object is the wrong type. 1446 | func (v *Value) EachUint32(callback func(int, uint32) bool) *Value { 1447 | for index, val := range v.MustUint32Slice() { 1448 | carryon := callback(index, val) 1449 | if !carryon { 1450 | break 1451 | } 1452 | } 1453 | return v 1454 | } 1455 | 1456 | // WhereUint32 uses the specified decider function to select items 1457 | // from the []uint32. The object contained in the result will contain 1458 | // only the selected items. 1459 | func (v *Value) WhereUint32(decider func(int, uint32) bool) *Value { 1460 | var selected []uint32 1461 | v.EachUint32(func(index int, val uint32) bool { 1462 | shouldSelect := decider(index, val) 1463 | if !shouldSelect { 1464 | selected = append(selected, val) 1465 | } 1466 | return true 1467 | }) 1468 | return &Value{data: selected} 1469 | } 1470 | 1471 | // GroupUint32 uses the specified grouper function to group the items 1472 | // keyed by the return of the grouper. The object contained in the 1473 | // result will contain a map[string][]uint32. 1474 | func (v *Value) GroupUint32(grouper func(int, uint32) string) *Value { 1475 | groups := make(map[string][]uint32) 1476 | v.EachUint32(func(index int, val uint32) bool { 1477 | group := grouper(index, val) 1478 | if _, ok := groups[group]; !ok { 1479 | groups[group] = make([]uint32, 0) 1480 | } 1481 | groups[group] = append(groups[group], val) 1482 | return true 1483 | }) 1484 | return &Value{data: groups} 1485 | } 1486 | 1487 | // ReplaceUint32 uses the specified function to replace each uint32s 1488 | // by iterating each item. The data in the returned result will be a 1489 | // []uint32 containing the replaced items. 1490 | func (v *Value) ReplaceUint32(replacer func(int, uint32) uint32) *Value { 1491 | arr := v.MustUint32Slice() 1492 | replaced := make([]uint32, len(arr)) 1493 | v.EachUint32(func(index int, val uint32) bool { 1494 | replaced[index] = replacer(index, val) 1495 | return true 1496 | }) 1497 | return &Value{data: replaced} 1498 | } 1499 | 1500 | // CollectUint32 uses the specified collector function to collect a value 1501 | // for each of the uint32s in the slice. The data returned will be a 1502 | // []interface{}. 1503 | func (v *Value) CollectUint32(collector func(int, uint32) interface{}) *Value { 1504 | arr := v.MustUint32Slice() 1505 | collected := make([]interface{}, len(arr)) 1506 | v.EachUint32(func(index int, val uint32) bool { 1507 | collected[index] = collector(index, val) 1508 | return true 1509 | }) 1510 | return &Value{data: collected} 1511 | } 1512 | 1513 | /* 1514 | Uint64 (uint64 and []uint64) 1515 | */ 1516 | 1517 | // Uint64 gets the value as a uint64, returns the optionalDefault 1518 | // value or a system default object if the value is the wrong type. 1519 | func (v *Value) Uint64(optionalDefault ...uint64) uint64 { 1520 | if s, ok := v.data.(uint64); ok { 1521 | return s 1522 | } 1523 | if len(optionalDefault) == 1 { 1524 | return optionalDefault[0] 1525 | } 1526 | return 0 1527 | } 1528 | 1529 | // MustUint64 gets the value as a uint64. 1530 | // 1531 | // Panics if the object is not a uint64. 1532 | func (v *Value) MustUint64() uint64 { 1533 | return v.data.(uint64) 1534 | } 1535 | 1536 | // Uint64Slice gets the value as a []uint64, returns the optionalDefault 1537 | // value or nil if the value is not a []uint64. 1538 | func (v *Value) Uint64Slice(optionalDefault ...[]uint64) []uint64 { 1539 | if s, ok := v.data.([]uint64); ok { 1540 | return s 1541 | } 1542 | if len(optionalDefault) == 1 { 1543 | return optionalDefault[0] 1544 | } 1545 | return nil 1546 | } 1547 | 1548 | // MustUint64Slice gets the value as a []uint64. 1549 | // 1550 | // Panics if the object is not a []uint64. 1551 | func (v *Value) MustUint64Slice() []uint64 { 1552 | return v.data.([]uint64) 1553 | } 1554 | 1555 | // IsUint64 gets whether the object contained is a uint64 or not. 1556 | func (v *Value) IsUint64() bool { 1557 | _, ok := v.data.(uint64) 1558 | return ok 1559 | } 1560 | 1561 | // IsUint64Slice gets whether the object contained is a []uint64 or not. 1562 | func (v *Value) IsUint64Slice() bool { 1563 | _, ok := v.data.([]uint64) 1564 | return ok 1565 | } 1566 | 1567 | // EachUint64 calls the specified callback for each object 1568 | // in the []uint64. 1569 | // 1570 | // Panics if the object is the wrong type. 1571 | func (v *Value) EachUint64(callback func(int, uint64) bool) *Value { 1572 | for index, val := range v.MustUint64Slice() { 1573 | carryon := callback(index, val) 1574 | if !carryon { 1575 | break 1576 | } 1577 | } 1578 | return v 1579 | } 1580 | 1581 | // WhereUint64 uses the specified decider function to select items 1582 | // from the []uint64. The object contained in the result will contain 1583 | // only the selected items. 1584 | func (v *Value) WhereUint64(decider func(int, uint64) bool) *Value { 1585 | var selected []uint64 1586 | v.EachUint64(func(index int, val uint64) bool { 1587 | shouldSelect := decider(index, val) 1588 | if !shouldSelect { 1589 | selected = append(selected, val) 1590 | } 1591 | return true 1592 | }) 1593 | return &Value{data: selected} 1594 | } 1595 | 1596 | // GroupUint64 uses the specified grouper function to group the items 1597 | // keyed by the return of the grouper. The object contained in the 1598 | // result will contain a map[string][]uint64. 1599 | func (v *Value) GroupUint64(grouper func(int, uint64) string) *Value { 1600 | groups := make(map[string][]uint64) 1601 | v.EachUint64(func(index int, val uint64) bool { 1602 | group := grouper(index, val) 1603 | if _, ok := groups[group]; !ok { 1604 | groups[group] = make([]uint64, 0) 1605 | } 1606 | groups[group] = append(groups[group], val) 1607 | return true 1608 | }) 1609 | return &Value{data: groups} 1610 | } 1611 | 1612 | // ReplaceUint64 uses the specified function to replace each uint64s 1613 | // by iterating each item. The data in the returned result will be a 1614 | // []uint64 containing the replaced items. 1615 | func (v *Value) ReplaceUint64(replacer func(int, uint64) uint64) *Value { 1616 | arr := v.MustUint64Slice() 1617 | replaced := make([]uint64, len(arr)) 1618 | v.EachUint64(func(index int, val uint64) bool { 1619 | replaced[index] = replacer(index, val) 1620 | return true 1621 | }) 1622 | return &Value{data: replaced} 1623 | } 1624 | 1625 | // CollectUint64 uses the specified collector function to collect a value 1626 | // for each of the uint64s in the slice. The data returned will be a 1627 | // []interface{}. 1628 | func (v *Value) CollectUint64(collector func(int, uint64) interface{}) *Value { 1629 | arr := v.MustUint64Slice() 1630 | collected := make([]interface{}, len(arr)) 1631 | v.EachUint64(func(index int, val uint64) bool { 1632 | collected[index] = collector(index, val) 1633 | return true 1634 | }) 1635 | return &Value{data: collected} 1636 | } 1637 | 1638 | /* 1639 | Uintptr (uintptr and []uintptr) 1640 | */ 1641 | 1642 | // Uintptr gets the value as a uintptr, returns the optionalDefault 1643 | // value or a system default object if the value is the wrong type. 1644 | func (v *Value) Uintptr(optionalDefault ...uintptr) uintptr { 1645 | if s, ok := v.data.(uintptr); ok { 1646 | return s 1647 | } 1648 | if len(optionalDefault) == 1 { 1649 | return optionalDefault[0] 1650 | } 1651 | return 0 1652 | } 1653 | 1654 | // MustUintptr gets the value as a uintptr. 1655 | // 1656 | // Panics if the object is not a uintptr. 1657 | func (v *Value) MustUintptr() uintptr { 1658 | return v.data.(uintptr) 1659 | } 1660 | 1661 | // UintptrSlice gets the value as a []uintptr, returns the optionalDefault 1662 | // value or nil if the value is not a []uintptr. 1663 | func (v *Value) UintptrSlice(optionalDefault ...[]uintptr) []uintptr { 1664 | if s, ok := v.data.([]uintptr); ok { 1665 | return s 1666 | } 1667 | if len(optionalDefault) == 1 { 1668 | return optionalDefault[0] 1669 | } 1670 | return nil 1671 | } 1672 | 1673 | // MustUintptrSlice gets the value as a []uintptr. 1674 | // 1675 | // Panics if the object is not a []uintptr. 1676 | func (v *Value) MustUintptrSlice() []uintptr { 1677 | return v.data.([]uintptr) 1678 | } 1679 | 1680 | // IsUintptr gets whether the object contained is a uintptr or not. 1681 | func (v *Value) IsUintptr() bool { 1682 | _, ok := v.data.(uintptr) 1683 | return ok 1684 | } 1685 | 1686 | // IsUintptrSlice gets whether the object contained is a []uintptr or not. 1687 | func (v *Value) IsUintptrSlice() bool { 1688 | _, ok := v.data.([]uintptr) 1689 | return ok 1690 | } 1691 | 1692 | // EachUintptr calls the specified callback for each object 1693 | // in the []uintptr. 1694 | // 1695 | // Panics if the object is the wrong type. 1696 | func (v *Value) EachUintptr(callback func(int, uintptr) bool) *Value { 1697 | for index, val := range v.MustUintptrSlice() { 1698 | carryon := callback(index, val) 1699 | if !carryon { 1700 | break 1701 | } 1702 | } 1703 | return v 1704 | } 1705 | 1706 | // WhereUintptr uses the specified decider function to select items 1707 | // from the []uintptr. The object contained in the result will contain 1708 | // only the selected items. 1709 | func (v *Value) WhereUintptr(decider func(int, uintptr) bool) *Value { 1710 | var selected []uintptr 1711 | v.EachUintptr(func(index int, val uintptr) bool { 1712 | shouldSelect := decider(index, val) 1713 | if !shouldSelect { 1714 | selected = append(selected, val) 1715 | } 1716 | return true 1717 | }) 1718 | return &Value{data: selected} 1719 | } 1720 | 1721 | // GroupUintptr uses the specified grouper function to group the items 1722 | // keyed by the return of the grouper. The object contained in the 1723 | // result will contain a map[string][]uintptr. 1724 | func (v *Value) GroupUintptr(grouper func(int, uintptr) string) *Value { 1725 | groups := make(map[string][]uintptr) 1726 | v.EachUintptr(func(index int, val uintptr) bool { 1727 | group := grouper(index, val) 1728 | if _, ok := groups[group]; !ok { 1729 | groups[group] = make([]uintptr, 0) 1730 | } 1731 | groups[group] = append(groups[group], val) 1732 | return true 1733 | }) 1734 | return &Value{data: groups} 1735 | } 1736 | 1737 | // ReplaceUintptr uses the specified function to replace each uintptrs 1738 | // by iterating each item. The data in the returned result will be a 1739 | // []uintptr containing the replaced items. 1740 | func (v *Value) ReplaceUintptr(replacer func(int, uintptr) uintptr) *Value { 1741 | arr := v.MustUintptrSlice() 1742 | replaced := make([]uintptr, len(arr)) 1743 | v.EachUintptr(func(index int, val uintptr) bool { 1744 | replaced[index] = replacer(index, val) 1745 | return true 1746 | }) 1747 | return &Value{data: replaced} 1748 | } 1749 | 1750 | // CollectUintptr uses the specified collector function to collect a value 1751 | // for each of the uintptrs in the slice. The data returned will be a 1752 | // []interface{}. 1753 | func (v *Value) CollectUintptr(collector func(int, uintptr) interface{}) *Value { 1754 | arr := v.MustUintptrSlice() 1755 | collected := make([]interface{}, len(arr)) 1756 | v.EachUintptr(func(index int, val uintptr) bool { 1757 | collected[index] = collector(index, val) 1758 | return true 1759 | }) 1760 | return &Value{data: collected} 1761 | } 1762 | 1763 | /* 1764 | Float32 (float32 and []float32) 1765 | */ 1766 | 1767 | // Float32 gets the value as a float32, returns the optionalDefault 1768 | // value or a system default object if the value is the wrong type. 1769 | func (v *Value) Float32(optionalDefault ...float32) float32 { 1770 | if s, ok := v.data.(float32); ok { 1771 | return s 1772 | } 1773 | if len(optionalDefault) == 1 { 1774 | return optionalDefault[0] 1775 | } 1776 | return 0 1777 | } 1778 | 1779 | // MustFloat32 gets the value as a float32. 1780 | // 1781 | // Panics if the object is not a float32. 1782 | func (v *Value) MustFloat32() float32 { 1783 | return v.data.(float32) 1784 | } 1785 | 1786 | // Float32Slice gets the value as a []float32, returns the optionalDefault 1787 | // value or nil if the value is not a []float32. 1788 | func (v *Value) Float32Slice(optionalDefault ...[]float32) []float32 { 1789 | if s, ok := v.data.([]float32); ok { 1790 | return s 1791 | } 1792 | if len(optionalDefault) == 1 { 1793 | return optionalDefault[0] 1794 | } 1795 | return nil 1796 | } 1797 | 1798 | // MustFloat32Slice gets the value as a []float32. 1799 | // 1800 | // Panics if the object is not a []float32. 1801 | func (v *Value) MustFloat32Slice() []float32 { 1802 | return v.data.([]float32) 1803 | } 1804 | 1805 | // IsFloat32 gets whether the object contained is a float32 or not. 1806 | func (v *Value) IsFloat32() bool { 1807 | _, ok := v.data.(float32) 1808 | return ok 1809 | } 1810 | 1811 | // IsFloat32Slice gets whether the object contained is a []float32 or not. 1812 | func (v *Value) IsFloat32Slice() bool { 1813 | _, ok := v.data.([]float32) 1814 | return ok 1815 | } 1816 | 1817 | // EachFloat32 calls the specified callback for each object 1818 | // in the []float32. 1819 | // 1820 | // Panics if the object is the wrong type. 1821 | func (v *Value) EachFloat32(callback func(int, float32) bool) *Value { 1822 | for index, val := range v.MustFloat32Slice() { 1823 | carryon := callback(index, val) 1824 | if !carryon { 1825 | break 1826 | } 1827 | } 1828 | return v 1829 | } 1830 | 1831 | // WhereFloat32 uses the specified decider function to select items 1832 | // from the []float32. The object contained in the result will contain 1833 | // only the selected items. 1834 | func (v *Value) WhereFloat32(decider func(int, float32) bool) *Value { 1835 | var selected []float32 1836 | v.EachFloat32(func(index int, val float32) bool { 1837 | shouldSelect := decider(index, val) 1838 | if !shouldSelect { 1839 | selected = append(selected, val) 1840 | } 1841 | return true 1842 | }) 1843 | return &Value{data: selected} 1844 | } 1845 | 1846 | // GroupFloat32 uses the specified grouper function to group the items 1847 | // keyed by the return of the grouper. The object contained in the 1848 | // result will contain a map[string][]float32. 1849 | func (v *Value) GroupFloat32(grouper func(int, float32) string) *Value { 1850 | groups := make(map[string][]float32) 1851 | v.EachFloat32(func(index int, val float32) bool { 1852 | group := grouper(index, val) 1853 | if _, ok := groups[group]; !ok { 1854 | groups[group] = make([]float32, 0) 1855 | } 1856 | groups[group] = append(groups[group], val) 1857 | return true 1858 | }) 1859 | return &Value{data: groups} 1860 | } 1861 | 1862 | // ReplaceFloat32 uses the specified function to replace each float32s 1863 | // by iterating each item. The data in the returned result will be a 1864 | // []float32 containing the replaced items. 1865 | func (v *Value) ReplaceFloat32(replacer func(int, float32) float32) *Value { 1866 | arr := v.MustFloat32Slice() 1867 | replaced := make([]float32, len(arr)) 1868 | v.EachFloat32(func(index int, val float32) bool { 1869 | replaced[index] = replacer(index, val) 1870 | return true 1871 | }) 1872 | return &Value{data: replaced} 1873 | } 1874 | 1875 | // CollectFloat32 uses the specified collector function to collect a value 1876 | // for each of the float32s in the slice. The data returned will be a 1877 | // []interface{}. 1878 | func (v *Value) CollectFloat32(collector func(int, float32) interface{}) *Value { 1879 | arr := v.MustFloat32Slice() 1880 | collected := make([]interface{}, len(arr)) 1881 | v.EachFloat32(func(index int, val float32) bool { 1882 | collected[index] = collector(index, val) 1883 | return true 1884 | }) 1885 | return &Value{data: collected} 1886 | } 1887 | 1888 | /* 1889 | Float64 (float64 and []float64) 1890 | */ 1891 | 1892 | // Float64 gets the value as a float64, returns the optionalDefault 1893 | // value or a system default object if the value is the wrong type. 1894 | func (v *Value) Float64(optionalDefault ...float64) float64 { 1895 | if s, ok := v.data.(float64); ok { 1896 | return s 1897 | } 1898 | if len(optionalDefault) == 1 { 1899 | return optionalDefault[0] 1900 | } 1901 | return 0 1902 | } 1903 | 1904 | // MustFloat64 gets the value as a float64. 1905 | // 1906 | // Panics if the object is not a float64. 1907 | func (v *Value) MustFloat64() float64 { 1908 | return v.data.(float64) 1909 | } 1910 | 1911 | // Float64Slice gets the value as a []float64, returns the optionalDefault 1912 | // value or nil if the value is not a []float64. 1913 | func (v *Value) Float64Slice(optionalDefault ...[]float64) []float64 { 1914 | if s, ok := v.data.([]float64); ok { 1915 | return s 1916 | } 1917 | if len(optionalDefault) == 1 { 1918 | return optionalDefault[0] 1919 | } 1920 | return nil 1921 | } 1922 | 1923 | // MustFloat64Slice gets the value as a []float64. 1924 | // 1925 | // Panics if the object is not a []float64. 1926 | func (v *Value) MustFloat64Slice() []float64 { 1927 | return v.data.([]float64) 1928 | } 1929 | 1930 | // IsFloat64 gets whether the object contained is a float64 or not. 1931 | func (v *Value) IsFloat64() bool { 1932 | _, ok := v.data.(float64) 1933 | return ok 1934 | } 1935 | 1936 | // IsFloat64Slice gets whether the object contained is a []float64 or not. 1937 | func (v *Value) IsFloat64Slice() bool { 1938 | _, ok := v.data.([]float64) 1939 | return ok 1940 | } 1941 | 1942 | // EachFloat64 calls the specified callback for each object 1943 | // in the []float64. 1944 | // 1945 | // Panics if the object is the wrong type. 1946 | func (v *Value) EachFloat64(callback func(int, float64) bool) *Value { 1947 | for index, val := range v.MustFloat64Slice() { 1948 | carryon := callback(index, val) 1949 | if !carryon { 1950 | break 1951 | } 1952 | } 1953 | return v 1954 | } 1955 | 1956 | // WhereFloat64 uses the specified decider function to select items 1957 | // from the []float64. The object contained in the result will contain 1958 | // only the selected items. 1959 | func (v *Value) WhereFloat64(decider func(int, float64) bool) *Value { 1960 | var selected []float64 1961 | v.EachFloat64(func(index int, val float64) bool { 1962 | shouldSelect := decider(index, val) 1963 | if !shouldSelect { 1964 | selected = append(selected, val) 1965 | } 1966 | return true 1967 | }) 1968 | return &Value{data: selected} 1969 | } 1970 | 1971 | // GroupFloat64 uses the specified grouper function to group the items 1972 | // keyed by the return of the grouper. The object contained in the 1973 | // result will contain a map[string][]float64. 1974 | func (v *Value) GroupFloat64(grouper func(int, float64) string) *Value { 1975 | groups := make(map[string][]float64) 1976 | v.EachFloat64(func(index int, val float64) bool { 1977 | group := grouper(index, val) 1978 | if _, ok := groups[group]; !ok { 1979 | groups[group] = make([]float64, 0) 1980 | } 1981 | groups[group] = append(groups[group], val) 1982 | return true 1983 | }) 1984 | return &Value{data: groups} 1985 | } 1986 | 1987 | // ReplaceFloat64 uses the specified function to replace each float64s 1988 | // by iterating each item. The data in the returned result will be a 1989 | // []float64 containing the replaced items. 1990 | func (v *Value) ReplaceFloat64(replacer func(int, float64) float64) *Value { 1991 | arr := v.MustFloat64Slice() 1992 | replaced := make([]float64, len(arr)) 1993 | v.EachFloat64(func(index int, val float64) bool { 1994 | replaced[index] = replacer(index, val) 1995 | return true 1996 | }) 1997 | return &Value{data: replaced} 1998 | } 1999 | 2000 | // CollectFloat64 uses the specified collector function to collect a value 2001 | // for each of the float64s in the slice. The data returned will be a 2002 | // []interface{}. 2003 | func (v *Value) CollectFloat64(collector func(int, float64) interface{}) *Value { 2004 | arr := v.MustFloat64Slice() 2005 | collected := make([]interface{}, len(arr)) 2006 | v.EachFloat64(func(index int, val float64) bool { 2007 | collected[index] = collector(index, val) 2008 | return true 2009 | }) 2010 | return &Value{data: collected} 2011 | } 2012 | 2013 | /* 2014 | Complex64 (complex64 and []complex64) 2015 | */ 2016 | 2017 | // Complex64 gets the value as a complex64, returns the optionalDefault 2018 | // value or a system default object if the value is the wrong type. 2019 | func (v *Value) Complex64(optionalDefault ...complex64) complex64 { 2020 | if s, ok := v.data.(complex64); ok { 2021 | return s 2022 | } 2023 | if len(optionalDefault) == 1 { 2024 | return optionalDefault[0] 2025 | } 2026 | return 0 2027 | } 2028 | 2029 | // MustComplex64 gets the value as a complex64. 2030 | // 2031 | // Panics if the object is not a complex64. 2032 | func (v *Value) MustComplex64() complex64 { 2033 | return v.data.(complex64) 2034 | } 2035 | 2036 | // Complex64Slice gets the value as a []complex64, returns the optionalDefault 2037 | // value or nil if the value is not a []complex64. 2038 | func (v *Value) Complex64Slice(optionalDefault ...[]complex64) []complex64 { 2039 | if s, ok := v.data.([]complex64); ok { 2040 | return s 2041 | } 2042 | if len(optionalDefault) == 1 { 2043 | return optionalDefault[0] 2044 | } 2045 | return nil 2046 | } 2047 | 2048 | // MustComplex64Slice gets the value as a []complex64. 2049 | // 2050 | // Panics if the object is not a []complex64. 2051 | func (v *Value) MustComplex64Slice() []complex64 { 2052 | return v.data.([]complex64) 2053 | } 2054 | 2055 | // IsComplex64 gets whether the object contained is a complex64 or not. 2056 | func (v *Value) IsComplex64() bool { 2057 | _, ok := v.data.(complex64) 2058 | return ok 2059 | } 2060 | 2061 | // IsComplex64Slice gets whether the object contained is a []complex64 or not. 2062 | func (v *Value) IsComplex64Slice() bool { 2063 | _, ok := v.data.([]complex64) 2064 | return ok 2065 | } 2066 | 2067 | // EachComplex64 calls the specified callback for each object 2068 | // in the []complex64. 2069 | // 2070 | // Panics if the object is the wrong type. 2071 | func (v *Value) EachComplex64(callback func(int, complex64) bool) *Value { 2072 | for index, val := range v.MustComplex64Slice() { 2073 | carryon := callback(index, val) 2074 | if !carryon { 2075 | break 2076 | } 2077 | } 2078 | return v 2079 | } 2080 | 2081 | // WhereComplex64 uses the specified decider function to select items 2082 | // from the []complex64. The object contained in the result will contain 2083 | // only the selected items. 2084 | func (v *Value) WhereComplex64(decider func(int, complex64) bool) *Value { 2085 | var selected []complex64 2086 | v.EachComplex64(func(index int, val complex64) bool { 2087 | shouldSelect := decider(index, val) 2088 | if !shouldSelect { 2089 | selected = append(selected, val) 2090 | } 2091 | return true 2092 | }) 2093 | return &Value{data: selected} 2094 | } 2095 | 2096 | // GroupComplex64 uses the specified grouper function to group the items 2097 | // keyed by the return of the grouper. The object contained in the 2098 | // result will contain a map[string][]complex64. 2099 | func (v *Value) GroupComplex64(grouper func(int, complex64) string) *Value { 2100 | groups := make(map[string][]complex64) 2101 | v.EachComplex64(func(index int, val complex64) bool { 2102 | group := grouper(index, val) 2103 | if _, ok := groups[group]; !ok { 2104 | groups[group] = make([]complex64, 0) 2105 | } 2106 | groups[group] = append(groups[group], val) 2107 | return true 2108 | }) 2109 | return &Value{data: groups} 2110 | } 2111 | 2112 | // ReplaceComplex64 uses the specified function to replace each complex64s 2113 | // by iterating each item. The data in the returned result will be a 2114 | // []complex64 containing the replaced items. 2115 | func (v *Value) ReplaceComplex64(replacer func(int, complex64) complex64) *Value { 2116 | arr := v.MustComplex64Slice() 2117 | replaced := make([]complex64, len(arr)) 2118 | v.EachComplex64(func(index int, val complex64) bool { 2119 | replaced[index] = replacer(index, val) 2120 | return true 2121 | }) 2122 | return &Value{data: replaced} 2123 | } 2124 | 2125 | // CollectComplex64 uses the specified collector function to collect a value 2126 | // for each of the complex64s in the slice. The data returned will be a 2127 | // []interface{}. 2128 | func (v *Value) CollectComplex64(collector func(int, complex64) interface{}) *Value { 2129 | arr := v.MustComplex64Slice() 2130 | collected := make([]interface{}, len(arr)) 2131 | v.EachComplex64(func(index int, val complex64) bool { 2132 | collected[index] = collector(index, val) 2133 | return true 2134 | }) 2135 | return &Value{data: collected} 2136 | } 2137 | 2138 | /* 2139 | Complex128 (complex128 and []complex128) 2140 | */ 2141 | 2142 | // Complex128 gets the value as a complex128, returns the optionalDefault 2143 | // value or a system default object if the value is the wrong type. 2144 | func (v *Value) Complex128(optionalDefault ...complex128) complex128 { 2145 | if s, ok := v.data.(complex128); ok { 2146 | return s 2147 | } 2148 | if len(optionalDefault) == 1 { 2149 | return optionalDefault[0] 2150 | } 2151 | return 0 2152 | } 2153 | 2154 | // MustComplex128 gets the value as a complex128. 2155 | // 2156 | // Panics if the object is not a complex128. 2157 | func (v *Value) MustComplex128() complex128 { 2158 | return v.data.(complex128) 2159 | } 2160 | 2161 | // Complex128Slice gets the value as a []complex128, returns the optionalDefault 2162 | // value or nil if the value is not a []complex128. 2163 | func (v *Value) Complex128Slice(optionalDefault ...[]complex128) []complex128 { 2164 | if s, ok := v.data.([]complex128); ok { 2165 | return s 2166 | } 2167 | if len(optionalDefault) == 1 { 2168 | return optionalDefault[0] 2169 | } 2170 | return nil 2171 | } 2172 | 2173 | // MustComplex128Slice gets the value as a []complex128. 2174 | // 2175 | // Panics if the object is not a []complex128. 2176 | func (v *Value) MustComplex128Slice() []complex128 { 2177 | return v.data.([]complex128) 2178 | } 2179 | 2180 | // IsComplex128 gets whether the object contained is a complex128 or not. 2181 | func (v *Value) IsComplex128() bool { 2182 | _, ok := v.data.(complex128) 2183 | return ok 2184 | } 2185 | 2186 | // IsComplex128Slice gets whether the object contained is a []complex128 or not. 2187 | func (v *Value) IsComplex128Slice() bool { 2188 | _, ok := v.data.([]complex128) 2189 | return ok 2190 | } 2191 | 2192 | // EachComplex128 calls the specified callback for each object 2193 | // in the []complex128. 2194 | // 2195 | // Panics if the object is the wrong type. 2196 | func (v *Value) EachComplex128(callback func(int, complex128) bool) *Value { 2197 | for index, val := range v.MustComplex128Slice() { 2198 | carryon := callback(index, val) 2199 | if !carryon { 2200 | break 2201 | } 2202 | } 2203 | return v 2204 | } 2205 | 2206 | // WhereComplex128 uses the specified decider function to select items 2207 | // from the []complex128. The object contained in the result will contain 2208 | // only the selected items. 2209 | func (v *Value) WhereComplex128(decider func(int, complex128) bool) *Value { 2210 | var selected []complex128 2211 | v.EachComplex128(func(index int, val complex128) bool { 2212 | shouldSelect := decider(index, val) 2213 | if !shouldSelect { 2214 | selected = append(selected, val) 2215 | } 2216 | return true 2217 | }) 2218 | return &Value{data: selected} 2219 | } 2220 | 2221 | // GroupComplex128 uses the specified grouper function to group the items 2222 | // keyed by the return of the grouper. The object contained in the 2223 | // result will contain a map[string][]complex128. 2224 | func (v *Value) GroupComplex128(grouper func(int, complex128) string) *Value { 2225 | groups := make(map[string][]complex128) 2226 | v.EachComplex128(func(index int, val complex128) bool { 2227 | group := grouper(index, val) 2228 | if _, ok := groups[group]; !ok { 2229 | groups[group] = make([]complex128, 0) 2230 | } 2231 | groups[group] = append(groups[group], val) 2232 | return true 2233 | }) 2234 | return &Value{data: groups} 2235 | } 2236 | 2237 | // ReplaceComplex128 uses the specified function to replace each complex128s 2238 | // by iterating each item. The data in the returned result will be a 2239 | // []complex128 containing the replaced items. 2240 | func (v *Value) ReplaceComplex128(replacer func(int, complex128) complex128) *Value { 2241 | arr := v.MustComplex128Slice() 2242 | replaced := make([]complex128, len(arr)) 2243 | v.EachComplex128(func(index int, val complex128) bool { 2244 | replaced[index] = replacer(index, val) 2245 | return true 2246 | }) 2247 | return &Value{data: replaced} 2248 | } 2249 | 2250 | // CollectComplex128 uses the specified collector function to collect a value 2251 | // for each of the complex128s in the slice. The data returned will be a 2252 | // []interface{}. 2253 | func (v *Value) CollectComplex128(collector func(int, complex128) interface{}) *Value { 2254 | arr := v.MustComplex128Slice() 2255 | collected := make([]interface{}, len(arr)) 2256 | v.EachComplex128(func(index int, val complex128) bool { 2257 | collected[index] = collector(index, val) 2258 | return true 2259 | }) 2260 | return &Value{data: collected} 2261 | } 2262 | -------------------------------------------------------------------------------- /type_specific_test.go: -------------------------------------------------------------------------------- 1 | package objx_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/objx" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | /* 12 | Tests for MSI (map[string]interface{} and []map[string]interface{}) 13 | */ 14 | func TestMSI(t *testing.T) { 15 | val := map[string]interface{}(map[string]interface{}{"name": "Tyler"}) 16 | m := objx.Map{"value": val, "nothing": nil} 17 | mVal := map[string]interface{}{"value": val, "nothing": nil} 18 | 19 | assert.Equal(t, mVal, m.Value().MSI()) 20 | assert.Equal(t, val, m.Get("value").MSI()) 21 | assert.Equal(t, mVal, m.Value().MustMSI()) 22 | assert.Equal(t, val, m.Get("value").MustMSI()) 23 | assert.Equal(t, map[string]interface{}(nil), m.Get("nothing").MSI()) 24 | assert.Equal(t, val, m.Get("nothing").MSI(map[string]interface{}{"name": "Tyler"})) 25 | assert.Panics(t, func() { 26 | m.Get("age").MustMSI() 27 | }) 28 | } 29 | 30 | func TestMSISlice(t *testing.T) { 31 | val := map[string]interface{}(map[string]interface{}{"name": "Tyler"}) 32 | m := objx.Map{ 33 | "value": []map[string]interface{}{val}, 34 | "value2": []objx.Map{val}, 35 | "value3": []interface{}{val}, 36 | "nothing": nil, 37 | } 38 | 39 | assert.Equal(t, val, m.Get("value").MSISlice()[0]) 40 | assert.Equal(t, val, m.Get("value2").MSISlice()[0]) 41 | assert.Equal(t, val, m.Get("value3").MSISlice()[0]) 42 | assert.Equal(t, val, m.Get("value").MustMSISlice()[0]) 43 | assert.Equal(t, val, m.Get("value2").MustMSISlice()[0]) 44 | assert.Equal(t, val, m.Get("value3").MustMSISlice()[0]) 45 | assert.Equal(t, []map[string]interface{}(nil), m.Get("nothing").MSISlice()) 46 | assert.Equal(t, val, m.Get("nothing").MSISlice([]map[string]interface{}{map[string]interface{}(map[string]interface{}{"name": "Tyler"})})[0]) 47 | assert.Panics(t, func() { 48 | m.Get("nothing").MustMSISlice() 49 | }) 50 | 51 | o := objx.MustFromJSON(`{"d":[{"author":{"displayName":"DemoUser3","id":2},"classes":null,"id":9879,"v":{"code":"","created":"2013-09-19T09:38:50+02:00","published":"0001-01-01T00:00:00Z","updated":"2013-09-19T09:38:50+02:00"}}],"s":200}`) 52 | assert.Equal(t, float64(9879), o.Get("d").MustMSISlice()[0]["id"]) 53 | assert.Equal(t, 1, len(o.Get("d").MSISlice())) 54 | 55 | i := objx.MustFromJSON(`{"d":[{"author":"abc"},[1]]}`) 56 | assert.Nil(t, i.Get("d").MSISlice()) 57 | } 58 | 59 | func TestIsMSI(t *testing.T) { 60 | m := objx.Map{"data": map[string]interface{}(map[string]interface{}{"name": "Tyler"})} 61 | 62 | assert.True(t, m.Get("data").IsMSI()) 63 | assert.True(t, m.Value().IsMSI()) 64 | } 65 | 66 | func TestIsMSISlice(t *testing.T) { 67 | val := map[string]interface{}(map[string]interface{}{"name": "Tyler"}) 68 | m := objx.Map{"data": []map[string]interface{}{val}, "data2": []objx.Map{val}} 69 | 70 | assert.True(t, m.Get("data").IsMSISlice()) 71 | assert.True(t, m.Get("data2").IsMSISlice()) 72 | 73 | o := objx.MustFromJSON(`{"d":[{"author":{"displayName":"DemoUser3","id":2},"classes":null,"id":9879,"v":{"code":"","created":"2013-09-19T09:38:50+02:00","published":"0001-01-01T00:00:00Z","updated":"2013-09-19T09:38:50+02:00"}}],"s":200}`) 74 | assert.True(t, o.Has("d")) 75 | assert.True(t, o.Get("d").IsMSISlice()) 76 | 77 | o = objx.MustFromJSON(`{"d":[{"author":"abc"},[1]]}`) 78 | assert.True(t, o.Has("d")) 79 | assert.False(t, o.Get("d").IsMSISlice()) 80 | } 81 | 82 | func TestEachMSI(t *testing.T) { 83 | m := objx.Map{"data": []map[string]interface{}{map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"})}} 84 | count := 0 85 | replacedVals := make([]map[string]interface{}, 0) 86 | assert.Equal(t, m.Get("data"), m.Get("data").EachMSI(func(i int, val map[string]interface{}) bool { 87 | count++ 88 | replacedVals = append(replacedVals, val) 89 | 90 | // abort early 91 | return i != 2 92 | })) 93 | 94 | m2 := objx.Map{"data": []objx.Map{{"name": "Taylor"}, {"name": "Taylor"}, {"name": "Taylor"}, {"name": "Taylor"}, {"name": "Taylor"}}} 95 | assert.Equal(t, m2.Get("data"), m2.Get("data").EachMSI(func(i int, val map[string]interface{}) bool { 96 | count++ 97 | replacedVals = append(replacedVals, val) 98 | 99 | // abort early 100 | return i != 2 101 | })) 102 | 103 | assert.Equal(t, count, 6) 104 | assert.Equal(t, replacedVals[0], m.Get("data").MustMSISlice()[0]) 105 | assert.Equal(t, replacedVals[1], m.Get("data").MustMSISlice()[1]) 106 | assert.Equal(t, replacedVals[2], m.Get("data").MustMSISlice()[2]) 107 | assert.Equal(t, replacedVals[3], m2.Get("data").MustMSISlice()[0]) 108 | assert.Equal(t, replacedVals[4], m2.Get("data").MustMSISlice()[1]) 109 | assert.Equal(t, replacedVals[5], m2.Get("data").MustMSISlice()[2]) 110 | } 111 | 112 | func TestWhereMSI(t *testing.T) { 113 | m := objx.Map{"data": []map[string]interface{}{map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"})}} 114 | 115 | selected := m.Get("data").WhereMSI(func(i int, val map[string]interface{}) bool { 116 | return i%2 == 0 117 | }).MustMSISlice() 118 | 119 | assert.Equal(t, 3, len(selected)) 120 | } 121 | 122 | func TestWhereMSI2(t *testing.T) { 123 | m := objx.Map{"data": []objx.Map{{"name": "Taylor"}, {"name": "Taylor"}, {"name": "Taylor"}, {"name": "Taylor"}, {"name": "Taylor"}}} 124 | 125 | selected := m.Get("data").WhereMSI(func(i int, val map[string]interface{}) bool { 126 | return i%2 == 0 127 | }).MustMSISlice() 128 | 129 | assert.Equal(t, 2, len(selected)) 130 | } 131 | 132 | func TestGroupMSI(t *testing.T) { 133 | m := objx.Map{"data": []map[string]interface{}{map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"})}} 134 | 135 | grouped := m.Get("data").GroupMSI(func(i int, val map[string]interface{}) string { 136 | return fmt.Sprintf("%v", i%2 == 0) 137 | }).Data().(map[string][]map[string]interface{}) 138 | 139 | assert.Equal(t, 2, len(grouped)) 140 | assert.Equal(t, 3, len(grouped["true"])) 141 | assert.Equal(t, 3, len(grouped["false"])) 142 | } 143 | 144 | func TestGroupMSI2(t *testing.T) { 145 | m := objx.Map{"data": []objx.Map{{"name": "Taylor"}, {"name": "Taylor"}, {"name": "Taylor"}, {"name": "Taylor"}, {"name": "Taylor"}}} 146 | 147 | grouped := m.Get("data").GroupMSI(func(i int, val map[string]interface{}) string { 148 | return fmt.Sprintf("%v", i%2 == 0) 149 | }).Data().(map[string][]map[string]interface{}) 150 | 151 | assert.Equal(t, 2, len(grouped)) 152 | assert.Equal(t, 3, len(grouped["true"])) 153 | assert.Equal(t, 2, len(grouped["false"])) 154 | } 155 | 156 | func TestReplaceMSI(t *testing.T) { 157 | m := objx.Map{"data": []map[string]interface{}{map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"})}} 158 | rawArr := m.Get("data").MustMSISlice() 159 | 160 | replaced := m.Get("data").ReplaceMSI(func(index int, val map[string]interface{}) map[string]interface{} { 161 | if index < len(rawArr)-1 { 162 | return rawArr[index+1] 163 | } 164 | return rawArr[0] 165 | }) 166 | replacedArr := replaced.MustMSISlice() 167 | 168 | if assert.Equal(t, 6, len(replacedArr)) { 169 | assert.Equal(t, replacedArr[0], rawArr[1]) 170 | assert.Equal(t, replacedArr[1], rawArr[2]) 171 | assert.Equal(t, replacedArr[2], rawArr[3]) 172 | assert.Equal(t, replacedArr[3], rawArr[4]) 173 | assert.Equal(t, replacedArr[4], rawArr[5]) 174 | assert.Equal(t, replacedArr[5], rawArr[0]) 175 | } 176 | } 177 | 178 | func TestReplaceMSI2(t *testing.T) { 179 | m := objx.Map{"data": []objx.Map{{"name": "Taylor"}, {"name": "Taylor"}, {"name": "Taylor"}, {"name": "Taylor"}, {"name": "Taylor"}}} 180 | rawArr := m.Get("data").MustMSISlice() 181 | 182 | replaced := m.Get("data").ReplaceMSI(func(index int, val map[string]interface{}) map[string]interface{} { 183 | if index < len(rawArr)-1 { 184 | return rawArr[index+1] 185 | } 186 | return rawArr[0] 187 | }) 188 | replacedArr := replaced.MustMSISlice() 189 | 190 | if assert.Equal(t, 5, len(replacedArr)) { 191 | assert.Equal(t, replacedArr[0], rawArr[1]) 192 | assert.Equal(t, replacedArr[1], rawArr[2]) 193 | assert.Equal(t, replacedArr[2], rawArr[3]) 194 | assert.Equal(t, replacedArr[3], rawArr[4]) 195 | assert.Equal(t, replacedArr[4], rawArr[0]) 196 | } 197 | } 198 | 199 | func TestCollectMSI(t *testing.T) { 200 | m := objx.Map{"data": []map[string]interface{}{map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"})}} 201 | 202 | collected := m.Get("data").CollectMSI(func(index int, val map[string]interface{}) interface{} { 203 | return index 204 | }) 205 | collectedArr := collected.MustInterSlice() 206 | 207 | if assert.Equal(t, 6, len(collectedArr)) { 208 | assert.Equal(t, collectedArr[0], 0) 209 | assert.Equal(t, collectedArr[1], 1) 210 | assert.Equal(t, collectedArr[2], 2) 211 | assert.Equal(t, collectedArr[3], 3) 212 | assert.Equal(t, collectedArr[4], 4) 213 | assert.Equal(t, collectedArr[5], 5) 214 | } 215 | } 216 | 217 | func TestCollectMSI2(t *testing.T) { 218 | m := objx.Map{"data": []objx.Map{{"name": "Taylor"}, {"name": "Taylor"}, {"name": "Taylor"}, {"name": "Taylor"}, {"name": "Taylor"}}} 219 | 220 | collected := m.Get("data").CollectMSI(func(index int, val map[string]interface{}) interface{} { 221 | return index 222 | }) 223 | collectedArr := collected.MustInterSlice() 224 | 225 | if assert.Equal(t, 5, len(collectedArr)) { 226 | assert.Equal(t, collectedArr[0], 0) 227 | assert.Equal(t, collectedArr[1], 1) 228 | assert.Equal(t, collectedArr[2], 2) 229 | assert.Equal(t, collectedArr[3], 3) 230 | assert.Equal(t, collectedArr[4], 4) 231 | } 232 | } 233 | 234 | /* 235 | Tests for ObjxMap ((objx.Map) and [](objx.Map)) 236 | */ 237 | func TestObjxMap(t *testing.T) { 238 | val := (objx.Map)(objx.New(1)) 239 | m := objx.Map{"value": val, "value2": map[string]interface{}{"name": "Taylor"}, "nothing": nil} 240 | valMSI := objx.Map{"name": "Taylor"} 241 | 242 | assert.Equal(t, val, m.Get("value").ObjxMap()) 243 | assert.Equal(t, valMSI, m.Get("value2").ObjxMap()) 244 | assert.Equal(t, val, m.Get("value").MustObjxMap()) 245 | assert.Equal(t, valMSI, m.Get("value2").MustObjxMap()) 246 | assert.Equal(t, (objx.Map)(objx.New(nil)), m.Get("nothing").ObjxMap()) 247 | assert.Equal(t, val, m.Get("nothing").ObjxMap(objx.New(1))) 248 | assert.Panics(t, func() { 249 | m.Get("age").MustObjxMap() 250 | }) 251 | } 252 | 253 | func TestObjxMapSlice(t *testing.T) { 254 | val := (objx.Map)(objx.New(1)) 255 | m := objx.Map{ 256 | "value": [](objx.Map){val}, 257 | "value2": []map[string]interface{}{map[string]interface{}(map[string]interface{}{"name": "Taylor"})}, 258 | "value3": []interface{}{val}, 259 | "value4": []interface{}{map[string]interface{}(map[string]interface{}{"name": "Taylor"})}, 260 | "nothing": nil, 261 | } 262 | valMSI := objx.Map{"name": "Taylor"} 263 | 264 | assert.Equal(t, val, m.Get("value").ObjxMapSlice()[0]) 265 | assert.Equal(t, valMSI, m.Get("value2").ObjxMapSlice()[0]) 266 | assert.Equal(t, val, m.Get("value3").ObjxMapSlice()[0]) 267 | assert.Equal(t, valMSI, m.Get("value4").ObjxMapSlice()[0]) 268 | assert.Equal(t, val, m.Get("value").MustObjxMapSlice()[0]) 269 | assert.Equal(t, valMSI, m.Get("value2").MustObjxMapSlice()[0]) 270 | assert.Equal(t, val, m.Get("value3").MustObjxMapSlice()[0]) 271 | assert.Equal(t, valMSI, m.Get("value4").MustObjxMapSlice()[0]) 272 | assert.Equal(t, [](objx.Map)(nil), m.Get("nothing").ObjxMapSlice()) 273 | assert.Equal(t, val, m.Get("nothing").ObjxMapSlice([](objx.Map){(objx.Map)(objx.New(1))})[0]) 274 | assert.Panics(t, func() { 275 | m.Get("nothing").MustObjxMapSlice() 276 | }) 277 | 278 | o := objx.MustFromJSON(`{"d":[{"author":{"displayName":"DemoUser3","id":2},"classes":null,"id":9879,"v":{"code":"","created":"2013-09-19T09:38:50+02:00","published":"0001-01-01T00:00:00Z","updated":"2013-09-19T09:38:50+02:00"}}],"s":200}`) 279 | assert.Equal(t, 9879, o.Get("d").MustObjxMapSlice()[0].Get("id").Int()) 280 | assert.Equal(t, 1, len(o.Get("d").ObjxMapSlice())) 281 | 282 | i := objx.MustFromJSON(`{"d":[{"author":"abc"},[1]]}`) 283 | assert.Nil(t, i.Get("d").ObjxMapSlice()) 284 | } 285 | 286 | func TestIsObjxMap(t *testing.T) { 287 | m := objx.Map{"data": (objx.Map)(objx.New(1)), "data2": map[string]interface{}{"name": "Taylor"}} 288 | 289 | assert.True(t, m.Get("data").IsObjxMap()) 290 | assert.True(t, m.Get("data2").IsObjxMap()) 291 | } 292 | 293 | func TestIsObjxMapSlice(t *testing.T) { 294 | m := objx.Map{"data": [](objx.Map){(objx.Map)(objx.New(1))}, "data2": []map[string]interface{}{map[string]interface{}(map[string]interface{}{"name": "Taylor"})}} 295 | 296 | assert.True(t, m.Get("data").IsObjxMapSlice()) 297 | assert.True(t, m.Get("data2").IsObjxMapSlice()) 298 | 299 | o := objx.MustFromJSON(`{"d":[{"author":{"displayName":"DemoUser3","id":2},"classes":null,"id":9879,"v":{"code":"","created":"2013-09-19T09:38:50+02:00","published":"0001-01-01T00:00:00Z","updated":"2013-09-19T09:38:50+02:00"}}],"s":200}`) 300 | assert.True(t, o.Has("d")) 301 | assert.True(t, o.Get("d").IsObjxMapSlice()) 302 | 303 | // Valid json but not MSI slice 304 | o = objx.MustFromJSON(`{"d":[{"author":"abc"},[1]]}`) 305 | assert.True(t, o.Has("d")) 306 | assert.False(t, o.Get("d").IsObjxMapSlice()) 307 | } 308 | 309 | func TestEachObjxMap(t *testing.T) { 310 | m := objx.Map{"data": [](objx.Map){(objx.Map)(objx.New(1)), (objx.Map)(objx.New(1)), (objx.Map)(objx.New(1)), (objx.Map)(objx.New(1)), (objx.Map)(objx.New(1))}} 311 | count := 0 312 | replacedVals := make([](objx.Map), 0) 313 | assert.Equal(t, m.Get("data"), m.Get("data").EachObjxMap(func(i int, val objx.Map) bool { 314 | count++ 315 | replacedVals = append(replacedVals, val) 316 | 317 | // abort early 318 | return i != 2 319 | })) 320 | 321 | m2 := objx.Map{"data": []map[string]interface{}{map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"})}} 322 | assert.Equal(t, m2.Get("data"), m2.Get("data").EachObjxMap(func(i int, val objx.Map) bool { 323 | count++ 324 | replacedVals = append(replacedVals, val) 325 | 326 | // abort early 327 | return i != 2 328 | })) 329 | 330 | assert.Equal(t, count, 6) 331 | assert.Equal(t, replacedVals[0], m.Get("data").MustObjxMapSlice()[0]) 332 | assert.Equal(t, replacedVals[1], m.Get("data").MustObjxMapSlice()[1]) 333 | assert.Equal(t, replacedVals[2], m.Get("data").MustObjxMapSlice()[2]) 334 | assert.Equal(t, replacedVals[3], m2.Get("data").MustObjxMapSlice()[0]) 335 | assert.Equal(t, replacedVals[4], m2.Get("data").MustObjxMapSlice()[1]) 336 | assert.Equal(t, replacedVals[5], m2.Get("data").MustObjxMapSlice()[2]) 337 | } 338 | 339 | func TestWhereObjxMap(t *testing.T) { 340 | m := objx.Map{"data": [](objx.Map){(objx.Map)(objx.New(1)), (objx.Map)(objx.New(1)), (objx.Map)(objx.New(1)), (objx.Map)(objx.New(1)), (objx.Map)(objx.New(1)), (objx.Map)(objx.New(1))}} 341 | 342 | selected := m.Get("data").WhereObjxMap(func(i int, val objx.Map) bool { 343 | return i%2 == 0 344 | }).MustObjxMapSlice() 345 | 346 | assert.Equal(t, 3, len(selected)) 347 | } 348 | 349 | func TestWhereObjxMap2(t *testing.T) { 350 | m := objx.Map{"data": []map[string]interface{}{map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"})}} 351 | 352 | selected := m.Get("data").WhereObjxMap(func(i int, val objx.Map) bool { 353 | return i%2 == 0 354 | }).MustObjxMapSlice() 355 | 356 | assert.Equal(t, 2, len(selected)) 357 | } 358 | 359 | func TestGroupObjxMap(t *testing.T) { 360 | m := objx.Map{"data": [](objx.Map){(objx.Map)(objx.New(1)), (objx.Map)(objx.New(1)), (objx.Map)(objx.New(1)), (objx.Map)(objx.New(1)), (objx.Map)(objx.New(1)), (objx.Map)(objx.New(1))}} 361 | 362 | grouped := m.Get("data").GroupObjxMap(func(i int, val objx.Map) string { 363 | return fmt.Sprintf("%v", i%2 == 0) 364 | }).Data().(map[string][](objx.Map)) 365 | 366 | assert.Equal(t, 2, len(grouped)) 367 | assert.Equal(t, 3, len(grouped["true"])) 368 | assert.Equal(t, 3, len(grouped["false"])) 369 | } 370 | 371 | func TestGroupObjxMap2(t *testing.T) { 372 | m := objx.Map{"data": []map[string]interface{}{map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"})}} 373 | 374 | grouped := m.Get("data").GroupObjxMap(func(i int, val objx.Map) string { 375 | return fmt.Sprintf("%v", i%2 == 0) 376 | }).Data().(map[string][](objx.Map)) 377 | 378 | assert.Equal(t, 2, len(grouped)) 379 | assert.Equal(t, 3, len(grouped["true"])) 380 | assert.Equal(t, 2, len(grouped["false"])) 381 | } 382 | 383 | func TestReplaceObjxMap(t *testing.T) { 384 | m := objx.Map{"data": [](objx.Map){(objx.Map)(objx.New(1)), (objx.Map)(objx.New(1)), (objx.Map)(objx.New(1)), (objx.Map)(objx.New(1)), (objx.Map)(objx.New(1)), (objx.Map)(objx.New(1))}} 385 | rawArr := m.Get("data").MustObjxMapSlice() 386 | 387 | replaced := m.Get("data").ReplaceObjxMap(func(index int, val objx.Map) objx.Map { 388 | if index < len(rawArr)-1 { 389 | return rawArr[index+1] 390 | } 391 | return rawArr[0] 392 | }) 393 | replacedArr := replaced.MustObjxMapSlice() 394 | 395 | if assert.Equal(t, 6, len(replacedArr)) { 396 | assert.Equal(t, replacedArr[0], rawArr[1]) 397 | assert.Equal(t, replacedArr[1], rawArr[2]) 398 | assert.Equal(t, replacedArr[2], rawArr[3]) 399 | assert.Equal(t, replacedArr[3], rawArr[4]) 400 | assert.Equal(t, replacedArr[4], rawArr[5]) 401 | assert.Equal(t, replacedArr[5], rawArr[0]) 402 | } 403 | } 404 | 405 | func TestReplaceObjxMap2(t *testing.T) { 406 | m := objx.Map{"data": []map[string]interface{}{map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"})}} 407 | rawArr := m.Get("data").MustObjxMapSlice() 408 | 409 | replaced := m.Get("data").ReplaceObjxMap(func(index int, val objx.Map) objx.Map { 410 | if index < len(rawArr)-1 { 411 | return rawArr[index+1] 412 | } 413 | return rawArr[0] 414 | }) 415 | replacedArr := replaced.MustObjxMapSlice() 416 | 417 | if assert.Equal(t, 5, len(replacedArr)) { 418 | assert.Equal(t, replacedArr[0], rawArr[1]) 419 | assert.Equal(t, replacedArr[1], rawArr[2]) 420 | assert.Equal(t, replacedArr[2], rawArr[3]) 421 | assert.Equal(t, replacedArr[3], rawArr[4]) 422 | assert.Equal(t, replacedArr[4], rawArr[0]) 423 | } 424 | } 425 | 426 | func TestCollectObjxMap(t *testing.T) { 427 | m := objx.Map{"data": [](objx.Map){(objx.Map)(objx.New(1)), (objx.Map)(objx.New(1)), (objx.Map)(objx.New(1)), (objx.Map)(objx.New(1)), (objx.Map)(objx.New(1)), (objx.Map)(objx.New(1))}} 428 | 429 | collected := m.Get("data").CollectObjxMap(func(index int, val objx.Map) interface{} { 430 | return index 431 | }) 432 | collectedArr := collected.MustInterSlice() 433 | 434 | if assert.Equal(t, 6, len(collectedArr)) { 435 | assert.Equal(t, collectedArr[0], 0) 436 | assert.Equal(t, collectedArr[1], 1) 437 | assert.Equal(t, collectedArr[2], 2) 438 | assert.Equal(t, collectedArr[3], 3) 439 | assert.Equal(t, collectedArr[4], 4) 440 | assert.Equal(t, collectedArr[5], 5) 441 | } 442 | } 443 | 444 | func TestCollectObjxMap2(t *testing.T) { 445 | m := objx.Map{"data": []map[string]interface{}{map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"}), map[string]interface{}(map[string]interface{}{"name": "Tyler"})}} 446 | 447 | collected := m.Get("data").CollectObjxMap(func(index int, val objx.Map) interface{} { 448 | return index 449 | }) 450 | collectedArr := collected.MustInterSlice() 451 | 452 | if assert.Equal(t, 5, len(collectedArr)) { 453 | assert.Equal(t, collectedArr[0], 0) 454 | assert.Equal(t, collectedArr[1], 1) 455 | assert.Equal(t, collectedArr[2], 2) 456 | assert.Equal(t, collectedArr[3], 3) 457 | assert.Equal(t, collectedArr[4], 4) 458 | } 459 | } 460 | -------------------------------------------------------------------------------- /value.go: -------------------------------------------------------------------------------- 1 | package objx 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | // Value provides methods for extracting interface{} data in various 9 | // types. 10 | type Value struct { 11 | // data contains the raw data being managed by this Value 12 | data interface{} 13 | } 14 | 15 | // Data returns the raw data contained by this Value 16 | func (v *Value) Data() interface{} { 17 | return v.data 18 | } 19 | 20 | // String returns the value always as a string 21 | func (v *Value) String() string { 22 | switch { 23 | case v.IsNil(): 24 | return "" 25 | case v.IsStr(): 26 | return v.Str() 27 | case v.IsBool(): 28 | return strconv.FormatBool(v.Bool()) 29 | case v.IsFloat32(): 30 | return strconv.FormatFloat(float64(v.Float32()), 'f', -1, 32) 31 | case v.IsFloat64(): 32 | return strconv.FormatFloat(v.Float64(), 'f', -1, 64) 33 | case v.IsInt(): 34 | return strconv.FormatInt(int64(v.Int()), 10) 35 | case v.IsInt8(): 36 | return strconv.FormatInt(int64(v.Int8()), 10) 37 | case v.IsInt16(): 38 | return strconv.FormatInt(int64(v.Int16()), 10) 39 | case v.IsInt32(): 40 | return strconv.FormatInt(int64(v.Int32()), 10) 41 | case v.IsInt64(): 42 | return strconv.FormatInt(v.Int64(), 10) 43 | case v.IsUint(): 44 | return strconv.FormatUint(uint64(v.Uint()), 10) 45 | case v.IsUint8(): 46 | return strconv.FormatUint(uint64(v.Uint8()), 10) 47 | case v.IsUint16(): 48 | return strconv.FormatUint(uint64(v.Uint16()), 10) 49 | case v.IsUint32(): 50 | return strconv.FormatUint(uint64(v.Uint32()), 10) 51 | case v.IsUint64(): 52 | return strconv.FormatUint(v.Uint64(), 10) 53 | } 54 | return fmt.Sprintf("%#v", v.Data()) 55 | } 56 | 57 | // StringSlice returns the value always as a []string 58 | func (v *Value) StringSlice(optionalDefault ...[]string) []string { 59 | switch { 60 | case v.IsStrSlice(): 61 | return v.MustStrSlice() 62 | case v.IsBoolSlice(): 63 | slice := v.MustBoolSlice() 64 | vals := make([]string, len(slice)) 65 | for i, iv := range slice { 66 | vals[i] = strconv.FormatBool(iv) 67 | } 68 | return vals 69 | case v.IsFloat32Slice(): 70 | slice := v.MustFloat32Slice() 71 | vals := make([]string, len(slice)) 72 | for i, iv := range slice { 73 | vals[i] = strconv.FormatFloat(float64(iv), 'f', -1, 32) 74 | } 75 | return vals 76 | case v.IsFloat64Slice(): 77 | slice := v.MustFloat64Slice() 78 | vals := make([]string, len(slice)) 79 | for i, iv := range slice { 80 | vals[i] = strconv.FormatFloat(iv, 'f', -1, 64) 81 | } 82 | return vals 83 | case v.IsIntSlice(): 84 | slice := v.MustIntSlice() 85 | vals := make([]string, len(slice)) 86 | for i, iv := range slice { 87 | vals[i] = strconv.FormatInt(int64(iv), 10) 88 | } 89 | return vals 90 | case v.IsInt8Slice(): 91 | slice := v.MustInt8Slice() 92 | vals := make([]string, len(slice)) 93 | for i, iv := range slice { 94 | vals[i] = strconv.FormatInt(int64(iv), 10) 95 | } 96 | return vals 97 | case v.IsInt16Slice(): 98 | slice := v.MustInt16Slice() 99 | vals := make([]string, len(slice)) 100 | for i, iv := range slice { 101 | vals[i] = strconv.FormatInt(int64(iv), 10) 102 | } 103 | return vals 104 | case v.IsInt32Slice(): 105 | slice := v.MustInt32Slice() 106 | vals := make([]string, len(slice)) 107 | for i, iv := range slice { 108 | vals[i] = strconv.FormatInt(int64(iv), 10) 109 | } 110 | return vals 111 | case v.IsInt64Slice(): 112 | slice := v.MustInt64Slice() 113 | vals := make([]string, len(slice)) 114 | for i, iv := range slice { 115 | vals[i] = strconv.FormatInt(iv, 10) 116 | } 117 | return vals 118 | case v.IsUintSlice(): 119 | slice := v.MustUintSlice() 120 | vals := make([]string, len(slice)) 121 | for i, iv := range slice { 122 | vals[i] = strconv.FormatUint(uint64(iv), 10) 123 | } 124 | return vals 125 | case v.IsUint8Slice(): 126 | slice := v.MustUint8Slice() 127 | vals := make([]string, len(slice)) 128 | for i, iv := range slice { 129 | vals[i] = strconv.FormatUint(uint64(iv), 10) 130 | } 131 | return vals 132 | case v.IsUint16Slice(): 133 | slice := v.MustUint16Slice() 134 | vals := make([]string, len(slice)) 135 | for i, iv := range slice { 136 | vals[i] = strconv.FormatUint(uint64(iv), 10) 137 | } 138 | return vals 139 | case v.IsUint32Slice(): 140 | slice := v.MustUint32Slice() 141 | vals := make([]string, len(slice)) 142 | for i, iv := range slice { 143 | vals[i] = strconv.FormatUint(uint64(iv), 10) 144 | } 145 | return vals 146 | case v.IsUint64Slice(): 147 | slice := v.MustUint64Slice() 148 | vals := make([]string, len(slice)) 149 | for i, iv := range slice { 150 | vals[i] = strconv.FormatUint(iv, 10) 151 | } 152 | return vals 153 | } 154 | if len(optionalDefault) == 1 { 155 | return optionalDefault[0] 156 | } 157 | 158 | return []string{} 159 | } 160 | -------------------------------------------------------------------------------- /value_test.go: -------------------------------------------------------------------------------- 1 | package objx_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/objx" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestStringTypeString(t *testing.T) { 11 | m := objx.Map{ 12 | "string": "foo", 13 | } 14 | 15 | assert.Equal(t, "foo", m.Get("string").String()) 16 | } 17 | 18 | func TestStringTypeBool(t *testing.T) { 19 | m := objx.Map{ 20 | "bool": true, 21 | } 22 | 23 | assert.Equal(t, "true", m.Get("bool").String()) 24 | } 25 | 26 | func TestStringTypeInt(t *testing.T) { 27 | m := objx.Map{ 28 | "int": int(1), 29 | "int8": int8(8), 30 | "int16": int16(16), 31 | "int32": int32(32), 32 | "int64": int64(64), 33 | } 34 | 35 | assert.Equal(t, "1", m.Get("int").String()) 36 | assert.Equal(t, "8", m.Get("int8").String()) 37 | assert.Equal(t, "16", m.Get("int16").String()) 38 | assert.Equal(t, "32", m.Get("int32").String()) 39 | assert.Equal(t, "64", m.Get("int64").String()) 40 | } 41 | 42 | func TestStringTypeUint(t *testing.T) { 43 | m := objx.Map{ 44 | "uint": uint(1), 45 | "uint8": uint8(8), 46 | "uint16": uint16(16), 47 | "uint32": uint32(32), 48 | "uint64": uint64(64), 49 | } 50 | 51 | assert.Equal(t, "1", m.Get("uint").String()) 52 | assert.Equal(t, "8", m.Get("uint8").String()) 53 | assert.Equal(t, "16", m.Get("uint16").String()) 54 | assert.Equal(t, "32", m.Get("uint32").String()) 55 | assert.Equal(t, "64", m.Get("uint64").String()) 56 | } 57 | 58 | func TestStringTypeFloat(t *testing.T) { 59 | m := objx.Map{ 60 | "float32": float32(32.32), 61 | "float64": float64(64.64), 62 | } 63 | 64 | assert.Equal(t, "32.32", m.Get("float32").String()) 65 | assert.Equal(t, "64.64", m.Get("float64").String()) 66 | } 67 | 68 | func TestStringTypeOther(t *testing.T) { 69 | m := objx.Map{ 70 | "other": []string{"foo", "bar"}, 71 | "nilValue": nil, 72 | } 73 | 74 | assert.Equal(t, "[]string{\"foo\", \"bar\"}", m.Get("other").String()) 75 | assert.Equal(t, "", m.Get("nilValue").String()) 76 | } 77 | 78 | func TestStringSliceTypeString(t *testing.T) { 79 | m := objx.Map{ 80 | "string": []string{"foo", "bar"}, 81 | } 82 | 83 | assert.Equal(t, []string{"foo", "bar"}, m.Get("string").StringSlice()) 84 | } 85 | 86 | func TestStringSliceTypeBool(t *testing.T) { 87 | m := objx.Map{ 88 | "bool": []bool{true, false}, 89 | } 90 | 91 | assert.Equal(t, []string{"true", "false"}, m.Get("bool").StringSlice()) 92 | } 93 | 94 | func TestStringSliceTypeInt(t *testing.T) { 95 | m := objx.Map{ 96 | "int": []int{1, 2}, 97 | "int8": []int8{8, 9}, 98 | "int16": []int16{16, 17}, 99 | "int32": []int32{32, 33}, 100 | "int64": []int64{64, 65}, 101 | } 102 | 103 | assert.Equal(t, []string{"1", "2"}, m.Get("int").StringSlice()) 104 | assert.Equal(t, []string{"8", "9"}, m.Get("int8").StringSlice()) 105 | assert.Equal(t, []string{"16", "17"}, m.Get("int16").StringSlice()) 106 | assert.Equal(t, []string{"32", "33"}, m.Get("int32").StringSlice()) 107 | assert.Equal(t, []string{"64", "65"}, m.Get("int64").StringSlice()) 108 | } 109 | 110 | func TestStringSliceTypeUint(t *testing.T) { 111 | m := objx.Map{ 112 | "uint": []uint{1, 2}, 113 | "uint8": []uint8{8, 9}, 114 | "uint16": []uint16{16, 17}, 115 | "uint32": []uint32{32, 33}, 116 | "uint64": []uint64{64, 65}, 117 | } 118 | 119 | assert.Equal(t, []string{"1", "2"}, m.Get("uint").StringSlice()) 120 | assert.Equal(t, []string{"8", "9"}, m.Get("uint8").StringSlice()) 121 | assert.Equal(t, []string{"16", "17"}, m.Get("uint16").StringSlice()) 122 | assert.Equal(t, []string{"32", "33"}, m.Get("uint32").StringSlice()) 123 | assert.Equal(t, []string{"64", "65"}, m.Get("uint64").StringSlice()) 124 | } 125 | 126 | func TestStringSliceTypeFloat(t *testing.T) { 127 | m := objx.Map{ 128 | "float32": []float32{32.32, 33.33}, 129 | "float64": []float64{64.64, 65.65}, 130 | } 131 | 132 | assert.Equal(t, []string{"32.32", "33.33"}, m.Get("float32").StringSlice()) 133 | assert.Equal(t, []string{"64.64", "65.65"}, m.Get("float64").StringSlice()) 134 | } 135 | 136 | func TestStringSliceTypeOther(t *testing.T) { 137 | m := objx.Map{ 138 | "other": "foo", 139 | } 140 | 141 | assert.Equal(t, []string{}, m.Get("other").StringSlice()) 142 | assert.Equal(t, []string{"bar"}, m.Get("other").StringSlice([]string{"bar"})) 143 | } 144 | --------------------------------------------------------------------------------