├── LICENSE ├── README.md ├── README_CN.md ├── array ├── array.go ├── array_test.go ├── utils.go └── utils_test.go ├── go.mod └── logo.png /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2020-3500 deatil(http://github.com/deatil) 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## go-array 2 | 3 |

4 | Go Reference 5 | 6 | 7 | Mentioned in Awesome Go 8 |

9 | 10 |

11 | A Go package that read or set data from map, slice or json 12 |

13 | 14 | [中文](README_CN.md) | English 15 | 16 | 17 | ### Download 18 | 19 | ~~~go 20 | go get -u github.com/deatil/go-array 21 | ~~~ 22 | 23 | 24 | ### Get Starting 25 | 26 | ~~~go 27 | import "github.com/deatil/go-array/array" 28 | 29 | arrData := map[string]any{ 30 | "a": 123, 31 | "b": map[string]any{ 32 | "c": "ccc", 33 | "d": map[string]any{ 34 | "e": "eee", 35 | "f": map[string]any{ 36 | "g": "ggg", 37 | }, 38 | }, 39 | "dd": []any{ 40 | "ccccc", 41 | "ddddd", 42 | "fffff", 43 | }, 44 | "ff": map[any]any{ 45 | 111: "fccccc", 46 | 222: "fddddd", 47 | 333: "dfffff", 48 | }, 49 | "hh": map[int]any{ 50 | 1115: "hccccc", 51 | 2225: "hddddd", 52 | 3335: map[any]string{ 53 | "qq1": "qq1ccccc", 54 | "qq2": "qq2ddddd", 55 | "qq3": "qq3fffff", 56 | }, 57 | }, 58 | "kJh21ay": map[string]any{ 59 | "Hjk2": "fccDcc", 60 | "23rt": "^hgcF5c", 61 | }, 62 | }, 63 | } 64 | 65 | data := array.Get(arrData, "b.d.e") 66 | // output: eee 67 | 68 | data := array.Get(arrData, "b.dd.1") 69 | // output: ddddd 70 | 71 | data := array.Get(arrData, "b.hh.3335.qq2") 72 | // output: qq2ddddd 73 | 74 | data := array.Get(arrData, "b.kJh21ay.Hjk2", "defValString") 75 | // output: fccDcc 76 | 77 | data := array.Get(arrData, "b.kJh21ay.Hjk23333", "defValString") 78 | // output: defValString 79 | ~~~ 80 | 81 | 82 | ### Examples 83 | 84 | * Exists data 85 | ~~~go 86 | var res bool = array.New(arrData).Exists("b.kJh21ay.Hjk2") 87 | // output: true 88 | 89 | var res bool = array.New(arrData).Exists("b.kJh21ay.Hjk12") 90 | // output: false 91 | ~~~ 92 | 93 | * Get data 94 | ~~~go 95 | var res any = array.New(arrData).Get("b.kJh21ay.Hjk2") 96 | // output: fccDcc 97 | 98 | var res any = array.New(arrData).Get("b.kJh21ay.Hjk12", "defVal") 99 | // output: defVal 100 | ~~~ 101 | 102 | * Find data 103 | ~~~go 104 | var res any = array.New(arrData).Find("b.kJh21ay.Hjk2") 105 | // output: fccDcc 106 | 107 | var res any = array.New(arrData).Find("b.kJh21ay.Hjk12") 108 | // output: nil 109 | ~~~ 110 | 111 | * Use Sub to Find data 112 | ~~~go 113 | var res any = array.New(arrData).Sub("b.kJh21ay.Hjk2").Value() 114 | // output: fccDcc 115 | 116 | var res any = array.New(arrData).Sub("b.kJh21ay.Hjk12").Value() 117 | // output: nil 118 | ~~~ 119 | 120 | * Use Search to Find data 121 | ~~~go 122 | var res any = array.New(arrData).Search("b", "kJh21ay", "Hjk2").Value() 123 | // output: fccDcc 124 | 125 | var res any = array.New(arrData).Search("b", "kJh21ay", "Hjk12").Value() 126 | // output: nil 127 | ~~~ 128 | 129 | * Use Index to Find data 130 | ~~~go 131 | var res any = array.New(arrData).Sub("b.dd").Index(1).Value() 132 | // output: ddddd 133 | 134 | var res any = array.New(arrData).Sub("b.dd").Index(6).Value() 135 | // output: nil 136 | ~~~ 137 | 138 | * Use Set to set data 139 | ~~~go 140 | arr, err := array.New(arrData).Set("qqqyyy", "b", "ff", 222) 141 | // arr.Get("b.ff.222") output: qqqyyy 142 | ~~~ 143 | 144 | * Use SetIndex to set data 145 | ~~~go 146 | arr, err := array.New(arrData).Sub("b.dd").SetIndex("qqqyyySetIndex", 1) 147 | // arr.Get("b.dd.1") output: qqqyyySetIndex 148 | ~~~ 149 | 150 | * Use Delete to delete data 151 | ~~~go 152 | arr, err := array.New(arrData).Delete("b", "hh", 2225) 153 | // arr.Get("b.hh.2225") output: nil 154 | ~~~ 155 | 156 | * Use DeleteKey to delete data 157 | ~~~go 158 | arr, err := array.New(arrData).DeleteKey("b.d.e") 159 | // arr.Get("b.d.e") output: nil 160 | ~~~ 161 | 162 | 163 | ### LICENSE 164 | 165 | * The library LICENSE is `Apache2`, using the library need keep the LICENSE. 166 | 167 | 168 | ### Copyright 169 | 170 | * Copyright deatil(https://github.com/deatil). 171 | -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | ## go-array 2 | 3 |

4 | Go Reference 5 | 6 | 7 | Mentioned in Awesome Go 8 |

9 | 10 |

11 | go-array 可以快速的从 map, slice 及 json 中获取数据或者设置数据 12 |

13 | 14 | 中文 | [English](README.md) 15 | 16 | 17 | ### 下载安装 18 | 19 | ~~~go 20 | go get -u github.com/deatil/go-array 21 | ~~~ 22 | 23 | 24 | ### 使用 25 | 26 | ~~~go 27 | import "github.com/deatil/go-array/array" 28 | 29 | arrData := map[string]any{ 30 | "a": 123, 31 | "b": map[string]any{ 32 | "c": "ccc", 33 | "d": map[string]any{ 34 | "e": "eee", 35 | "f": map[string]any{ 36 | "g": "ggg", 37 | }, 38 | }, 39 | "dd": []any{ 40 | "ccccc", 41 | "ddddd", 42 | "fffff", 43 | }, 44 | "ff": map[any]any{ 45 | 111: "fccccc", 46 | 222: "fddddd", 47 | 333: "dfffff", 48 | }, 49 | "hh": map[int]any{ 50 | 1115: "hccccc", 51 | 2225: "hddddd", 52 | 3335: map[any]string{ 53 | "qq1": "qq1ccccc", 54 | "qq2": "qq2ddddd", 55 | "qq3": "qq3fffff", 56 | }, 57 | }, 58 | "kJh21ay": map[string]any{ 59 | "Hjk2": "fccDcc", 60 | "23rt": "^hgcF5c", 61 | }, 62 | }, 63 | } 64 | 65 | data := array.Get(arrData, "b.d.e") 66 | // output: eee 67 | 68 | data := array.Get(arrData, "b.dd.1") 69 | // output: ddddd 70 | 71 | data := array.Get(arrData, "b.hh.3335.qq2") 72 | // output: qq2ddddd 73 | 74 | data := array.Get(arrData, "b.kJh21ay.Hjk2", "defValString") 75 | // output: fccDcc 76 | 77 | data := array.Get(arrData, "b.kJh21ay.Hjk23333", "defValString") 78 | // output: defValString 79 | ~~~ 80 | 81 | 82 | ### 常用示例 83 | 84 | * 判断是否存在 85 | ~~~go 86 | var res bool = array.New(arrData).Exists("b.kJh21ay.Hjk2") 87 | // output: true 88 | 89 | var res bool = array.New(arrData).Exists("b.kJh21ay.Hjk12") 90 | // output: false 91 | ~~~ 92 | 93 | * 获取数据 94 | ~~~go 95 | var res any = array.New(arrData).Get("b.kJh21ay.Hjk2") 96 | // output: fccDcc 97 | 98 | var res any = array.New(arrData).Get("b.kJh21ay.Hjk12", "defVal") 99 | // output: defVal 100 | ~~~ 101 | 102 | * 查找数据 103 | ~~~go 104 | var res any = array.New(arrData).Find("b.kJh21ay.Hjk2") 105 | // output: fccDcc 106 | 107 | var res any = array.New(arrData).Find("b.kJh21ay.Hjk12") 108 | // output: nil 109 | ~~~ 110 | 111 | * 用 Sub 获取数据 112 | ~~~go 113 | var res any = array.New(arrData).Sub("b.kJh21ay.Hjk2").Value() 114 | // output: fccDcc 115 | 116 | var res any = array.New(arrData).Sub("b.kJh21ay.Hjk12").Value() 117 | // output: nil 118 | ~~~ 119 | 120 | * 用 Search 获取数据 121 | ~~~go 122 | var res any = array.New(arrData).Search("b", "kJh21ay", "Hjk2").Value() 123 | // output: fccDcc 124 | 125 | var res any = array.New(arrData).Search("b", "kJh21ay", "Hjk12").Value() 126 | // output: nil 127 | ~~~ 128 | 129 | * 用 Index 获取数据 130 | ~~~go 131 | var res any = array.New(arrData).Sub("b.dd").Index(1).Value() 132 | // output: ddddd 133 | 134 | var res any = array.New(arrData).Sub("b.dd").Index(6).Value() 135 | // output: nil 136 | ~~~ 137 | 138 | * 用 Set 设置数据 139 | ~~~go 140 | arr, err := array.New(arrData).Set("qqqyyy", "b", "ff", 222) 141 | // arr.Get("b.ff.222") output: qqqyyy 142 | ~~~ 143 | 144 | * 用 SetIndex 设置数据 145 | ~~~go 146 | arr, err := array.New(arrData).Sub("b.dd").SetIndex("qqqyyySetIndex", 1) 147 | // arr.Get("b.dd.1") output: qqqyyySetIndex 148 | ~~~ 149 | 150 | * 用 Delete 删除数据 151 | ~~~go 152 | arr, err := array.New(arrData).Delete("b", "hh", 2225) 153 | // arr.Get("b.hh.2225") output: nil 154 | ~~~ 155 | 156 | * 用 DeleteKey 删除数据 157 | ~~~go 158 | arr, err := array.New(arrData).DeleteKey("b.d.e") 159 | // arr.Get("b.d.e") output: nil 160 | ~~~ 161 | 162 | 163 | ### 开源协议 164 | 165 | * 本软件包遵循 `Apache2` 开源协议发布,在保留本软件包版权的情况下提供个人及商业免费使用。 166 | 167 | 168 | ### 版权 169 | 170 | * 本软件包所属版权归 deatil(https://github.com/deatil) 所有。 171 | -------------------------------------------------------------------------------- /array/array.go: -------------------------------------------------------------------------------- 1 | package array 2 | 3 | import ( 4 | "encoding/json" 5 | "errors" 6 | "fmt" 7 | "reflect" 8 | "strconv" 9 | "strings" 10 | ) 11 | 12 | var ( 13 | r1 *strings.Replacer 14 | r2 *strings.Replacer 15 | ) 16 | 17 | func init() { 18 | r1 = strings.NewReplacer("~1", "/", "~0", "~") 19 | r2 = strings.NewReplacer("~1", ".", "~0", "~") 20 | } 21 | 22 | // format JSONPointer to Slice 23 | func JSONPointerToSlice(path string) ([]string, error) { 24 | if path == "" { 25 | return nil, nil 26 | } 27 | 28 | if path[0] != '/' { 29 | return nil, errors.New("failed to resolve JSON pointer: path must begin with '/'") 30 | } 31 | 32 | if path == "/" { 33 | return []string{""}, nil 34 | } 35 | 36 | hierarchy := strings.Split(path, "/")[1:] 37 | for i, v := range hierarchy { 38 | hierarchy[i] = r1.Replace(v) 39 | } 40 | 41 | return hierarchy, nil 42 | } 43 | 44 | // format Path with KeyDelim to Slice 45 | func KeyDelimPathToSlice(path, keyDelim string) []string { 46 | hierarchy := strings.Split(path, keyDelim) 47 | for i, v := range hierarchy { 48 | hierarchy[i] = r2.Replace(v) 49 | } 50 | 51 | return hierarchy 52 | } 53 | 54 | var ( 55 | ErrOutOfBounds = errors.New("out of bounds") 56 | ) 57 | 58 | /** 59 | * 获取数组数据 / array struct 60 | * 61 | * @create 2022-5-3 62 | * @author deatil 63 | */ 64 | type Array struct { 65 | // 分隔符 / key Delim 66 | keyDelim string 67 | 68 | // 原始数据 / source data 69 | source any 70 | } 71 | 72 | // New Array 73 | func New(source any) *Array { 74 | return &Array{ 75 | keyDelim: ".", 76 | source: source, 77 | } 78 | } 79 | 80 | // 解析 JSON 数据 81 | // parse json data 82 | func ParseJSON(source []byte) (*Array, error) { 83 | var dst any 84 | err := json.Unmarshal(source, &dst) 85 | 86 | return New(dst), err 87 | } 88 | 89 | // ParseJSONDecoder applies a json.Decoder to a *Container. 90 | func ParseJSONDecoder(decoder *json.Decoder) (*Array, error) { 91 | var dst any 92 | if err := decoder.Decode(&dst); err != nil { 93 | return nil, err 94 | } 95 | 96 | return New(dst), nil 97 | } 98 | 99 | // 设置 keyDelim 100 | // set keyDelim string 101 | func (this *Array) WithKeyDelim(data string) *Array { 102 | this.keyDelim = data 103 | 104 | return this 105 | } 106 | 107 | // String marshals an element to a JSON formatted string. 108 | func (this *Array) String() string { 109 | return string(this.ToJSON()) 110 | } 111 | 112 | // 返回数据 113 | // return the source data 114 | func (this *Array) Value() any { 115 | return this.source 116 | } 117 | 118 | // 返回 JSON 数据 119 | // return JSON data 120 | func (this *Array) ToJSON() []byte { 121 | if data, err := json.Marshal(this.anyDataFormat(this.source)); err == nil { 122 | return data 123 | } 124 | 125 | return []byte("null") 126 | } 127 | 128 | // BytesIndent marshals an element to a JSON []byte blob formatted with a prefix 129 | // and indent string. 130 | func (this *Array) ToJSONIndent(prefix, indent string) []byte { 131 | if this.source != nil { 132 | if data, err := json.MarshalIndent(this.anyDataFormat(this.source), prefix, indent); err == nil { 133 | return data 134 | } 135 | } 136 | 137 | return []byte("null") 138 | } 139 | 140 | // 返回 JSON 数据 141 | // return JSON data 142 | func (this *Array) MarshalJSON() ([]byte, error) { 143 | return json.Marshal(this.anyDataFormat(this.source)) 144 | } 145 | 146 | // 判断是否存在 147 | // if key in source return true or false 148 | func (this *Array) Exists(key string) bool { 149 | if this.Find(key) != nil { 150 | return true 151 | } 152 | 153 | return false 154 | } 155 | 156 | // 判断是否存在 157 | // if key in source return true or false 158 | func Exists(source any, key string) bool { 159 | return New(source).Exists(key) 160 | } 161 | 162 | // 获取数据 163 | // get data with key and can set default value 164 | func (this *Array) Get(key string, defVal ...any) any { 165 | data := this.Find(key) 166 | if data != nil { 167 | return data 168 | } 169 | 170 | if len(defVal) > 0 { 171 | return defVal[0] 172 | } 173 | 174 | return nil 175 | } 176 | 177 | // 获取数据 178 | // get data with key from source and can set default value 179 | func Get(source any, key string, defVal ...any) any { 180 | return New(source).Get(key, defVal...) 181 | } 182 | 183 | // 查找数据 184 | // find data with key 185 | func (this *Array) Find(key string) any { 186 | return this.Sub(key).Value() 187 | } 188 | 189 | // 查找数据 190 | // find data with key from source 191 | func Find(source any, key string) any { 192 | return New(source).Find(key) 193 | } 194 | 195 | // 获取数据 196 | // get data and return Array 197 | func (this *Array) JSONPointer(path string) (*Array, error) { 198 | paths, err := JSONPointerToSlice(path) 199 | if err != nil { 200 | return nil, err 201 | } 202 | 203 | return this.Search(paths...), nil 204 | } 205 | 206 | // 获取数据 207 | // get data and return Array 208 | func JSONPointer(source any, path string) (*Array, error) { 209 | return New(source).JSONPointer(path) 210 | } 211 | 212 | // 获取数据 213 | // get data and return Array 214 | func (this *Array) Sub(key string) *Array { 215 | path := KeyDelimPathToSlice(key, this.keyDelim) 216 | 217 | return this.Search(path...) 218 | } 219 | 220 | // 获取数据 221 | // get data and return Array 222 | func Sub(source any, key string) *Array { 223 | return New(source).Sub(key) 224 | } 225 | 226 | // 搜索数据 227 | // Search data with key 228 | func (this *Array) Search(path ...string) *Array { 229 | source := this.search(this.source, path...) 230 | 231 | return &Array{ 232 | keyDelim: this.keyDelim, 233 | source: source, 234 | } 235 | } 236 | 237 | // 搜索数据 238 | // Search data with key from source 239 | func Search(source any, path ...string) *Array { 240 | return New(source).Search(path...) 241 | } 242 | 243 | // 使用 index 搜索数据 244 | // Index attempts to find and return an element 245 | func (this *Array) Index(index int) *Array { 246 | if array, ok := this.Value().([]any); ok { 247 | if index >= len(array) { 248 | return &Array{ 249 | keyDelim: this.keyDelim, 250 | source: nil, 251 | } 252 | } 253 | 254 | source := array[index] 255 | 256 | return &Array{ 257 | keyDelim: this.keyDelim, 258 | source: source, 259 | } 260 | } 261 | 262 | // 反射返回 263 | sourceValue := reflect.ValueOf(this.Value()) 264 | for sourceValue.Kind() == reflect.Ptr { 265 | sourceValue = sourceValue.Elem() 266 | } 267 | 268 | if sourceValue.Kind() == reflect.Slice { 269 | if index >= sourceValue.Len() { 270 | return &Array{ 271 | keyDelim: this.keyDelim, 272 | source: nil, 273 | } 274 | } 275 | 276 | source := sourceValue.Index(index).Interface() 277 | 278 | return &Array{ 279 | keyDelim: this.keyDelim, 280 | source: source, 281 | } 282 | } 283 | 284 | return &Array{ 285 | keyDelim: this.keyDelim, 286 | source: nil, 287 | } 288 | } 289 | 290 | // 返回 slice 数据 291 | // Children returns a slice of all children of an array element. This also works 292 | // for objects, however, the children returned for an source will be in a random 293 | // order and you lose the names of the returned objects this way. If the 294 | // underlying container value isn't an array or map nil is returned. 295 | func (this *Array) Children() []*Array { 296 | source := this.anyDataFormat(this.source) 297 | 298 | if array, ok := source.([]any); ok { 299 | children := make([]*Array, len(array)) 300 | for i := 0; i < len(array); i++ { 301 | children[i] = &Array{ 302 | keyDelim: this.keyDelim, 303 | source: array[i], 304 | } 305 | } 306 | 307 | return children 308 | } 309 | 310 | if mmap, ok := source.(map[string]any); ok { 311 | children := make([]*Array, 0, len(mmap)) 312 | for _, obj := range mmap { 313 | children = append(children, &Array{ 314 | keyDelim: this.keyDelim, 315 | source: obj, 316 | }) 317 | } 318 | 319 | return children 320 | } 321 | 322 | return nil 323 | } 324 | 325 | // 返回 map 数据 326 | // ChildrenMap returns a map of all the children of an source element. IF the 327 | // underlying value isn't a source then an empty map is returned. 328 | func (this *Array) ChildrenMap() map[string]*Array { 329 | source := this.anyDataFormat(this.source) 330 | 331 | if mmap, ok := source.(map[string]any); ok { 332 | children := make(map[string]*Array, len(mmap)) 333 | for name, obj := range mmap { 334 | children[name] = &Array{ 335 | keyDelim: this.keyDelim, 336 | source: obj, 337 | } 338 | } 339 | 340 | return children 341 | } 342 | 343 | return map[string]*Array{} 344 | } 345 | 346 | // 设置数据 347 | // set data with key 348 | func (this *Array) SetKey(value any, key string) (*Array, error) { 349 | path := KeyDelimPathToSlice(key, this.keyDelim) 350 | 351 | return this.Set(value, formatPath(path)...) 352 | } 353 | 354 | // 设置数据 355 | // set data with path 356 | func (this *Array) Set(value any, path ...any) (*Array, error) { 357 | if len(path) == 0 { 358 | this.source = value 359 | return this, nil 360 | } 361 | 362 | if this.source == nil { 363 | this.source = map[string]any{} 364 | } 365 | 366 | source := this.source 367 | 368 | for target := 0; target < len(path); target++ { 369 | pathSeg := toString(path[target]) 370 | 371 | switch typedObj := source.(type) { 372 | case map[string]any: 373 | if target == len(path)-1 { 374 | source = value 375 | typedObj[pathSeg] = source 376 | } else if source = typedObj[pathSeg]; source == nil { 377 | typedObj[pathSeg] = map[string]any{} 378 | source = typedObj[pathSeg] 379 | } 380 | case []any: 381 | if pathSeg == "-" { 382 | if target < 1 { 383 | return nil, errors.New("unable to append new array index at root of path") 384 | } 385 | 386 | if target == len(path)-1 { 387 | source = value 388 | } else { 389 | source = map[string]any{} 390 | } 391 | 392 | typedObj = append(typedObj, source) 393 | if _, err := this.Set(typedObj, path[:target]...); err != nil { 394 | return nil, err 395 | } 396 | } else { 397 | index, err := strconv.Atoi(pathSeg) 398 | if err != nil { 399 | return nil, fmt.Errorf("failed to resolve path segment '%v': found array but segment value '%v' could not be parsed into array index: %v", target, pathSeg, err) 400 | } 401 | 402 | if index < 0 { 403 | return nil, fmt.Errorf("failed to resolve path segment '%v': found array but index '%v' is invalid", target, pathSeg) 404 | } 405 | 406 | if len(typedObj) <= index { 407 | return nil, fmt.Errorf("failed to resolve path segment '%v': found array but index '%v' exceeded target array size of '%v'", target, pathSeg, len(typedObj)) 408 | } 409 | 410 | if target == len(path)-1 { 411 | source = value 412 | typedObj[index] = source 413 | } else if source = typedObj[index]; source == nil { 414 | return nil, fmt.Errorf("failed to resolve path segment '%v': field '%v' was not found", target, pathSeg) 415 | } 416 | } 417 | default: 418 | sourceValue := reflect.ValueOf(source) 419 | for sourceValue.Kind() == reflect.Ptr { 420 | sourceValue = sourceValue.Elem() 421 | } 422 | 423 | switch { 424 | case sourceValue.Kind() == reflect.Map: 425 | sourceType := sourceValue.Type() 426 | 427 | pathSegValue, ok := this.convertTo(sourceType.Key(), path[target]) 428 | if !ok { 429 | return nil, fmt.Errorf("convert failed to resolve path segment '%v': field '%v' was error", target, pathSeg) 430 | } 431 | 432 | if target == len(path)-1 { 433 | valueValue, ok := this.convertTo(sourceType.Elem(), value) 434 | if !ok { 435 | return nil, fmt.Errorf("map failed to resolve path segment '%v': field '%v' was error", target, pathSeg) 436 | } 437 | 438 | sourceValue.SetMapIndex(pathSegValue, valueValue) 439 | 440 | source = valueValue.Interface() 441 | } else if source = sourceValue.MapIndex(pathSegValue).Interface(); source == nil { 442 | valueValue := reflect.New(sourceType.Elem()) 443 | 444 | sourceValue.SetMapIndex(pathSegValue, valueValue) 445 | 446 | source = valueValue.Interface() 447 | } 448 | case sourceValue.Kind() == reflect.Slice: 449 | if pathSeg == "-" { 450 | if target < 1 { 451 | return nil, errors.New("unable to append new array index at root of path") 452 | } 453 | 454 | if target == len(path)-1 { 455 | source = value 456 | } else { 457 | source = map[string]any{} 458 | } 459 | 460 | valueValue, ok := this.convertTo(sourceValue.Type().Elem(), source) 461 | if !ok { 462 | return nil, fmt.Errorf("slice failed to resolve path segment '%v': field '%v' was error", target, pathSeg) 463 | } 464 | 465 | sourceValue = reflect.AppendSlice(sourceValue, valueValue) 466 | 467 | if _, err := this.Set(sourceValue, path[:target]...); err != nil { 468 | return nil, err 469 | } 470 | } else { 471 | index, err := strconv.Atoi(pathSeg) 472 | if err != nil { 473 | return nil, fmt.Errorf("slice failed to resolve path segment '%v': found array but segment value '%v' could not be parsed into array index: %v", target, pathSeg, err) 474 | } 475 | 476 | if index < 0 { 477 | return nil, fmt.Errorf("slice failed to resolve path segment '%v': found array but index '%v' is invalid", target, pathSeg) 478 | } 479 | 480 | if sourceValue.Len() <= index { 481 | return nil, fmt.Errorf("slice failed to resolve path segment '%v': found array but index '%v' exceeded target array size of '%v'", target, pathSeg, sourceValue.Len()) 482 | } 483 | 484 | if target == len(path)-1 { 485 | source = value 486 | 487 | valueValue, ok := this.convertTo(sourceValue.Index(index).Type(), source) 488 | if !ok { 489 | return nil, fmt.Errorf("slice failed to resolve path segment '%v': field '%v' was error", target, pathSeg) 490 | } 491 | 492 | sourceValue.Index(index).Set(valueValue) 493 | } else if source = sourceValue.Index(index).Interface(); source == nil { 494 | return nil, fmt.Errorf("slice failed to resolve path segment '%v': field '%v' was not found", target, pathSeg) 495 | } 496 | } 497 | default: 498 | return nil, errors.New("encountered value collision whilst building path") 499 | } 500 | } 501 | } 502 | 503 | return &Array{ 504 | keyDelim: this.keyDelim, 505 | source: source, 506 | }, nil 507 | } 508 | 509 | // SetIndex attempts to set a value of an array element based on an index. 510 | func (this *Array) SetIndex(value any, index int) (*Array, error) { 511 | if array, ok := this.Value().([]any); ok { 512 | if index >= len(array) { 513 | return nil, ErrOutOfBounds 514 | } 515 | 516 | array[index] = value 517 | 518 | return &Array{ 519 | keyDelim: this.keyDelim, 520 | source: array[index], 521 | }, nil 522 | } 523 | 524 | // 反射设置 525 | sourceValue := reflect.ValueOf(this.Value()) 526 | for sourceValue.Kind() == reflect.Ptr { 527 | sourceValue = sourceValue.Elem() 528 | } 529 | 530 | if sourceValue.Kind() == reflect.Slice { 531 | if index >= sourceValue.Len() { 532 | return nil, ErrOutOfBounds 533 | } 534 | 535 | valueValue, ok := this.convertTo(sourceValue.Index(index).Type(), value) 536 | if !ok { 537 | return nil, fmt.Errorf("failed: field '%v' was error", value) 538 | } 539 | 540 | sourceValue.Index(index).Set(valueValue) 541 | 542 | return &Array{ 543 | keyDelim: this.keyDelim, 544 | source: sourceValue.Interface(), 545 | }, nil 546 | } 547 | 548 | return nil, errors.New("not an array") 549 | } 550 | 551 | // ArrayOfSize creates a new array of a particular size at a path. Returns 552 | // an error if the path contains a collision with a non object type. 553 | func (this *Array) ArrayOfSize(size int, path ...any) (*Array, error) { 554 | a := make([]any, size) 555 | return this.Set(a, path...) 556 | } 557 | 558 | // ArrayOfSizeIndex creates a new array of a particular size at a index. Returns 559 | // an error if the path contains a collision with a non object type. 560 | func (this *Array) ArrayOfSizeIndex(size, index int) (*Array, error) { 561 | a := make([]any, size) 562 | return this.SetIndex(a, index) 563 | } 564 | 565 | // ArrayOfSizeIndex creates a new array of a particular size at a key. Returns 566 | // an error if the path contains a collision with a non object type. 567 | func (this *Array) ArrayOfSizeKey(size int, key string) (*Array, error) { 568 | path := KeyDelimPathToSlice(key, this.keyDelim) 569 | 570 | return this.ArrayOfSize(size, formatPath(path)...) 571 | } 572 | 573 | // 使用 key 删除数据 574 | // delete data with key 575 | func (this *Array) DeleteKey(key string) error { 576 | path := KeyDelimPathToSlice(key, this.keyDelim) 577 | 578 | return this.Delete(formatPath(path)...) 579 | } 580 | 581 | // 使用路径删除数据 582 | // delete data with path 583 | func (this *Array) Delete(path ...any) error { 584 | if this == nil || this.source == nil { 585 | return errors.New("source is nil") 586 | } 587 | 588 | if len(path) == 0 { 589 | return errors.New("invalid search path") 590 | } 591 | 592 | source := this.source 593 | 594 | target := toString(path[len(path)-1]) 595 | if len(path) > 1 { 596 | source = this.Search(formatPathString(path[:len(path)-1])...).Value() 597 | } 598 | 599 | if obj, ok := source.(map[string]any); ok { 600 | if _, ok = obj[target]; !ok { 601 | return errors.New("field not found") 602 | } 603 | 604 | delete(obj, target) 605 | 606 | this.Set(obj, path[:len(path)-1]...) 607 | return nil 608 | } 609 | 610 | if array, ok := source.([]any); ok { 611 | if len(path) < 2 { 612 | return errors.New("unable to delete array index at root of path") 613 | } 614 | 615 | index, err := strconv.Atoi(target) 616 | if err != nil { 617 | return fmt.Errorf("failed to parse array index '%v': %v", target, err) 618 | } 619 | 620 | if index >= len(array) { 621 | return ErrOutOfBounds 622 | } 623 | if index < 0 { 624 | return ErrOutOfBounds 625 | } 626 | 627 | array = append(array[:index], array[index+1:]...) 628 | this.Set(array, path[:len(path)-1]...) 629 | 630 | return nil 631 | } 632 | 633 | // 通用删除 634 | sourceValue := reflect.ValueOf(source) 635 | for sourceValue.Kind() == reflect.Ptr { 636 | sourceValue = sourceValue.Elem() 637 | } 638 | 639 | var dstValue reflect.Value 640 | 641 | if sourceValue.Kind() == reflect.Map { 642 | hasKey := false 643 | 644 | dstValue = reflect.MakeMap(sourceValue.Type()) 645 | 646 | iter := sourceValue.MapRange() 647 | for iter.Next() { 648 | k := iter.Key().Interface() 649 | 650 | if toString(k) != target { 651 | dstValue.SetMapIndex(iter.Key(), iter.Value()) 652 | } else { 653 | hasKey = true 654 | } 655 | } 656 | 657 | if !hasKey { 658 | return errors.New("field not found") 659 | } 660 | 661 | this.Set(dstValue.Interface(), path[:len(path)-1]...) 662 | return nil 663 | } 664 | 665 | if sourceValue.Kind() == reflect.Slice { 666 | dstValue = reflect.MakeSlice(sourceValue.Type(), len(path)-1, len(path)-1) 667 | 668 | if len(path) < 2 { 669 | return errors.New("unable to delete array index at root of path") 670 | } 671 | 672 | index, err := strconv.Atoi(target) 673 | if err != nil { 674 | return fmt.Errorf("failed to parse array index '%v': %v", target, err) 675 | } 676 | 677 | if index >= sourceValue.Len() { 678 | return ErrOutOfBounds 679 | } 680 | if index < 0 { 681 | return ErrOutOfBounds 682 | } 683 | 684 | dstValue = reflect.AppendSlice(sourceValue.Slice(0, index), sourceValue.Slice(index+1, sourceValue.Len())) 685 | 686 | this.Set(dstValue.Interface(), path[:len(path)-1]...) 687 | return nil 688 | } 689 | 690 | return errors.New("source is error") 691 | } 692 | 693 | // Flatten a array or slice into an source of key/value pairs for each 694 | // field, where the key is the full path of the structured field in dot path 695 | // notation matching the spec for the method Path. 696 | func (this *Array) Flatten() (map[string]any, error) { 697 | return this.flatten(false) 698 | } 699 | 700 | // FlattenIncludeEmpty a array or slice into an source of key/value pairs 701 | // for each field, just as Flatten, but includes empty arrays and objects, where 702 | // the key is the full path of the structured field in dot path notation matching 703 | // the spec for the method Path. 704 | func (this *Array) FlattenIncludeEmpty() (map[string]any, error) { 705 | return this.flatten(true) 706 | } 707 | 708 | func (this *Array) flatten(includeEmpty bool) (map[string]any, error) { 709 | flattened := map[string]any{} 710 | 711 | source := this.anyDataFormat(this.source) 712 | 713 | switch t := source.(type) { 714 | case map[string]any: 715 | this.walkObject("", t, flattened, includeEmpty) 716 | case []any: 717 | this.walkArray("", t, flattened, includeEmpty) 718 | default: 719 | return nil, errors.New("not a map or slice") 720 | } 721 | 722 | return flattened, nil 723 | } 724 | 725 | // 获取 array, slice, map or string 的长度 726 | // get array, slice, map or string len 727 | func (this *Array) Len() (num int) { 728 | defer func() { 729 | if err := recover(); err != nil { 730 | num = 0 731 | } 732 | }() 733 | 734 | return reflect.ValueOf(this.source).Len() 735 | } 736 | 737 | // 判断是否是 slice 类型 738 | // if the source is slice and return true 739 | func (this *Array) IsSlice() bool { 740 | kind := reflect.TypeOf(this.source).Kind() 741 | 742 | return kind == reflect.Slice || kind == reflect.Array 743 | } 744 | 745 | // 判断是否是 map 类型 746 | // if the source is map and return true 747 | func (this *Array) IsMap() bool { 748 | kind := reflect.TypeOf(this.source).Kind() 749 | 750 | return kind == reflect.Map 751 | } 752 | 753 | // 判断是否是 slice or map 类型 754 | // if the source is slice or map and return true 755 | func (this *Array) IsArray() bool { 756 | return this.IsSlice() || this.IsMap() 757 | } 758 | 759 | // 搜索 760 | // Search data with key from source 761 | func (this *Array) search(source any, path ...string) any { 762 | var val any 763 | 764 | newSource, isMap := this.anyDataMapFormat(source) 765 | if isMap { 766 | // map 767 | val = this.searchMap(newSource, path) 768 | if val != nil { 769 | return val 770 | } 771 | } 772 | 773 | // 格式化 774 | source = this.anyDataFormat(source) 775 | 776 | // 索引 777 | val = this.searchIndexWithPathPrefixes(source, path) 778 | if val != nil { 779 | return val 780 | } 781 | 782 | return nil 783 | } 784 | 785 | // 数组 786 | // searchMap 787 | func (this *Array) searchMap(source map[string]any, path []string) any { 788 | if len(path) == 0 { 789 | return source 790 | } 791 | 792 | next, ok := source[path[0]] 793 | if !ok { 794 | return nil 795 | } 796 | 797 | if len(path) == 1 { 798 | return next 799 | } 800 | 801 | switch n := next.(type) { 802 | case map[any]any: 803 | return this.searchMap(toStringMap(n), path[1:]) 804 | case map[string]any: 805 | return this.searchMap(n, path[1:]) 806 | default: 807 | if nextMap, isMap := this.anyMapFormat(next); isMap { 808 | return this.searchMap(toStringMap(nextMap), path[1:]) 809 | } 810 | } 811 | 812 | return nil 813 | } 814 | 815 | // 索引查询 816 | // searchIndexWithPathPrefixes 817 | func (this *Array) searchIndexWithPathPrefixes(source any, path []string) any { 818 | if len(path) == 0 { 819 | return source 820 | } 821 | 822 | for i := len(path); i > 0; i-- { 823 | prefixKey := strings.Join(path[0:i], this.keyDelim) 824 | 825 | var val any 826 | switch sourceIndexable := source.(type) { 827 | case []any: 828 | val = this.searchSliceWithPathPrefixes(sourceIndexable, prefixKey, i, path) 829 | case map[string]any: 830 | val = this.searchMapWithPathPrefixes(sourceIndexable, prefixKey, i, path) 831 | } 832 | 833 | if val != nil { 834 | return val 835 | } 836 | } 837 | 838 | return nil 839 | } 840 | 841 | // 切片 842 | // searchSliceWithPathPrefixes 843 | func (this *Array) searchSliceWithPathPrefixes( 844 | sourceSlice []any, 845 | prefixKey string, 846 | pathIndex int, 847 | path []string, 848 | ) any { 849 | index, err := strconv.Atoi(prefixKey) 850 | if err != nil || len(sourceSlice) <= index { 851 | return nil 852 | } 853 | 854 | next := sourceSlice[index] 855 | 856 | if pathIndex == len(path) { 857 | return next 858 | } 859 | 860 | n := this.anyDataFormat(next) 861 | if n != nil { 862 | return this.searchIndexWithPathPrefixes(n, path[pathIndex:]) 863 | } 864 | 865 | return nil 866 | } 867 | 868 | // map 数据 869 | // searchMapWithPathPrefixes 870 | func (this *Array) searchMapWithPathPrefixes( 871 | sourceMap map[string]any, 872 | prefixKey string, 873 | pathIndex int, 874 | path []string, 875 | ) any { 876 | next, ok := sourceMap[prefixKey] 877 | if !ok { 878 | return nil 879 | } 880 | 881 | if pathIndex == len(path) { 882 | return next 883 | } 884 | 885 | n := this.anyDataFormat(next) 886 | if n != nil { 887 | return this.searchIndexWithPathPrefixes(n, path[pathIndex:]) 888 | } 889 | 890 | return nil 891 | } 892 | 893 | func (this *Array) isPathShadowedInDeepMap(path []string, m map[string]any) string { 894 | var parentVal any 895 | 896 | for i := 1; i < len(path); i++ { 897 | parentVal = this.searchMap(m, path[0:i]) 898 | if parentVal == nil { 899 | return "" 900 | } 901 | 902 | switch parentVal.(type) { 903 | case map[any]any: 904 | continue 905 | case map[string]any: 906 | continue 907 | default: 908 | parentValKind := reflect.TypeOf(parentVal).Kind() 909 | if parentValKind == reflect.Map { 910 | continue 911 | } 912 | 913 | return strings.Join(path[0:i], this.keyDelim) 914 | } 915 | } 916 | 917 | return "" 918 | } 919 | 920 | // any data 数据格式化 921 | // any data format 922 | func (this *Array) anyDataFormat(data any) any { 923 | if data == nil { 924 | return nil 925 | } 926 | 927 | switch n := data.(type) { 928 | case map[any]any: 929 | return toStringMap(n) 930 | case map[string]any, []any: 931 | return n 932 | default: 933 | dataMap, isMap := this.anyMapFormat(data) 934 | if isMap { 935 | return toStringMap(dataMap) 936 | } 937 | 938 | if dataSlice, isSlice := this.anySliceFormat(data); isSlice { 939 | return dataSlice 940 | } 941 | } 942 | 943 | return nil 944 | } 945 | 946 | // any data map 数据格式化 947 | // any data map format 948 | func (this *Array) anyDataMapFormat(data any) (map[string]any, bool) { 949 | switch n := data.(type) { 950 | case map[any]any: 951 | return toStringMap(n), true 952 | case map[string]any: 953 | return n, true 954 | default: 955 | dataMap, isMap := this.anyMapFormat(data) 956 | if isMap { 957 | return toStringMap(dataMap), true 958 | } 959 | } 960 | 961 | return nil, false 962 | } 963 | 964 | // any map 数据格式化 965 | // any map format 966 | func (this *Array) anyMapFormat(data any) (map[any]any, bool) { 967 | m := make(map[any]any) 968 | isMap := false 969 | 970 | if data == nil { 971 | return m, isMap 972 | } 973 | 974 | dataValue := reflect.ValueOf(data) 975 | for dataValue.Kind() == reflect.Pointer { 976 | dataValue = dataValue.Elem() 977 | } 978 | 979 | // 获取最后的数据 980 | newData := dataValue.Interface() 981 | 982 | newDataKind := reflect.TypeOf(newData).Kind() 983 | if newDataKind == reflect.Map { 984 | iter := reflect.ValueOf(newData).MapRange() 985 | for iter.Next() { 986 | k := iter.Key().Interface() 987 | v := iter.Value().Interface() 988 | 989 | m[k] = v 990 | } 991 | 992 | isMap = true 993 | } 994 | 995 | return m, isMap 996 | } 997 | 998 | // any slice 数据格式化 999 | // any slice format 1000 | func (this *Array) anySliceFormat(data any) ([]any, bool) { 1001 | m := make([]any, 0) 1002 | isSlice := false 1003 | 1004 | if data == nil { 1005 | return m, isSlice 1006 | } 1007 | 1008 | dataValue := reflect.ValueOf(data) 1009 | for dataValue.Kind() == reflect.Pointer { 1010 | dataValue = dataValue.Elem() 1011 | } 1012 | 1013 | // 获取最后的数据 1014 | newData := dataValue.Interface() 1015 | 1016 | newDataKind := reflect.TypeOf(newData).Kind() 1017 | if newDataKind == reflect.Slice || newDataKind == reflect.Array { 1018 | newDataValue := reflect.ValueOf(newData) 1019 | newDataLen := newDataValue.Len() 1020 | 1021 | for i := 0; i < newDataLen; i++ { 1022 | v := newDataValue.Index(i).Interface() 1023 | 1024 | m = append(m, v) 1025 | } 1026 | 1027 | isSlice = true 1028 | } 1029 | 1030 | return m, isSlice 1031 | } 1032 | 1033 | func (this *Array) walkObject(path string, obj, flat map[string]any, includeEmpty bool) { 1034 | if includeEmpty && len(obj) == 0 { 1035 | flat[path] = struct{}{} 1036 | } 1037 | 1038 | for elePath, value := range obj { 1039 | if len(path) > 0 { 1040 | elePath = path + "." + elePath 1041 | } 1042 | 1043 | v := this.anyDataFormat(value) 1044 | 1045 | switch t := v.(type) { 1046 | case map[string]any: 1047 | this.walkObject(elePath, t, flat, includeEmpty) 1048 | case []any: 1049 | this.walkArray(elePath, t, flat, includeEmpty) 1050 | default: 1051 | flat[elePath] = value 1052 | } 1053 | } 1054 | } 1055 | 1056 | func (this *Array) walkArray(path string, arr []any, flat map[string]any, includeEmpty bool) { 1057 | if includeEmpty && len(arr) == 0 { 1058 | flat[path] = []struct{}{} 1059 | } 1060 | 1061 | for i, value := range arr { 1062 | elePath := strconv.Itoa(i) 1063 | if len(path) > 0 { 1064 | elePath = path + "." + elePath 1065 | } 1066 | 1067 | ele := this.anyDataFormat(value) 1068 | 1069 | switch t := ele.(type) { 1070 | case map[string]any: 1071 | this.walkObject(elePath, t, flat, includeEmpty) 1072 | case []any: 1073 | this.walkArray(elePath, t, flat, includeEmpty) 1074 | default: 1075 | flat[elePath] = value 1076 | } 1077 | } 1078 | } 1079 | 1080 | func (this *Array) convertTo(typ reflect.Type, src any) (reflect.Value, bool) { 1081 | if !reflect.ValueOf(src).CanConvert(typ) { 1082 | return reflect.Value{}, false 1083 | } 1084 | 1085 | return reflect.ValueOf(src).Convert(typ), true 1086 | } 1087 | -------------------------------------------------------------------------------- /array/array_test.go: -------------------------------------------------------------------------------- 1 | package array 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "reflect" 8 | "strings" 9 | "testing" 10 | ) 11 | 12 | var ( 13 | arrData = map[string]any{ 14 | "a": 123, 15 | "b": map[string]any{ 16 | "c": "ccc", 17 | "d": map[string]any{ 18 | "e": "eee", 19 | "f": map[string]any{ 20 | "g": "ggg", 21 | }, 22 | }, 23 | "dd": []any{ 24 | "ccccc", 25 | "ddddd", 26 | "fffff", 27 | }, 28 | "ddeef": []any{ 29 | "ccc中文", 30 | "ddddd", 31 | "fffff", 32 | }, 33 | "ddd": []int64{ 34 | 22, 35 | 333, 36 | 555, 37 | }, 38 | "qqq": [10]int64{ 39 | 22, 40 | 333, 41 | 555, 42 | 5557, 43 | 5558, 44 | }, 45 | "ff": map[any]any{ 46 | 111: "fccccc", 47 | 222: "fddddd", 48 | 333: "dfffff", 49 | }, 50 | "hhTy3": &map[int]any{ 51 | 111: "hccccc", 52 | 222: "hddddd", 53 | 333: map[any]string{ 54 | "qq1": "qq1ccccc", 55 | "qq2": "qq2ddddd", 56 | "qq3": "qq3fffff", 57 | }, 58 | 666: []float64{ 59 | 12.3, 60 | 32.5, 61 | 22.56, 62 | 789.156, 63 | }, 64 | }, 65 | "hhTy66": &map[int]any{ 66 | 777: &[]float64{ 67 | 12.3, 68 | 32.5, 69 | 22.56, 70 | 789.156, 71 | }, 72 | }, 73 | "kJh21ay": map[string]any{ 74 | "Hjk2": "fccDcc", 75 | "23rt": "^hgcF5c", 76 | "23rt5": []any{ 77 | "adfa", 78 | 1231, 79 | }, 80 | }, 81 | "kJh21ay22": map[int64]any{ 82 | 33: []any{ 83 | "adfa", 84 | 1231, 85 | }, 86 | }, 87 | }, 88 | } 89 | ) 90 | 91 | func assertT(t *testing.T) func(any, string, string) { 92 | return func(actual any, expected string, msg string) { 93 | actualStr := toString(actual) 94 | if actualStr != expected { 95 | t.Errorf("Failed %s: actual: %v, expected: %v", msg, actualStr, expected) 96 | } 97 | } 98 | } 99 | 100 | func assertDeepEqualT(t *testing.T) func(any, any, string) { 101 | return func(actual any, expected any, msg string) { 102 | if !reflect.DeepEqual(actual, expected) { 103 | t.Errorf("Failed %s: actual: %v, expected: %v", msg, actual, expected) 104 | } 105 | } 106 | } 107 | 108 | func Test_WithKeyDelim(t *testing.T) { 109 | assert := assertT(t) 110 | 111 | testData := []struct { 112 | index string 113 | keyDelim string 114 | check string 115 | }{ 116 | { 117 | "index-1", 118 | "a", 119 | "a", 120 | }, 121 | { 122 | "index-2", 123 | "-", 124 | "-", 125 | }, 126 | } 127 | 128 | for _, v := range testData { 129 | t.Run(v.index, func(t *testing.T) { 130 | arr := New("").WithKeyDelim(v.keyDelim) 131 | 132 | assert(arr.keyDelim, v.check, "WithKeyDelim fail") 133 | }) 134 | } 135 | 136 | } 137 | 138 | func Test_Exists(t *testing.T) { 139 | testData := []struct { 140 | index string 141 | key string 142 | check bool 143 | }{ 144 | { 145 | "index-1", 146 | "a", 147 | true, 148 | }, 149 | { 150 | "index-2", 151 | "b.dd.1", 152 | true, 153 | }, 154 | { 155 | "index-3", 156 | "b.ff.222333", 157 | false, 158 | }, 159 | { 160 | "index-4", 161 | "b.hhTy3.222.yu", 162 | false, 163 | }, 164 | { 165 | "index-5", 166 | "b.hhTy3.333.qq2", 167 | true, 168 | }, 169 | } 170 | 171 | for _, v := range testData { 172 | t.Run(v.index, func(t *testing.T) { 173 | check := New(arrData).Exists(v.key) 174 | if check != v.check { 175 | t.Error("Exists fail") 176 | } 177 | }) 178 | } 179 | 180 | } 181 | 182 | func Test_Exists_func(t *testing.T) { 183 | testData := []struct { 184 | index string 185 | key string 186 | check bool 187 | }{ 188 | { 189 | "index-1", 190 | "a", 191 | true, 192 | }, 193 | { 194 | "index-2", 195 | "b.dd.1", 196 | true, 197 | }, 198 | { 199 | "index-3", 200 | "b.ff.222333", 201 | false, 202 | }, 203 | { 204 | "index-4", 205 | "b.hhTy3.222.yu", 206 | false, 207 | }, 208 | { 209 | "index-5", 210 | "b.hhTy3.333.qq2", 211 | true, 212 | }, 213 | } 214 | 215 | for _, v := range testData { 216 | t.Run(v.index, func(t *testing.T) { 217 | check := Exists(arrData, v.key) 218 | if check != v.check { 219 | t.Error("Exists func fail") 220 | } 221 | }) 222 | } 223 | 224 | } 225 | 226 | func Test_Get(t *testing.T) { 227 | assert := assertT(t) 228 | 229 | testData := []struct { 230 | key string 231 | expected string 232 | def string 233 | msg string 234 | }{ 235 | { 236 | "a", 237 | "123", 238 | "", 239 | "map[string]any", 240 | }, 241 | { 242 | "b.dd.1", 243 | "ddddd", 244 | "", 245 | "[]any", 246 | }, 247 | { 248 | "b.ff.222", 249 | "fddddd", 250 | "", 251 | "map[any]any", 252 | }, 253 | { 254 | "b.hhTy3.222", 255 | "hddddd", 256 | "", 257 | "&map[int]any", 258 | }, 259 | { 260 | "b.hhTy3.333.qq2", 261 | "qq2ddddd", 262 | "", 263 | "map[any]string", 264 | }, 265 | { 266 | "b.hhTy3.666.3", 267 | "789.156", 268 | "", 269 | "Slice", 270 | }, 271 | { 272 | "b.hhTy3.666.9999999", 273 | "222555", 274 | "222555", 275 | "default", 276 | }, 277 | } 278 | 279 | for _, v := range testData { 280 | check := New(arrData).Get(v.key, v.def) 281 | 282 | assert(check, v.expected, v.msg) 283 | } 284 | 285 | check2 := New(arrData).Get("b.hhTy3.666.65555") 286 | assert(check2, "", "nil") 287 | } 288 | 289 | func Test_Get_func(t *testing.T) { 290 | assert := assertT(t) 291 | 292 | testData := []struct { 293 | key string 294 | expected string 295 | def string 296 | msg string 297 | }{ 298 | { 299 | "a", 300 | "123", 301 | "", 302 | "map[string]any", 303 | }, 304 | { 305 | "b.dd.1", 306 | "ddddd", 307 | "", 308 | "[]any", 309 | }, 310 | { 311 | "b.ff.222", 312 | "fddddd", 313 | "", 314 | "map[any]any", 315 | }, 316 | { 317 | "b.hhTy3.222", 318 | "hddddd", 319 | "", 320 | "&map[int]any", 321 | }, 322 | { 323 | "b.hhTy3.333.qq2", 324 | "qq2ddddd", 325 | "", 326 | "map[any]string", 327 | }, 328 | { 329 | "b.hhTy3.666.3", 330 | "789.156", 331 | "", 332 | "Slice", 333 | }, 334 | { 335 | "b.hhTy3.666.9999999", 336 | "222555", 337 | "222555", 338 | "default", 339 | }, 340 | } 341 | 342 | for _, v := range testData { 343 | check := Get(arrData, v.key, v.def) 344 | 345 | assert(check, v.expected, v.msg) 346 | } 347 | 348 | } 349 | 350 | func Test_Find(t *testing.T) { 351 | assert := assertT(t) 352 | 353 | testData := []struct { 354 | key string 355 | expected string 356 | msg string 357 | }{ 358 | { 359 | "a", 360 | "123", 361 | "map[string]any", 362 | }, 363 | { 364 | "b.dd.1", 365 | "ddddd", 366 | "[]any", 367 | }, 368 | { 369 | "b.qqq.2", 370 | "555", 371 | "[num]any", 372 | }, 373 | { 374 | "b.ff.222", 375 | "fddddd", 376 | "map[any]any", 377 | }, 378 | { 379 | "b.hhTy3.222", 380 | "hddddd", 381 | "&map[int]any", 382 | }, 383 | { 384 | "b.hhTy3.333.qq2", 385 | "qq2ddddd", 386 | "map[any]string", 387 | }, 388 | { 389 | "b.hhTy3.666.3", 390 | "789.156", 391 | "Slice", 392 | }, 393 | } 394 | 395 | for _, v := range testData { 396 | check := New(arrData).Find(v.key) 397 | 398 | assert(check, v.expected, v.msg) 399 | } 400 | 401 | } 402 | 403 | func Test_Find_func(t *testing.T) { 404 | assert := assertT(t) 405 | 406 | testData := []struct { 407 | key string 408 | expected string 409 | msg string 410 | }{ 411 | { 412 | "a", 413 | "123", 414 | "map[string]any", 415 | }, 416 | { 417 | "b.dd.1", 418 | "ddddd", 419 | "[]any", 420 | }, 421 | { 422 | "b.ff.222", 423 | "fddddd", 424 | "map[any]any", 425 | }, 426 | { 427 | "b.hhTy3.222", 428 | "hddddd", 429 | "&map[int]any", 430 | }, 431 | { 432 | "b.hhTy3.333.qq2", 433 | "qq2ddddd", 434 | "map[any]string", 435 | }, 436 | { 437 | "b.hhTy3.666.3", 438 | "789.156", 439 | "Slice", 440 | }, 441 | } 442 | 443 | for _, v := range testData { 444 | check := Find(arrData, v.key) 445 | 446 | assert(check, v.expected, v.msg) 447 | } 448 | 449 | } 450 | 451 | func Test_Len(t *testing.T) { 452 | assert := assertDeepEqualT(t) 453 | 454 | testData := []struct { 455 | key string 456 | expected int 457 | msg string 458 | }{ 459 | { 460 | "a", 461 | 0, 462 | "int", 463 | }, 464 | { 465 | "b.ddeef.1", 466 | 5, 467 | "string", 468 | }, 469 | { 470 | "b.dd", 471 | 3, 472 | "[]any", 473 | }, 474 | { 475 | "b.qqq", 476 | 10, 477 | "[num]any", 478 | }, 479 | { 480 | "b.ff", 481 | 3, 482 | "map[any]any", 483 | }, 484 | { 485 | "b.hhTy3", 486 | 0, 487 | "&map[int]any", 488 | }, 489 | { 490 | "b.hhTy3.333", 491 | 3, 492 | "map[any]string", 493 | }, 494 | { 495 | "b.hhTy3.666", 496 | 4, 497 | "Slice", 498 | }, 499 | } 500 | 501 | for _, v := range testData { 502 | check := New(arrData).Sub(v.key).Len() 503 | 504 | assert(check, v.expected, v.msg) 505 | } 506 | 507 | } 508 | 509 | func Test_IsArray(t *testing.T) { 510 | assert := assertDeepEqualT(t) 511 | 512 | testData := []struct { 513 | key string 514 | expected bool 515 | msg string 516 | }{ 517 | { 518 | "a", 519 | false, 520 | "int", 521 | }, 522 | { 523 | "b.dd", 524 | true, 525 | "[]any", 526 | }, 527 | { 528 | "b.qqq", 529 | true, 530 | "[num]any", 531 | }, 532 | { 533 | "b.ff", 534 | true, 535 | "map[any]any", 536 | }, 537 | { 538 | "b.hhTy3", 539 | false, 540 | "&map[int]any", 541 | }, 542 | { 543 | "b.hhTy3.333", 544 | true, 545 | "map[any]string", 546 | }, 547 | { 548 | "b.hhTy3.666", 549 | true, 550 | "Slice", 551 | }, 552 | } 553 | 554 | for _, v := range testData { 555 | check := New(arrData).Sub(v.key).IsArray() 556 | 557 | assert(check, v.expected, v.msg) 558 | } 559 | 560 | } 561 | 562 | func Test_IsMap(t *testing.T) { 563 | assert := assertDeepEqualT(t) 564 | 565 | testData := []struct { 566 | key string 567 | expected bool 568 | msg string 569 | }{ 570 | { 571 | "a", 572 | false, 573 | "int", 574 | }, 575 | { 576 | "b.dd", 577 | false, 578 | "[]any", 579 | }, 580 | { 581 | "b.qqq", 582 | false, 583 | "[num]any", 584 | }, 585 | { 586 | "b.ff", 587 | true, 588 | "map[any]any", 589 | }, 590 | { 591 | "b.hhTy3", 592 | false, 593 | "&map[int]any", 594 | }, 595 | { 596 | "b.hhTy3.333", 597 | true, 598 | "map[any]string", 599 | }, 600 | { 601 | "b.hhTy3.666", 602 | false, 603 | "Slice", 604 | }, 605 | } 606 | 607 | for _, v := range testData { 608 | check := New(arrData).Sub(v.key).IsMap() 609 | 610 | assert(check, v.expected, v.msg) 611 | } 612 | 613 | } 614 | 615 | func Test_IsSlice(t *testing.T) { 616 | assert := assertDeepEqualT(t) 617 | 618 | testData := []struct { 619 | key string 620 | expected bool 621 | msg string 622 | }{ 623 | { 624 | "a", 625 | false, 626 | "int", 627 | }, 628 | { 629 | "b.dd", 630 | true, 631 | "[]any", 632 | }, 633 | { 634 | "b.qqq", 635 | true, 636 | "[num]any", 637 | }, 638 | { 639 | "b.ff", 640 | false, 641 | "map[any]any", 642 | }, 643 | { 644 | "b.hhTy3", 645 | false, 646 | "&map[int]any", 647 | }, 648 | { 649 | "b.hhTy3.333", 650 | false, 651 | "map[any]string", 652 | }, 653 | { 654 | "b.hhTy3.666", 655 | true, 656 | "Slice", 657 | }, 658 | } 659 | 660 | for _, v := range testData { 661 | check := New(arrData).Sub(v.key).IsSlice() 662 | 663 | assert(check, v.expected, v.msg) 664 | } 665 | 666 | } 667 | 668 | func Test_Search(t *testing.T) { 669 | assert := assertT(t) 670 | 671 | testData := []struct { 672 | key string 673 | expected string 674 | msg string 675 | }{ 676 | { 677 | "a", 678 | "123", 679 | "map[string]any", 680 | }, 681 | { 682 | "b.dd.1", 683 | "ddddd", 684 | "[]any", 685 | }, 686 | { 687 | "b.ff.222", 688 | "fddddd", 689 | "map[any]any", 690 | }, 691 | { 692 | "b.hhTy3.222", 693 | "hddddd", 694 | "&map[int]any", 695 | }, 696 | { 697 | "b.hhTy3.333.qq2", 698 | "qq2ddddd", 699 | "map[any]string", 700 | }, 701 | { 702 | "b.hhTy3.666.3", 703 | "789.156", 704 | "Slice", 705 | }, 706 | } 707 | 708 | for _, v := range testData { 709 | check := New(arrData).Search(strings.Split(v.key, ".")...).Value() 710 | 711 | assert(check, v.expected, v.msg) 712 | } 713 | 714 | } 715 | 716 | func Test_Search_func(t *testing.T) { 717 | assert := assertT(t) 718 | 719 | testData := []struct { 720 | key string 721 | expected string 722 | msg string 723 | }{ 724 | { 725 | "a", 726 | "123", 727 | "map[string]any", 728 | }, 729 | { 730 | "b.dd.1", 731 | "ddddd", 732 | "[]any", 733 | }, 734 | { 735 | "b.ff.222", 736 | "fddddd", 737 | "map[any]any", 738 | }, 739 | { 740 | "b.hhTy3.222", 741 | "hddddd", 742 | "&map[int]any", 743 | }, 744 | { 745 | "b.hhTy3.333.qq2", 746 | "qq2ddddd", 747 | "map[any]string", 748 | }, 749 | { 750 | "b.hhTy3.666.3", 751 | "789.156", 752 | "Slice", 753 | }, 754 | } 755 | 756 | for _, v := range testData { 757 | check := Search(arrData, strings.Split(v.key, ".")...).Value() 758 | 759 | assert(check, v.expected, v.msg) 760 | } 761 | 762 | } 763 | 764 | func Test_ParseJSON(t *testing.T) { 765 | assert := assertT(t) 766 | 767 | jsonParsed, err := ParseJSON([]byte(`{ 768 | "outer":{ 769 | "inner":{ 770 | "value1":21, 771 | "value2":35 772 | }, 773 | "alsoInner":{ 774 | "value1":99, 775 | "array1":[ 776 | 11, 23 777 | ] 778 | } 779 | } 780 | }`)) 781 | if err != nil { 782 | t.Fatal(err) 783 | } 784 | 785 | value := jsonParsed.Find("outer.inner.value1") 786 | expected := "21" 787 | 788 | assert(value, expected, "ParseJSON fail") 789 | 790 | value2 := jsonParsed.Find("outer.alsoInner.array1.1") 791 | expected2 := "23" 792 | 793 | assert(value2, expected2, "ParseJSON 2 fail") 794 | } 795 | 796 | func Test_Sub_And_Value(t *testing.T) { 797 | assert := assertT(t) 798 | 799 | testData := []struct { 800 | key string 801 | expected string 802 | msg string 803 | }{ 804 | { 805 | "a", 806 | "123", 807 | "map[string]any", 808 | }, 809 | { 810 | "b.dd.1", 811 | "ddddd", 812 | "[]any", 813 | }, 814 | { 815 | "b.ff.222", 816 | "fddddd", 817 | "map[any]any", 818 | }, 819 | { 820 | "b.hhTy3.222", 821 | "hddddd", 822 | "&map[int]any", 823 | }, 824 | { 825 | "b.hhTy3.333.qq2", 826 | "qq2ddddd", 827 | "map[any]string", 828 | }, 829 | { 830 | "b.hhTy3.666.3", 831 | "789.156", 832 | "Slice", 833 | }, 834 | } 835 | 836 | for _, v := range testData { 837 | check := New(arrData).Sub(v.key).Value() 838 | 839 | assert(check, v.expected, v.msg) 840 | } 841 | 842 | } 843 | 844 | func Test_Sub_And_Value_func(t *testing.T) { 845 | assert := assertT(t) 846 | 847 | testData := []struct { 848 | key string 849 | expected string 850 | msg string 851 | }{ 852 | { 853 | "a", 854 | "123", 855 | "map[string]any", 856 | }, 857 | { 858 | "b.dd.1", 859 | "ddddd", 860 | "[]any", 861 | }, 862 | { 863 | "b.ff.222", 864 | "fddddd", 865 | "map[any]any", 866 | }, 867 | { 868 | "b.hhTy3.222", 869 | "hddddd", 870 | "&map[int]any", 871 | }, 872 | { 873 | "b.hhTy3.333.qq2", 874 | "qq2ddddd", 875 | "map[any]string", 876 | }, 877 | { 878 | "b.hhTy3.666.3", 879 | "789.156", 880 | "Slice", 881 | }, 882 | } 883 | 884 | for _, v := range testData { 885 | check := Sub(arrData, v.key).Value() 886 | 887 | assert(check, v.expected, v.msg) 888 | } 889 | 890 | } 891 | 892 | func Test_Sub_And_ToJSON(t *testing.T) { 893 | assert := assertT(t) 894 | 895 | testData := []struct { 896 | key string 897 | expected string 898 | msg string 899 | }{ 900 | { 901 | "b.dd", 902 | `["ccccc","ddddd","fffff"]`, 903 | "[]any", 904 | }, 905 | { 906 | "b.d", 907 | `{"e":"eee","f":{"g":"ggg"}}`, 908 | "map[any]any", 909 | }, 910 | { 911 | "b.hhTy3.333", 912 | `{"qq1":"qq1ccccc","qq2":"qq2ddddd","qq3":"qq3fffff"}`, 913 | "&map[int]any", 914 | }, 915 | { 916 | "b.hhTy3", 917 | `null`, 918 | "null", 919 | }, 920 | } 921 | 922 | for _, v := range testData { 923 | check := New(arrData).Sub(v.key).ToJSON() 924 | 925 | assert(check, v.expected, v.msg) 926 | } 927 | 928 | } 929 | 930 | func Test_Sub_And_ToJSONIndent(t *testing.T) { 931 | assert := assertT(t) 932 | 933 | testData := []struct { 934 | key string 935 | expected string 936 | msg string 937 | }{ 938 | { 939 | "b.dd", 940 | `[ 941 | "ccccc", 942 | "ddddd", 943 | "fffff" 944 | ]`, 945 | "[]any", 946 | }, 947 | { 948 | "b.d", 949 | `{ 950 | "e": "eee", 951 | "f": { 952 | "g": "ggg" 953 | } 954 | }`, 955 | "map[any]any", 956 | }, 957 | { 958 | "b.hhTy3.333", 959 | `{ 960 | "qq1": "qq1ccccc", 961 | "qq2": "qq2ddddd", 962 | "qq3": "qq3fffff" 963 | }`, 964 | "&map[int]any", 965 | }, 966 | { 967 | "b.d222", 968 | `null`, 969 | "null", 970 | }, 971 | } 972 | 973 | for _, v := range testData { 974 | check := New(arrData).Sub(v.key).ToJSONIndent("", " ") 975 | 976 | assert(check, v.expected, v.msg) 977 | } 978 | 979 | } 980 | 981 | func Test_Sub_And_MarshalJSON(t *testing.T) { 982 | assert := assertT(t) 983 | 984 | testData := []struct { 985 | key string 986 | expected string 987 | msg string 988 | }{ 989 | { 990 | "b.dd", 991 | `["ccccc","ddddd","fffff"]`, 992 | "[]any", 993 | }, 994 | { 995 | "b.d", 996 | `{"e":"eee","f":{"g":"ggg"}}`, 997 | "map[any]any", 998 | }, 999 | { 1000 | "b.hhTy3.333", 1001 | `{"qq1":"qq1ccccc","qq2":"qq2ddddd","qq3":"qq3fffff"}`, 1002 | "&map[int]any", 1003 | }, 1004 | { 1005 | "b.hhTy3", 1006 | ``, 1007 | "null", 1008 | }, 1009 | } 1010 | 1011 | for _, v := range testData { 1012 | check, _ := New(arrData).Sub(v.key).MarshalJSON() 1013 | 1014 | assert(check, v.expected, v.msg) 1015 | } 1016 | 1017 | } 1018 | 1019 | func Test_Children(t *testing.T) { 1020 | jsonParsed, _ := ParseJSON([]byte(`{"map":{"objectOne":{"num":1}}, "array":[ "first", "second", "third" ]}`)) 1021 | 1022 | expected := []string{"first", "second", "third"} 1023 | 1024 | childrenNil := jsonParsed.Sub("array123132").Children() 1025 | if childrenNil != nil { 1026 | t.Error("Child need return nil") 1027 | } 1028 | 1029 | children := jsonParsed.Sub("array").Children() 1030 | for i, child := range children { 1031 | if expected[i] != child.Value().(string) { 1032 | t.Errorf("Child unexpected: %v != %v", expected[i], child.Value().(string)) 1033 | } 1034 | } 1035 | 1036 | mapChildren := jsonParsed.Sub("map").Children() 1037 | for key, val := range mapChildren { 1038 | switch key { 1039 | case 0: 1040 | if val := val.Sub("num").Value().(float64); val != 1 { 1041 | t.Errorf("%v != %v", val, 1) 1042 | } 1043 | default: 1044 | t.Errorf("Unexpected key: %v", key) 1045 | } 1046 | } 1047 | } 1048 | 1049 | func Test_ChildrenMap(t *testing.T) { 1050 | json1, _ := ParseJSON([]byte(`{ 1051 | "objectOne":{"num":1}, 1052 | "objectTwo":{"num":2}, 1053 | "objectThree":{"num":3} 1054 | }`)) 1055 | 1056 | objectMap := json1.ChildrenMap() 1057 | if len(objectMap) != 3 { 1058 | t.Errorf("Wrong num of elements in objectMap: %v != %v", len(objectMap), 3) 1059 | return 1060 | } 1061 | 1062 | ChildrenMapNil := json1.Sub("array123132").ChildrenMap() 1063 | if len(ChildrenMapNil) != 0 { 1064 | t.Error("Child need return map[string]*Array{}") 1065 | } 1066 | 1067 | for key, val := range objectMap { 1068 | switch key { 1069 | case "objectOne": 1070 | if val := val.Sub("num").Value().(float64); val != 1 { 1071 | t.Errorf("%v != %v", val, 1) 1072 | } 1073 | case "objectTwo": 1074 | if val := val.Sub("num").Value().(float64); val != 2 { 1075 | t.Errorf("%v != %v", val, 2) 1076 | } 1077 | case "objectThree": 1078 | if val := val.Sub("num").Value().(float64); val != 3 { 1079 | t.Errorf("%v != %v", val, 3) 1080 | } 1081 | default: 1082 | t.Errorf("Unexpected key: %v", key) 1083 | } 1084 | } 1085 | } 1086 | 1087 | func Test_Flatten(t *testing.T) { 1088 | assert := assertDeepEqualT(t) 1089 | 1090 | json1, _ := ParseJSON([]byte(`{"foo":[{"bar":"1"},{"bar":"2"}]}`)) 1091 | 1092 | flattenData, err := json1.Flatten() 1093 | if err != nil { 1094 | t.Fatal(err) 1095 | } 1096 | 1097 | check := map[string]any{ 1098 | "foo.0.bar": "1", 1099 | "foo.1.bar": "2", 1100 | } 1101 | 1102 | assert(flattenData, check, "Flatten fail") 1103 | 1104 | // ===== 1105 | 1106 | flattenData2, err := json1.Sub("foo").Flatten() 1107 | if err != nil { 1108 | t.Fatal(err) 1109 | } 1110 | 1111 | check2 := map[string]any{ 1112 | "0.bar": "1", 1113 | "1.bar": "2", 1114 | } 1115 | 1116 | assert(flattenData2, check2, "Flatten 2 fail") 1117 | } 1118 | 1119 | func Test_FlattenIncludeEmpty(t *testing.T) { 1120 | assert := assertDeepEqualT(t) 1121 | 1122 | json1, _ := ParseJSON([]byte(`{"foo":[{"bar":"1"},{"bar":"2"},{"bar222":{}}]}`)) 1123 | 1124 | flattenData, err := json1.FlattenIncludeEmpty() 1125 | if err != nil { 1126 | t.Fatal(err) 1127 | } 1128 | 1129 | check := map[string]any{ 1130 | "foo.0.bar": "1", 1131 | "foo.1.bar": "2", 1132 | "foo.2.bar222": struct{}{}, 1133 | } 1134 | 1135 | assert(flattenData, check, "FlattenIncludeEmpty fail") 1136 | } 1137 | 1138 | func Test_JSONPointer(t *testing.T) { 1139 | assert := assertT(t) 1140 | 1141 | data := []byte(`{"foo":[{"bar":"1"},{"bar":"2"}]}`) 1142 | 1143 | json1, _ := ParseJSON(data) 1144 | 1145 | testData := []struct { 1146 | key string 1147 | expected string 1148 | msg string 1149 | }{ 1150 | { 1151 | "/foo/0", 1152 | `{"bar":"1"}`, 1153 | "map[string]any", 1154 | }, 1155 | { 1156 | "/foo", 1157 | `[{"bar":"1"},{"bar":"2"}]`, 1158 | "[]any", 1159 | }, 1160 | { 1161 | "/foo/1", 1162 | `{"bar":"2"}`, 1163 | "map[string]any", 1164 | }, 1165 | { 1166 | "/foo/5", 1167 | `null`, 1168 | "not find", 1169 | }, 1170 | { 1171 | "/", 1172 | `null`, 1173 | "null", 1174 | }, 1175 | } 1176 | 1177 | for _, v := range testData { 1178 | check, err := json1.JSONPointer(v.key) 1179 | if err != nil { 1180 | t.Fatal(err) 1181 | } 1182 | 1183 | assert(check.String(), v.expected, v.msg) 1184 | } 1185 | 1186 | var dst any 1187 | json.Unmarshal(data, &dst) 1188 | 1189 | for _, v := range testData { 1190 | check, err := JSONPointer(dst, v.key) 1191 | if err != nil { 1192 | t.Fatal(err) 1193 | } 1194 | 1195 | assert(check.String(), v.expected, v.msg) 1196 | } 1197 | 1198 | _, err := json1.JSONPointer("foo/1") 1199 | if err == nil { 1200 | t.Error("value should not have been found in foo") 1201 | } 1202 | } 1203 | 1204 | func Test_Set(t *testing.T) { 1205 | gObj := New(nil) 1206 | 1207 | if _, err := gObj.Set([]interface{}{}, "foo"); err != nil { 1208 | t.Fatal(err) 1209 | } 1210 | if _, err := gObj.Set(1, "foo", "-"); err != nil { 1211 | t.Fatal(err) 1212 | } 1213 | if _, err := gObj.Set([]interface{}{}, "foo", "-", "baz"); err != nil { 1214 | t.Fatal(err) 1215 | } 1216 | if _, err := gObj.Set(2, "foo", "1", "baz", "-"); err != nil { 1217 | t.Fatal(err) 1218 | } 1219 | if _, err := gObj.Set(3, "foo", "1", "baz", "-"); err != nil { 1220 | t.Fatal(err) 1221 | } 1222 | if _, err := gObj.Set(5, "foo", "-"); err != nil { 1223 | t.Fatal(err) 1224 | } 1225 | 1226 | exp := `{"foo":[1,{"baz":[2,3]},5]}` 1227 | if act := gObj.String(); act != exp { 1228 | t.Errorf("Unexpected value: %v != %v", act, exp) 1229 | } 1230 | 1231 | // ======== 1232 | 1233 | arrData2 := map[string]any{ 1234 | "a": 123, 1235 | "b": map[string]any{ 1236 | "c": "ccc", 1237 | "d": map[string]any{ 1238 | "e": "eee", 1239 | "f": map[string]any{ 1240 | "g": "ggg", 1241 | }, 1242 | }, 1243 | "dd": []any{ 1244 | "ccccc", 1245 | "ddddd", 1246 | "fffff", 1247 | }, 1248 | "ddd": []int64{ 1249 | 22, 1250 | 333, 1251 | 555, 1252 | }, 1253 | "ff": map[any]any{ 1254 | 111: "fccccc", 1255 | 222: "fddddd", 1256 | 333: "dfffff", 1257 | }, 1258 | "hhTy3": &map[int]any{ 1259 | 111: "hccccc", 1260 | 222: "hddddd", 1261 | 333: map[any]string{ 1262 | "qq1": "qq1ccccc", 1263 | "qq2": "qq2ddddd", 1264 | "qq3": "qq3fffff", 1265 | }, 1266 | 666: []float64{ 1267 | 12.3, 1268 | 32.5, 1269 | 22.56, 1270 | 789.156, 1271 | }, 1272 | }, 1273 | "kJh21ay": map[string]any{ 1274 | "Hjk2": "fccDcc", 1275 | "23rt": "^hgcF5c", 1276 | "23rt5": []any{ 1277 | "adfa", 1278 | 1231, 1279 | }, 1280 | }, 1281 | }, 1282 | } 1283 | 1284 | gObj2 := New(arrData2) 1285 | if _, err := gObj2.Set(5, "b", "ff", 555); err != nil { 1286 | t.Fatal(err) 1287 | } 1288 | if _, err := gObj2.Set("qqqqqqqqw", "b", "dd", "-"); err != nil { 1289 | t.Fatal(err) 1290 | } 1291 | if _, err := gObj2.Set(float64(222.9999), "b", "hhTy3", int(666), 2); err != nil { 1292 | t.Fatal(err) 1293 | } 1294 | 1295 | exp2 := `{"111":"fccccc","222":"fddddd","333":"dfffff","555":5}` 1296 | if act := gObj2.Sub("b.ff").String(); act != exp2 { 1297 | t.Errorf("Unexpected value: %v != %v", act, exp2) 1298 | } 1299 | exp3 := `["ccccc","ddddd","fffff","qqqqqqqqw"]` 1300 | if act := gObj2.Sub("b.dd").String(); act != exp3 { 1301 | t.Errorf("Unexpected value: %v != %v", act, exp3) 1302 | } 1303 | exp5 := `[12.3,32.5,222.9999,789.156]` 1304 | if act := gObj2.Sub("b.hhTy3.666").String(); act != exp5 { 1305 | t.Errorf("Unexpected value: %v != %v", act, exp5) 1306 | } 1307 | 1308 | } 1309 | 1310 | func Test_SetMap(t *testing.T) { 1311 | obj := New(arrData) 1312 | _, err := obj.Set("yyyyyyyyy", "b", "ff", "555") 1313 | if err != nil { 1314 | t.Fatal(err) 1315 | } 1316 | 1317 | res := obj.Sub("b.ff").String() 1318 | 1319 | check := `{"111":"fccccc","222":"fddddd","333":"dfffff","555":"yyyyyyyyy"}` 1320 | if res != check { 1321 | t.Errorf("SetMap fail. got %v, want %v", res, check) 1322 | } 1323 | 1324 | // ======= 1325 | 1326 | obj1 := New(arrData) 1327 | _, err = obj1.Set("yyyyyyyyy") 1328 | if err != nil { 1329 | t.Fatal(err) 1330 | } 1331 | 1332 | res1 := fmt.Sprintf("%v", obj1.Value()) 1333 | 1334 | check1 := `yyyyyyyyy` 1335 | if res1 != check1 { 1336 | t.Errorf("SetMap 1 fail. got %v, want %v", res1, check1) 1337 | } 1338 | 1339 | // ======= 1340 | 1341 | obj2 := New(arrData) 1342 | _, err = obj2.Set(133.122333, "b", "hhTy3", 666) 1343 | if err != nil { 1344 | t.Fatal(err) 1345 | } 1346 | 1347 | _, err = obj2.Set(133.122333, "b", "kJh21ay22", "ftd") 1348 | if err == nil { 1349 | t.Error("Set should error") 1350 | } 1351 | 1352 | res2 := fmt.Sprintf("%v", obj2.Sub("b.hhTy3").Value()) 1353 | 1354 | check2 := `&map[111:hccccc 222:hddddd 333:map[qq1:qq1ccccc qq2:qq2ddddd qq3:qq3fffff] 666:133.122333]` 1355 | if res2 != check2 { 1356 | t.Errorf("SetMap 2 fail. got %v, want %v", res2, check2) 1357 | } 1358 | } 1359 | 1360 | func Test_SetKey(t *testing.T) { 1361 | obj := New(arrData) 1362 | _, err := obj.SetKey("yyyyyyyyy", "b.ff.555") 1363 | if err != nil { 1364 | t.Fatal(err) 1365 | } 1366 | 1367 | res := obj.Sub("b.ff").String() 1368 | 1369 | check := `{"111":"fccccc","222":"fddddd","333":"dfffff","555":"yyyyyyyyy"}` 1370 | if res != check { 1371 | t.Errorf("SetKey fail. got %v, want %v", res, check) 1372 | } 1373 | } 1374 | 1375 | func Test_ArraysTwo(t *testing.T) { 1376 | json1 := New(nil) 1377 | 1378 | test1, err := json1.ArrayOfSize(4, "test1") 1379 | if err != nil { 1380 | t.Error(err) 1381 | } 1382 | 1383 | if _, err = test1.ArrayOfSizeIndex(2, 0); err != nil { 1384 | t.Error(err) 1385 | } 1386 | if _, err = test1.ArrayOfSizeIndex(2, 1); err != nil { 1387 | t.Error(err) 1388 | } 1389 | if _, err = test1.ArrayOfSizeIndex(2, 2); err != nil { 1390 | t.Error(err) 1391 | } 1392 | if _, err = test1.ArrayOfSizeIndex(2, 3); err != nil { 1393 | t.Error(err) 1394 | } 1395 | 1396 | if _, err = test1.ArrayOfSizeIndex(2, 4); err != ErrOutOfBounds { 1397 | t.Errorf("Index should have been out of bounds") 1398 | } 1399 | 1400 | if _, err = json1.Sub("test1").Index(0).SetIndex(10, 0); err != nil { 1401 | t.Error(err) 1402 | } 1403 | if _, err = json1.Sub("test1").Index(0).SetIndex(11, 1); err != nil { 1404 | t.Error(err) 1405 | } 1406 | 1407 | if _, err = json1.Sub("test1").Index(1).SetIndex(12, 0); err != nil { 1408 | t.Error(err) 1409 | } 1410 | if _, err = json1.Sub("test1").Index(1).SetIndex(13, 1); err != nil { 1411 | t.Error(err) 1412 | } 1413 | 1414 | if _, err = json1.Sub("test1").Index(2).SetIndex(14, 0); err != nil { 1415 | t.Error(err) 1416 | } 1417 | if _, err = json1.Sub("test1").Index(2).SetIndex(15, 1); err != nil { 1418 | t.Error(err) 1419 | } 1420 | 1421 | if _, err = json1.Sub("test1").Index(3).SetIndex(16, 0); err != nil { 1422 | t.Error(err) 1423 | } 1424 | if _, err = json1.Sub("test1").Index(3).SetIndex(17, 1); err != nil { 1425 | t.Error(err) 1426 | } 1427 | 1428 | if val := json1.Sub("test1").Index(0).Index(0).Value().(int); val != 10 { 1429 | t.Errorf("create array: %v != %v", val, 10) 1430 | } 1431 | if val := json1.Sub("test1").Index(0).Index(1).Value().(int); val != 11 { 1432 | t.Errorf("create array: %v != %v", val, 11) 1433 | } 1434 | 1435 | if val := json1.Sub("test1").Index(1).Index(0).Value().(int); val != 12 { 1436 | t.Errorf("create array: %v != %v", val, 12) 1437 | } 1438 | if val := json1.Sub("test1").Index(1).Index(1).Value().(int); val != 13 { 1439 | t.Errorf("create array: %v != %v", val, 13) 1440 | } 1441 | 1442 | if val := json1.Sub("test1").Index(2).Index(0).Value().(int); val != 14 { 1443 | t.Errorf("create array: %v != %v", val, 14) 1444 | } 1445 | if val := json1.Sub("test1").Index(2).Index(1).Value().(int); val != 15 { 1446 | t.Errorf("create array: %v != %v", val, 15) 1447 | } 1448 | 1449 | if val := json1.Sub("test1").Index(3).Index(0).Value().(int); val != 16 { 1450 | t.Errorf("create array: %v != %v", val, 16) 1451 | } 1452 | if val := json1.Sub("test1").Index(3).Index(1).Value().(int); val != 17 { 1453 | t.Errorf("create array: %v != %v", val, 17) 1454 | } 1455 | } 1456 | 1457 | func Test_ArraysThree(t *testing.T) { 1458 | json1 := New(nil) 1459 | 1460 | test, err := json1.ArrayOfSizeKey(1, "test1.test2") 1461 | if err != nil { 1462 | t.Fatal(err) 1463 | } 1464 | 1465 | test.SetIndex(10, 0) 1466 | if val := json1.Sub("test1.test2").Index(0).Value().(int); val != 10 { 1467 | t.Error(err) 1468 | } 1469 | 1470 | // ======== 1471 | 1472 | obj2 := New(arrData) 1473 | if val := obj2.Sub("b.ddd").Index(0).Value().(int64); val != int64(22) { 1474 | t.Error(err) 1475 | } 1476 | 1477 | oo := obj2.Sub("b.ddd") 1478 | 1479 | oo, err = oo.SetIndex(1000, 2) 1480 | if err != nil { 1481 | t.Error(err) 1482 | } 1483 | 1484 | if val := oo.Index(2).Value().(int64); val != 1000 { 1485 | t.Error(err) 1486 | } 1487 | } 1488 | 1489 | func Test_BadIndexes(t *testing.T) { 1490 | jsonObj, err := ParseJSON([]byte(`{"array":[1,2,3]}`)) 1491 | if err != nil { 1492 | t.Error(err) 1493 | } 1494 | 1495 | if act := jsonObj.Index(0).Value(); act != nil { 1496 | t.Errorf("Unexpected value returned: %v != %v", nil, act) 1497 | } 1498 | 1499 | if act := jsonObj.Sub("array").Index(4).Value(); act != nil { 1500 | t.Errorf("Unexpected value returned: %v != %v", nil, act) 1501 | } 1502 | 1503 | // ======== 1504 | 1505 | obj2 := New(arrData) 1506 | if act := obj2.Sub("b.ddd").Index(4).Value(); act != nil { 1507 | t.Errorf("Unexpected value returned: %v != %v", nil, act) 1508 | } 1509 | if act := obj2.Sub("b.hhTy66.777").Index(4).Value(); act != nil { 1510 | t.Errorf("Unexpected value returned: %v != %v", nil, act) 1511 | } 1512 | 1513 | oo := obj2.Sub("b.ddd") 1514 | 1515 | _, err = oo.SetIndex(1000, 4) 1516 | if err != ErrOutOfBounds { 1517 | t.Error("SetIndex error need ErrOutOfBounds") 1518 | } 1519 | } 1520 | 1521 | func Test_Deletes(t *testing.T) { 1522 | jsonParsed, _ := ParseJSON([]byte(`{ 1523 | "outter":{ 1524 | "inner":{ 1525 | "value1":10, 1526 | "value2":22, 1527 | "value3":32 1528 | }, 1529 | "alsoInner":{ 1530 | "value1":20, 1531 | "value2":42, 1532 | "value3":92 1533 | }, 1534 | "another":{ 1535 | "value1":null, 1536 | "value2":null, 1537 | "value3":null 1538 | } 1539 | } 1540 | }`)) 1541 | 1542 | if err := jsonParsed.Delete("outter", "inner", "value2"); err != nil { 1543 | t.Error(err) 1544 | } 1545 | if err := jsonParsed.Delete("outter", "inner", "value4"); err == nil { 1546 | t.Error("value4 should not have been found in outter.inner") 1547 | } 1548 | if err := jsonParsed.Delete("outter", "another", "value1"); err != nil { 1549 | t.Error(err) 1550 | } 1551 | if err := jsonParsed.Delete("outter", "another", "value4"); err == nil { 1552 | t.Error("value4 should not have been found in outter.another") 1553 | } 1554 | if err := jsonParsed.DeleteKey("outter.alsoInner.value1"); err != nil { 1555 | t.Error(err) 1556 | } 1557 | if err := jsonParsed.DeleteKey("outter.alsoInner.value4"); err == nil { 1558 | t.Error("value4 should not have been found in outter.alsoInner") 1559 | } 1560 | if err := jsonParsed.DeleteKey("outter.another.value2"); err != nil { 1561 | t.Error(err) 1562 | } 1563 | if err := jsonParsed.Delete("outter.another.value4"); err == nil { 1564 | t.Error("value4 should not have been found in outter.another") 1565 | } 1566 | 1567 | if err := jsonParsed.Delete(); err == nil { 1568 | t.Error("value should not have been found in null") 1569 | } 1570 | 1571 | expected := `{"outter":{"alsoInner":{"value2":42,"value3":92},"another":{"value3":null},"inner":{"value1":10,"value3":32}}}` 1572 | if actual := jsonParsed.String(); actual != expected { 1573 | t.Errorf("Unexpected result from deletes: %v != %v", actual, expected) 1574 | } 1575 | 1576 | arrData2 := map[string]any{ 1577 | "a": 123, 1578 | "b": map[string]any{ 1579 | "ff": map[any]any{ 1580 | 111: "fccccc", 1581 | 222: "fddddd", 1582 | 333: "dfffff", 1583 | }, 1584 | "t666": []float64{ 1585 | 12.3, 1586 | 32.5, 1587 | 22.56, 1588 | 789.156, 1589 | }, 1590 | }, 1591 | } 1592 | 1593 | jsonParsed2 := New(arrData2) 1594 | if err := jsonParsed2.Delete("b", "ff", 333); err != nil { 1595 | t.Error(err) 1596 | } 1597 | if err := jsonParsed2.Delete("b", "ff", 33355); err == nil { 1598 | t.Error("data should not have been found in b.ff") 1599 | } 1600 | 1601 | if err := jsonParsed2.Delete("b", "t666", 2); err != nil { 1602 | t.Error(err) 1603 | } 1604 | if err := jsonParsed2.Delete("b", "t666", 7); err == nil { 1605 | t.Error("data should not have been found in b.t666") 1606 | } 1607 | 1608 | expected2 := `{"111":"fccccc","222":"fddddd"}` 1609 | if actual2 := jsonParsed2.Sub("b.ff").String(); actual2 != expected2 { 1610 | t.Errorf("Unexpected result from deletes: %v != %v", actual2, expected2) 1611 | } 1612 | 1613 | expected2 = `[12.3,32.5,789.156]` 1614 | if actual2 := jsonParsed2.Sub("b.t666").String(); actual2 != expected2 { 1615 | t.Errorf("Unexpected result from deletes: %v != %v", actual2, expected2) 1616 | } 1617 | 1618 | jsonParsed3 := New(nil) 1619 | if err := jsonParsed3.Delete("b", "ff", 333); err == nil { 1620 | t.Error("data should return error") 1621 | } 1622 | } 1623 | 1624 | func Test_DeletesWithSlices(t *testing.T) { 1625 | rawJSON := `{ 1626 | "outter":[ 1627 | { 1628 | "foo":{ 1629 | "value1":10, 1630 | "value2":22, 1631 | "value3":32 1632 | }, 1633 | "bar": [ 1634 | 20, 1635 | 42, 1636 | 92 1637 | ] 1638 | }, 1639 | { 1640 | "baz":{ 1641 | "value1":null, 1642 | "value2":null, 1643 | "value3":null 1644 | } 1645 | } 1646 | ] 1647 | }` 1648 | 1649 | jsonParsed, err := ParseJSON([]byte(rawJSON)) 1650 | if err != nil { 1651 | t.Fatal(err) 1652 | } 1653 | if err = jsonParsed.Delete("outter", "1", "baz", "value1"); err != nil { 1654 | t.Error(err) 1655 | } 1656 | 1657 | expected := `{"outter":[{"bar":[20,42,92],"foo":{"value1":10,"value2":22,"value3":32}},{"baz":{"value2":null,"value3":null}}]}` 1658 | if actual := jsonParsed.String(); actual != expected { 1659 | t.Errorf("Unexpected result from array deletes: %v != %v", actual, expected) 1660 | } 1661 | 1662 | jsonParsed, err = ParseJSON([]byte(rawJSON)) 1663 | if err != nil { 1664 | t.Fatal(err) 1665 | } 1666 | if err = jsonParsed.Delete("outter", "1", "baz"); err != nil { 1667 | t.Error(err) 1668 | } 1669 | 1670 | expected = `{"outter":[{"bar":[20,42,92],"foo":{"value1":10,"value2":22,"value3":32}},{}]}` 1671 | if actual := jsonParsed.String(); actual != expected { 1672 | t.Errorf("Unexpected result from array deletes: %v != %v", actual, expected) 1673 | } 1674 | 1675 | jsonParsed, err = ParseJSON([]byte(rawJSON)) 1676 | if err != nil { 1677 | t.Fatal(err) 1678 | } 1679 | if err = jsonParsed.Delete("outter", "1"); err != nil { 1680 | t.Error(err) 1681 | } 1682 | 1683 | expected = `{"outter":[{"bar":[20,42,92],"foo":{"value1":10,"value2":22,"value3":32}}]}` 1684 | if actual := jsonParsed.String(); actual != expected { 1685 | t.Errorf("Unexpected result from array deletes: %v != %v", actual, expected) 1686 | } 1687 | 1688 | jsonParsed, err = ParseJSON([]byte(rawJSON)) 1689 | if err != nil { 1690 | t.Fatal(err) 1691 | } 1692 | if err = jsonParsed.Delete("outter", "0", "bar", "0"); err != nil { 1693 | t.Error(err) 1694 | } 1695 | 1696 | expected = `{"outter":[{"bar":[42,92],"foo":{"value1":10,"value2":22,"value3":32}},{"baz":{"value1":null,"value2":null,"value3":null}}]}` 1697 | if actual := jsonParsed.String(); actual != expected { 1698 | t.Errorf("Unexpected result from array deletes: %v != %v", actual, expected) 1699 | } 1700 | 1701 | jsonParsed, err = ParseJSON([]byte(rawJSON)) 1702 | if err != nil { 1703 | t.Fatal(err) 1704 | } 1705 | if err = jsonParsed.Delete("outter", "0", "bar", "1"); err != nil { 1706 | t.Error(err) 1707 | } 1708 | 1709 | expected = `{"outter":[{"bar":[20,92],"foo":{"value1":10,"value2":22,"value3":32}},{"baz":{"value1":null,"value2":null,"value3":null}}]}` 1710 | if actual := jsonParsed.String(); actual != expected { 1711 | t.Errorf("Unexpected result from array deletes: %v != %v", actual, expected) 1712 | } 1713 | 1714 | jsonParsed, err = ParseJSON([]byte(rawJSON)) 1715 | if err != nil { 1716 | t.Fatal(err) 1717 | } 1718 | if err = jsonParsed.Delete("outter", "0", "bar", "2"); err != nil { 1719 | t.Error(err) 1720 | } 1721 | 1722 | expected = `{"outter":[{"bar":[20,42],"foo":{"value1":10,"value2":22,"value3":32}},{"baz":{"value1":null,"value2":null,"value3":null}}]}` 1723 | if actual := jsonParsed.String(); actual != expected { 1724 | t.Errorf("Unexpected result from array deletes: %v != %v", actual, expected) 1725 | } 1726 | } 1727 | 1728 | func Test_BasicWithDecoder(t *testing.T) { 1729 | sample := []byte(`{"test":{"int":10, "float":6.66}}`) 1730 | dec := json.NewDecoder(bytes.NewReader(sample)) 1731 | dec.UseNumber() 1732 | 1733 | val, err := ParseJSONDecoder(dec) 1734 | if err != nil { 1735 | t.Errorf("Failed to parse: %v", err) 1736 | return 1737 | } 1738 | 1739 | checkNumber := func(path string, expectedVal json.Number) { 1740 | data := val.Sub(path).Value() 1741 | asNumber, isNumber := data.(json.Number) 1742 | if !isNumber { 1743 | t.Error("Failed to parse using decoder UseNumber policy") 1744 | } 1745 | if expectedVal != asNumber { 1746 | t.Errorf("Expected[%s] but got [%s]", expectedVal, asNumber) 1747 | } 1748 | } 1749 | 1750 | checkNumber("test.int", "10") 1751 | checkNumber("test.float", "6.66") 1752 | } 1753 | 1754 | func Test_BadWithDecoder(t *testing.T) { 1755 | sample := []byte(`{"test":{"int":10, "float":6.66}`) 1756 | dec := json.NewDecoder(bytes.NewReader(sample)) 1757 | dec.UseNumber() 1758 | 1759 | _, err := ParseJSONDecoder(dec) 1760 | if err == nil { 1761 | t.Error("data should not have been found in ParseJSONDecoder") 1762 | } 1763 | } 1764 | 1765 | func Test_isPathShadowedInDeepMap(t *testing.T) { 1766 | assert := assertT(t) 1767 | 1768 | testData := []struct { 1769 | key string 1770 | expected string 1771 | msg string 1772 | }{ 1773 | { 1774 | "a", 1775 | "", 1776 | "map[string]any", 1777 | }, 1778 | { 1779 | "b.dd.1", 1780 | "b.dd", 1781 | "[]any", 1782 | }, 1783 | { 1784 | "b.ff", 1785 | "", 1786 | "map[any]any", 1787 | }, 1788 | { 1789 | "b.hhTy3.222", 1790 | "b.hhTy3", 1791 | "&map[int]any", 1792 | }, 1793 | { 1794 | "b.hhTy3.333.qq2", 1795 | "b.hhTy3", 1796 | "map[any]string", 1797 | }, 1798 | { 1799 | "b.hhTy3.666.3", 1800 | "b.hhTy3", 1801 | "Slice", 1802 | }, 1803 | } 1804 | 1805 | for _, v := range testData { 1806 | check := New(nil).isPathShadowedInDeepMap(strings.Split(v.key, "."), arrData) 1807 | 1808 | assert(check, v.expected, v.msg) 1809 | } 1810 | 1811 | } 1812 | 1813 | func Test_Example(t *testing.T) { 1814 | assert := assertDeepEqualT(t) 1815 | 1816 | arrData := map[string]any{ 1817 | "a": 123, 1818 | "b": map[string]any{ 1819 | "c": "ccc", 1820 | "d": map[string]any{ 1821 | "e": "eee", 1822 | "f": map[string]any{ 1823 | "g": "ggg", 1824 | }, 1825 | }, 1826 | "dd": []any{ 1827 | "ccccc", 1828 | "ddddd", 1829 | "fffff", 1830 | }, 1831 | "ff": map[any]any{ 1832 | 111: "fccccc", 1833 | 222: "fddddd", 1834 | 333: "dfffff", 1835 | }, 1836 | "hh": map[int]any{ 1837 | 1115: "hccccc", 1838 | 2225: "hddddd", 1839 | 3335: map[any]string{ 1840 | "qq1": "qq1ccccc", 1841 | "qq2": "qq2ddddd", 1842 | "qq3": "qq3fffff", 1843 | }, 1844 | }, 1845 | "kJh21ay": map[string]any{ 1846 | "Hjk2": "fccDcc", 1847 | "23rt": "^hgcF5c", 1848 | }, 1849 | }, 1850 | } 1851 | 1852 | { 1853 | var res bool = New(arrData).Exists("b.kJh21ay.Hjk2") 1854 | assert(true, res, "Exists") 1855 | } 1856 | { 1857 | var res bool = New(arrData).Exists("b.kJh21ay.Hjk12") 1858 | assert(false, res, "Exists") 1859 | } 1860 | 1861 | { 1862 | var res any = New(arrData).Get("b.kJh21ay.Hjk2") 1863 | assert("fccDcc", res, "Get") 1864 | } 1865 | { 1866 | var res any = New(arrData).Get("b.kJh21ay.Hjk12", "defVal") 1867 | assert("defVal", res, "Get") 1868 | } 1869 | 1870 | { 1871 | var res any = New(arrData).Find("b.kJh21ay.Hjk2") 1872 | assert("fccDcc", res, "Find") 1873 | } 1874 | { 1875 | var res any = New(arrData).Find("b.kJh21ay.Hjk12") 1876 | assert(nil, res, "Find") 1877 | } 1878 | 1879 | { 1880 | var res any = New(arrData).Sub("b.kJh21ay.Hjk2").Value() 1881 | assert("fccDcc", res, "Sub") 1882 | } 1883 | { 1884 | var res any = New(arrData).Sub("b.kJh21ay.Hjk12").Value() 1885 | assert(nil, res, "Sub") 1886 | } 1887 | 1888 | { 1889 | var res any = New(arrData).Search("b", "kJh21ay", "Hjk2").Value() 1890 | assert("fccDcc", res, "Search") 1891 | } 1892 | { 1893 | var res any = New(arrData).Search("b", "kJh21ay", "Hjk12").Value() 1894 | assert(nil, res, "Search") 1895 | } 1896 | 1897 | { 1898 | var res any = New(arrData).Sub("b.dd").Index(1).Value() 1899 | assert("ddddd", res, "Index") 1900 | } 1901 | { 1902 | var res any = New(arrData).Sub("b.dd").Index(6).Value() 1903 | assert(nil, res, "Index") 1904 | } 1905 | 1906 | { 1907 | arr := New(arrData) 1908 | arr.Set("qqqyyy", "b", "ff", 222) 1909 | 1910 | var res any = arr.Sub("b.ff.222").Value() 1911 | assert("qqqyyy", res, "Set") 1912 | } 1913 | 1914 | { 1915 | arr := New(arrData) 1916 | arr.Sub("b.dd").SetIndex("qqqyyySetIndex", 1) 1917 | 1918 | var res any = arr.Sub("b.dd.1").Value() 1919 | assert("qqqyyySetIndex", res, "SetIndex") 1920 | } 1921 | 1922 | { 1923 | arr := New(arrData) 1924 | 1925 | var res0 any = arr.Sub("b.hh.2225").Value() 1926 | assert("hddddd", res0, "Delete") 1927 | 1928 | err := arr.Delete("b", "hh", 2225) 1929 | if err != nil { 1930 | t.Error(err.Error()) 1931 | } 1932 | 1933 | var res any = arr.Sub("b.hh.2225").Value() 1934 | assert(nil, res, "Delete") 1935 | } 1936 | 1937 | { 1938 | arr := New(arrData) 1939 | 1940 | var res0 any = arr.Sub("b.d.e").Value() 1941 | assert("eee", res0, "DeleteKey") 1942 | 1943 | err := arr.DeleteKey("b.d.e") 1944 | if err != nil { 1945 | t.Error(err.Error()) 1946 | } 1947 | 1948 | var res any = arr.Sub("b.d.e").Value() 1949 | assert(nil, res, "DeleteKey") 1950 | } 1951 | 1952 | } 1953 | 1954 | func Example() { 1955 | Get(arrData, "b.hhTy3.666.3") 1956 | } 1957 | -------------------------------------------------------------------------------- /array/utils.go: -------------------------------------------------------------------------------- 1 | package array 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "html/template" 7 | "reflect" 8 | "strconv" 9 | ) 10 | 11 | // 转为数组 12 | func toStringMap(i any) map[string]any { 13 | var m = map[string]any{} 14 | 15 | switch v := i.(type) { 16 | case map[any]any: 17 | for k, val := range v { 18 | m[toString(k)] = val 19 | } 20 | return m 21 | case map[string]any: 22 | return v 23 | case string: 24 | jsonStringToObject(v, &m) 25 | return m 26 | default: 27 | return m 28 | } 29 | } 30 | 31 | func toString(i any) string { 32 | i = indirectToStringerOrError(i) 33 | 34 | res, ok := toIntString(i) 35 | if ok { 36 | return res 37 | } 38 | 39 | switch s := i.(type) { 40 | case []byte: 41 | return string(s) 42 | case string: 43 | return s 44 | case bool: 45 | return strconv.FormatBool(s) 46 | case template.HTML: 47 | return string(s) 48 | case template.URL: 49 | return string(s) 50 | case template.JS: 51 | return string(s) 52 | case template.CSS: 53 | return string(s) 54 | case template.HTMLAttr: 55 | return string(s) 56 | case nil: 57 | return "" 58 | case fmt.Stringer: 59 | return s.String() 60 | case error: 61 | return s.Error() 62 | default: 63 | return "" 64 | } 65 | } 66 | 67 | func toIntString(i any) (string, bool) { 68 | switch s := i.(type) { 69 | case float64: 70 | return strconv.FormatFloat(s, 'f', -1, 64), true 71 | case float32: 72 | return strconv.FormatFloat(float64(s), 'f', -1, 32), true 73 | case int: 74 | return strconv.Itoa(s), true 75 | case int64: 76 | return strconv.FormatInt(s, 10), true 77 | case int32: 78 | return strconv.Itoa(int(s)), true 79 | case int16: 80 | return strconv.FormatInt(int64(s), 10), true 81 | case int8: 82 | return strconv.FormatInt(int64(s), 10), true 83 | case uint: 84 | return strconv.FormatUint(uint64(s), 10), true 85 | case uint64: 86 | return strconv.FormatUint(uint64(s), 10), true 87 | case uint32: 88 | return strconv.FormatUint(uint64(s), 10), true 89 | case uint16: 90 | return strconv.FormatUint(uint64(s), 10), true 91 | case uint8: 92 | return strconv.FormatUint(uint64(s), 10), true 93 | } 94 | 95 | return "", false 96 | } 97 | 98 | // json 转换 99 | func jsonStringToObject(s string, v any) error { 100 | data := []byte(s) 101 | return json.Unmarshal(data, v) 102 | } 103 | 104 | func indirectToStringerOrError(a any) any { 105 | if a == nil { 106 | return nil 107 | } 108 | 109 | var errorType = reflect.TypeOf((*error)(nil)).Elem() 110 | var fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() 111 | 112 | v := reflect.ValueOf(a) 113 | for !v.Type().Implements(fmtStringerType) && 114 | !v.Type().Implements(errorType) && 115 | v.Kind() == reflect.Ptr && 116 | !v.IsNil() { 117 | v = v.Elem() 118 | } 119 | 120 | return v.Interface() 121 | } 122 | 123 | func formatPath(path []string) []any { 124 | p := make([]any, 0) 125 | for _, v := range path { 126 | p = append(p, v) 127 | } 128 | 129 | return p 130 | } 131 | 132 | func formatPathString(path []any) []string { 133 | p := make([]string, 0) 134 | for _, v := range path { 135 | p = append(p, toString(v)) 136 | } 137 | 138 | return p 139 | } 140 | -------------------------------------------------------------------------------- /array/utils_test.go: -------------------------------------------------------------------------------- 1 | package array 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "html/template" 7 | "testing" 8 | ) 9 | 10 | func Test_ToString(t *testing.T) { 11 | assert := assertDeepEqualT(t) 12 | 13 | var jn json.Number 14 | _ = json.Unmarshal([]byte("8"), &jn) 15 | type Key struct { 16 | k string 17 | } 18 | key := &Key{"foo"} 19 | 20 | tests := []struct { 21 | input any 22 | expect string 23 | }{ 24 | {int(8), "8"}, 25 | {int8(8), "8"}, 26 | {int16(8), "8"}, 27 | {int32(8), "8"}, 28 | {int64(8), "8"}, 29 | {uint(8), "8"}, 30 | {uint8(8), "8"}, 31 | {uint16(8), "8"}, 32 | {uint32(8), "8"}, 33 | {uint64(8), "8"}, 34 | {float32(8.31), "8.31"}, 35 | {float64(8.31), "8.31"}, 36 | {jn, "8"}, 37 | {true, "true"}, 38 | {false, "false"}, 39 | {nil, ""}, 40 | {[]byte("one time"), "one time"}, 41 | {"one more time", "one more time"}, 42 | {template.HTML("one time"), "one time"}, 43 | {template.URL("http://somehost.foo"), "http://somehost.foo"}, 44 | {template.JS("(1+2)"), "(1+2)"}, 45 | {template.CSS("a"), "a"}, 46 | {template.HTMLAttr("a"), "a"}, 47 | // errors 48 | {testing.T{}, ""}, 49 | {key, ""}, 50 | } 51 | 52 | for i, test := range tests { 53 | errmsg := fmt.Sprintf("i = %d", i) 54 | 55 | v := toString(test.input) 56 | 57 | assert(v, test.expect, errmsg) 58 | } 59 | } 60 | 61 | func Test_ToStringMap(t *testing.T) { 62 | assert := assertDeepEqualT(t) 63 | 64 | tests := []struct { 65 | input any 66 | expect map[string]any 67 | }{ 68 | {map[any]any{"tag": "tags", "group": "groups"}, map[string]any{"tag": "tags", "group": "groups"}}, 69 | {map[string]any{"tag": "tags", "group": "groups"}, map[string]any{"tag": "tags", "group": "groups"}}, 70 | {`{"tag": "tags", "group": "groups"}`, map[string]any{"tag": "tags", "group": "groups"}}, 71 | {`{"tag": "tags", "group": true}`, map[string]any{"tag": "tags", "group": true}}, 72 | 73 | // errors 74 | {nil, map[string]any{}}, 75 | {testing.T{}, map[string]any{}}, 76 | {"", map[string]any{}}, 77 | } 78 | 79 | for i, test := range tests { 80 | errmsg := fmt.Sprintf("i = %d", i) 81 | 82 | v := toStringMap(test.input) 83 | 84 | assert(v, test.expect, errmsg) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/deatil/go-array 2 | 3 | go 1.18 4 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deatil/go-array/55f1d6b064e91ec9f1124a10de430da0351bc041/logo.png --------------------------------------------------------------------------------