├── .github └── workflows │ └── ci.yml ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── OWNERS ├── README.md ├── SECURITY.md ├── SECURITY_CONTACTS ├── code-of-conduct.md ├── doc.go ├── go.mod ├── hack └── verify-apidiff.sh ├── internal └── golang │ └── encoding │ └── json │ ├── bench_test.go │ ├── decode.go │ ├── decode_test.go │ ├── encode.go │ ├── encode_test.go │ ├── example_marshaling_test.go │ ├── example_test.go │ ├── example_text_marshaling_test.go │ ├── fold.go │ ├── fold_test.go │ ├── fuzz.go │ ├── fuzz_test.go │ ├── indent.go │ ├── kubernetes_patch.go │ ├── kubernetes_patch_test.go │ ├── number_test.go │ ├── scanner.go │ ├── scanner_test.go │ ├── stream.go │ ├── stream_test.go │ ├── tables.go │ ├── tagkey_test.go │ ├── tags.go │ ├── tags_test.go │ └── testdata │ └── code.json.gz ├── json.go ├── json_test.go └── testdata └── bench.json /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci-pipeline 2 | 3 | on: 4 | push: 5 | branches: [ "*" ] 6 | pull_request: 7 | branches: [ "*" ] 8 | 9 | jobs: 10 | 11 | ci: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | go: [ '', '1.x' ] # '' uses go.mod, 1.x uses latest go 16 | steps: 17 | - uses: actions/checkout@v2 18 | with: 19 | path: src/sigs.k8s.io/json/ 20 | 21 | - name: Set up Go 22 | uses: actions/setup-go@v3 23 | with: 24 | go-version-file: 'src/sigs.k8s.io/json/go.mod' 25 | go-version: ${{ matrix.go }} 26 | check-latest: true 27 | 28 | - name: Test 29 | run: | 30 | cd ${GITHUB_WORKSPACE}/src/sigs.k8s.io/json/ 31 | make test 32 | - name: Benchmark 33 | run: | 34 | cd ${GITHUB_WORKSPACE}/src/sigs.k8s.io/json/ 35 | make benchmark 36 | - name: Vet 37 | run: | 38 | cd ${GITHUB_WORKSPACE}/src/sigs.k8s.io/json/ 39 | make vet 40 | - name: fmt 41 | run: | 42 | cd ${GITHUB_WORKSPACE}/src/sigs.k8s.io/json/ 43 | make fmt 44 | git diff --exit-code 45 | 46 | apidiff: 47 | runs-on: ubuntu-latest 48 | if: github.base_ref 49 | steps: 50 | - name: Checkout old code 51 | uses: actions/checkout@v2 52 | with: 53 | ref: ${{ github.base_ref }} 54 | path: "old" 55 | - name: Checkout new code 56 | uses: actions/checkout@v2 57 | with: 58 | path: "new" 59 | - name: Install Go 60 | uses: actions/setup-go@v3 61 | with: 62 | go-version-file: 'new/go.mod' 63 | - name: Add GOBIN to PATH 64 | run: echo "$(go env GOPATH)/bin" >> $GITHUB_PATH 65 | - name: Install dependencies 66 | run: go install golang.org/x/exp/cmd/apidiff@latest 67 | - name: APIDiff 68 | run: ./hack/verify-apidiff.sh -d ../old 69 | working-directory: "new" 70 | 71 | 72 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Welcome to Kubernetes. We are excited about the prospect of you joining our [community](https://git.k8s.io/community)! The Kubernetes community abides by the CNCF [code of conduct](code-of-conduct.md). Here is an excerpt: 4 | 5 | _As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities._ 6 | 7 | ## Criteria for adding code here 8 | 9 | This library adapts the stdlib `encoding/json` decoder to be compatible with 10 | Kubernetes JSON decoding, and is not expected to actively add new features. 11 | 12 | It may be updated with changes from the stdlib `encoding/json` decoder. 13 | 14 | Any code that is added must: 15 | * Have full unit test and benchmark coverage 16 | * Be backward compatible with the existing exposed go API 17 | * Have zero external dependencies 18 | * Preserve existing benchmark performance 19 | * Preserve compatibility with existing decoding behavior of `UnmarshalCaseSensitivePreserveInts()` or `UnmarshalStrict()` 20 | * Avoid use of `unsafe` 21 | 22 | ## Getting Started 23 | 24 | We have full documentation on how to get started contributing here: 25 | 26 | 29 | 30 | - [Contributor License Agreement](https://git.k8s.io/community/CLA.md) Kubernetes projects require that you sign a Contributor License Agreement (CLA) before we can accept your pull requests 31 | - [Kubernetes Contributor Guide](https://git.k8s.io/community/contributors/guide) - Main contributor documentation, or you can just jump directly to the [contributing section](https://git.k8s.io/community/contributors/guide#contributing) 32 | - [Contributor Cheat Sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet) - Common resources for existing developers 33 | 34 | ## Community, discussion, contribution, and support 35 | 36 | You can reach the maintainers of this project via the 37 | [sig-api-machinery mailing list / channels](https://github.com/kubernetes/community/tree/master/sig-api-machinery#contact). 38 | 39 | ## Mentorship 40 | 41 | - [Mentoring Initiatives](https://git.k8s.io/community/mentoring) - We have a diverse set of mentorship programs available that are always looking for volunteers! 42 | 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Files other than internal/golang/* licensed under: 2 | 3 | 4 | Apache License 5 | Version 2.0, January 2004 6 | http://www.apache.org/licenses/ 7 | 8 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 9 | 10 | 1. Definitions. 11 | 12 | "License" shall mean the terms and conditions for use, reproduction, 13 | and distribution as defined by Sections 1 through 9 of this document. 14 | 15 | "Licensor" shall mean the copyright owner or entity authorized by 16 | the copyright owner that is granting the License. 17 | 18 | "Legal Entity" shall mean the union of the acting entity and all 19 | other entities that control, are controlled by, or are under common 20 | control with that entity. For the purposes of this definition, 21 | "control" means (i) the power, direct or indirect, to cause the 22 | direction or management of such entity, whether by contract or 23 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 24 | outstanding shares, or (iii) beneficial ownership of such entity. 25 | 26 | "You" (or "Your") shall mean an individual or Legal Entity 27 | exercising permissions granted by this License. 28 | 29 | "Source" form shall mean the preferred form for making modifications, 30 | including but not limited to software source code, documentation 31 | source, and configuration files. 32 | 33 | "Object" form shall mean any form resulting from mechanical 34 | transformation or translation of a Source form, including but 35 | not limited to compiled object code, generated documentation, 36 | and conversions to other media types. 37 | 38 | "Work" shall mean the work of authorship, whether in Source or 39 | Object form, made available under the License, as indicated by a 40 | copyright notice that is included in or attached to the work 41 | (an example is provided in the Appendix below). 42 | 43 | "Derivative Works" shall mean any work, whether in Source or Object 44 | form, that is based on (or derived from) the Work and for which the 45 | editorial revisions, annotations, elaborations, or other modifications 46 | represent, as a whole, an original work of authorship. For the purposes 47 | of this License, Derivative Works shall not include works that remain 48 | separable from, or merely link (or bind by name) to the interfaces of, 49 | the Work and Derivative Works thereof. 50 | 51 | "Contribution" shall mean any work of authorship, including 52 | the original version of the Work and any modifications or additions 53 | to that Work or Derivative Works thereof, that is intentionally 54 | submitted to Licensor for inclusion in the Work by the copyright owner 55 | or by an individual or Legal Entity authorized to submit on behalf of 56 | the copyright owner. For the purposes of this definition, "submitted" 57 | means any form of electronic, verbal, or written communication sent 58 | to the Licensor or its representatives, including but not limited to 59 | communication on electronic mailing lists, source code control systems, 60 | and issue tracking systems that are managed by, or on behalf of, the 61 | Licensor for the purpose of discussing and improving the Work, but 62 | excluding communication that is conspicuously marked or otherwise 63 | designated in writing by the copyright owner as "Not a Contribution." 64 | 65 | "Contributor" shall mean Licensor and any individual or Legal Entity 66 | on behalf of whom a Contribution has been received by Licensor and 67 | subsequently incorporated within the Work. 68 | 69 | 2. Grant of Copyright License. Subject to the terms and conditions of 70 | this License, each Contributor hereby grants to You a perpetual, 71 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 72 | copyright license to reproduce, prepare Derivative Works of, 73 | publicly display, publicly perform, sublicense, and distribute the 74 | Work and such Derivative Works in Source or Object form. 75 | 76 | 3. Grant of Patent License. Subject to the terms and conditions of 77 | this License, each Contributor hereby grants to You a perpetual, 78 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 79 | (except as stated in this section) patent license to make, have made, 80 | use, offer to sell, sell, import, and otherwise transfer the Work, 81 | where such license applies only to those patent claims licensable 82 | by such Contributor that are necessarily infringed by their 83 | Contribution(s) alone or by combination of their Contribution(s) 84 | with the Work to which such Contribution(s) was submitted. If You 85 | institute patent litigation against any entity (including a 86 | cross-claim or counterclaim in a lawsuit) alleging that the Work 87 | or a Contribution incorporated within the Work constitutes direct 88 | or contributory patent infringement, then any patent licenses 89 | granted to You under this License for that Work shall terminate 90 | as of the date such litigation is filed. 91 | 92 | 4. Redistribution. You may reproduce and distribute copies of the 93 | Work or Derivative Works thereof in any medium, with or without 94 | modifications, and in Source or Object form, provided that You 95 | meet the following conditions: 96 | 97 | (a) You must give any other recipients of the Work or 98 | Derivative Works a copy of this License; and 99 | 100 | (b) You must cause any modified files to carry prominent notices 101 | stating that You changed the files; and 102 | 103 | (c) You must retain, in the Source form of any Derivative Works 104 | that You distribute, all copyright, patent, trademark, and 105 | attribution notices from the Source form of the Work, 106 | excluding those notices that do not pertain to any part of 107 | the Derivative Works; and 108 | 109 | (d) If the Work includes a "NOTICE" text file as part of its 110 | distribution, then any Derivative Works that You distribute must 111 | include a readable copy of the attribution notices contained 112 | within such NOTICE file, excluding those notices that do not 113 | pertain to any part of the Derivative Works, in at least one 114 | of the following places: within a NOTICE text file distributed 115 | as part of the Derivative Works; within the Source form or 116 | documentation, if provided along with the Derivative Works; or, 117 | within a display generated by the Derivative Works, if and 118 | wherever such third-party notices normally appear. The contents 119 | of the NOTICE file are for informational purposes only and 120 | do not modify the License. You may add Your own attribution 121 | notices within Derivative Works that You distribute, alongside 122 | or as an addendum to the NOTICE text from the Work, provided 123 | that such additional attribution notices cannot be construed 124 | as modifying the License. 125 | 126 | You may add Your own copyright statement to Your modifications and 127 | may provide additional or different license terms and conditions 128 | for use, reproduction, or distribution of Your modifications, or 129 | for any such Derivative Works as a whole, provided Your use, 130 | reproduction, and distribution of the Work otherwise complies with 131 | the conditions stated in this License. 132 | 133 | 5. Submission of Contributions. Unless You explicitly state otherwise, 134 | any Contribution intentionally submitted for inclusion in the Work 135 | by You to the Licensor shall be under the terms and conditions of 136 | this License, without any additional terms or conditions. 137 | Notwithstanding the above, nothing herein shall supersede or modify 138 | the terms of any separate license agreement you may have executed 139 | with Licensor regarding such Contributions. 140 | 141 | 6. Trademarks. This License does not grant permission to use the trade 142 | names, trademarks, service marks, or product names of the Licensor, 143 | except as required for reasonable and customary use in describing the 144 | origin of the Work and reproducing the content of the NOTICE file. 145 | 146 | 7. Disclaimer of Warranty. Unless required by applicable law or 147 | agreed to in writing, Licensor provides the Work (and each 148 | Contributor provides its Contributions) on an "AS IS" BASIS, 149 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 150 | implied, including, without limitation, any warranties or conditions 151 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 152 | PARTICULAR PURPOSE. You are solely responsible for determining the 153 | appropriateness of using or redistributing the Work and assume any 154 | risks associated with Your exercise of permissions under this License. 155 | 156 | 8. Limitation of Liability. In no event and under no legal theory, 157 | whether in tort (including negligence), contract, or otherwise, 158 | unless required by applicable law (such as deliberate and grossly 159 | negligent acts) or agreed to in writing, shall any Contributor be 160 | liable to You for damages, including any direct, indirect, special, 161 | incidental, or consequential damages of any character arising as a 162 | result of this License or out of the use or inability to use the 163 | Work (including but not limited to damages for loss of goodwill, 164 | work stoppage, computer failure or malfunction, or any and all 165 | other commercial damages or losses), even if such Contributor 166 | has been advised of the possibility of such damages. 167 | 168 | 9. Accepting Warranty or Additional Liability. While redistributing 169 | the Work or Derivative Works thereof, You may choose to offer, 170 | and charge a fee for, acceptance of support, warranty, indemnity, 171 | or other liability obligations and/or rights consistent with this 172 | License. However, in accepting such obligations, You may act only 173 | on Your own behalf and on Your sole responsibility, not on behalf 174 | of any other Contributor, and only if You agree to indemnify, 175 | defend, and hold each Contributor harmless for any liability 176 | incurred by, or claims asserted against, such Contributor by reason 177 | of your accepting any such warranty or additional liability. 178 | 179 | END OF TERMS AND CONDITIONS 180 | 181 | APPENDIX: How to apply the Apache License to your work. 182 | 183 | To apply the Apache License to your work, attach the following 184 | boilerplate notice, with the fields enclosed by brackets "{}" 185 | replaced with your own identifying information. (Don't include 186 | the brackets!) The text should be enclosed in the appropriate 187 | comment syntax for the file format. We also recommend that a 188 | file or class name and description of purpose be included on the 189 | same "printed page" as the copyright notice for easier 190 | identification within third-party archives. 191 | 192 | Copyright {yyyy} {name of copyright owner} 193 | 194 | Licensed under the Apache License, Version 2.0 (the "License"); 195 | you may not use this file except in compliance with the License. 196 | You may obtain a copy of the License at 197 | 198 | http://www.apache.org/licenses/LICENSE-2.0 199 | 200 | Unless required by applicable law or agreed to in writing, software 201 | distributed under the License is distributed on an "AS IS" BASIS, 202 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 203 | See the License for the specific language governing permissions and 204 | limitations under the License. 205 | 206 | 207 | ------------------ 208 | 209 | internal/golang/* files licensed under: 210 | 211 | 212 | Copyright (c) 2009 The Go Authors. All rights reserved. 213 | 214 | Redistribution and use in source and binary forms, with or without 215 | modification, are permitted provided that the following conditions are 216 | met: 217 | 218 | * Redistributions of source code must retain the above copyright 219 | notice, this list of conditions and the following disclaimer. 220 | * Redistributions in binary form must reproduce the above 221 | copyright notice, this list of conditions and the following disclaimer 222 | in the documentation and/or other materials provided with the 223 | distribution. 224 | * Neither the name of Google Inc. nor the names of its 225 | contributors may be used to endorse or promote products derived from 226 | this software without specific prior written permission. 227 | 228 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 229 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 230 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 231 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 232 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 233 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 234 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 235 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 236 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 237 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 238 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 239 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: default build test benchmark fmt vet 2 | 3 | default: build 4 | 5 | build: 6 | go build ./... 7 | 8 | test: 9 | go test sigs.k8s.io/json/... 10 | 11 | benchmark: 12 | go test sigs.k8s.io/json -bench . -benchmem 13 | 14 | fmt: 15 | go mod tidy 16 | gofmt -s -w *.go 17 | 18 | vet: 19 | go vet sigs.k8s.io/json 20 | 21 | @echo "checking for external dependencies" 22 | @deps=$$(go list -f '{{ if not (or .Standard .Module.Main) }}{{.ImportPath}}{{ end }}' -deps sigs.k8s.io/json/... || true); \ 23 | if [ -n "$${deps}" ]; then \ 24 | echo "only stdlib dependencies allowed, found:"; \ 25 | echo "$${deps}"; \ 26 | exit 1; \ 27 | fi 28 | 29 | @echo "checking for unsafe use" 30 | @unsafe=$$(go list -f '{{.ImportPath}} depends on {{.Imports}}' sigs.k8s.io/json/... | grep unsafe || true); \ 31 | if [ -n "$${unsafe}" ]; then \ 32 | echo "no dependencies on unsafe allowed, found:"; \ 33 | echo "$${unsafe}"; \ 34 | exit 1; \ 35 | fi 36 | -------------------------------------------------------------------------------- /OWNERS: -------------------------------------------------------------------------------- 1 | # See the OWNERS docs at https://go.k8s.io/owners 2 | 3 | approvers: 4 | - deads2k 5 | - jpbetz 6 | - liggitt 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sigs.k8s.io/json 2 | 3 | [![Go Reference](https://pkg.go.dev/badge/sigs.k8s.io/json.svg)](https://pkg.go.dev/sigs.k8s.io/json) 4 | 5 | ## Introduction 6 | 7 | This library is a subproject of [sig-api-machinery](https://github.com/kubernetes/community/tree/master/sig-api-machinery#json). 8 | It provides case-sensitive, integer-preserving JSON unmarshaling functions based on `encoding/json` `Unmarshal()`. 9 | 10 | ## Compatibility 11 | 12 | The `UnmarshalCaseSensitivePreserveInts()` function behaves like `encoding/json#Unmarshal()` with the following differences: 13 | 14 | - JSON object keys are treated case-sensitively. 15 | Object keys must exactly match json tag names (for tagged struct fields) 16 | or struct field names (for untagged struct fields). 17 | - JSON integers are unmarshaled into `interface{}` fields as an `int64` instead of a 18 | `float64` when possible, falling back to `float64` on any parse or overflow error. 19 | - Syntax errors do not return an `encoding/json` `*SyntaxError` error. 20 | Instead, they return an error which can be passed to `SyntaxErrorOffset()` to obtain an offset. 21 | 22 | ## Additional capabilities 23 | 24 | The `UnmarshalStrict()` function decodes identically to `UnmarshalCaseSensitivePreserveInts()`, 25 | and also returns non-fatal strict errors encountered while decoding: 26 | 27 | - Duplicate fields encountered 28 | - Unknown fields encountered 29 | 30 | ### Community, discussion, contribution, and support 31 | 32 | You can reach the maintainers of this project via the 33 | [sig-api-machinery mailing list / channels](https://github.com/kubernetes/community/tree/master/sig-api-machinery#contact). 34 | 35 | ### Code of conduct 36 | 37 | Participation in the Kubernetes community is governed by the [Kubernetes Code of Conduct](code-of-conduct.md). 38 | 39 | [owners]: https://git.k8s.io/community/contributors/guide/owners.md 40 | [Creative Commons 4.0]: https://git.k8s.io/website/LICENSE 41 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Security Announcements 4 | 5 | Join the [kubernetes-security-announce] group for security and vulnerability announcements. 6 | 7 | You can also subscribe to an RSS feed of the above using [this link][kubernetes-security-announce-rss]. 8 | 9 | ## Reporting a Vulnerability 10 | 11 | Instructions for reporting a vulnerability can be found on the 12 | [Kubernetes Security and Disclosure Information] page. 13 | 14 | ## Supported Versions 15 | 16 | Information about supported Kubernetes versions can be found on the 17 | [Kubernetes version and version skew support policy] page on the Kubernetes website. 18 | 19 | [kubernetes-security-announce]: https://groups.google.com/forum/#!forum/kubernetes-security-announce 20 | [kubernetes-security-announce-rss]: https://groups.google.com/forum/feed/kubernetes-security-announce/msgs/rss_v2_0.xml?num=50 21 | [Kubernetes version and version skew support policy]: https://kubernetes.io/docs/setup/release/version-skew-policy/#supported-versions 22 | [Kubernetes Security and Disclosure Information]: https://kubernetes.io/docs/reference/issues-security/security/#report-a-vulnerability 23 | -------------------------------------------------------------------------------- /SECURITY_CONTACTS: -------------------------------------------------------------------------------- 1 | # Defined below are the security contacts for this repo. 2 | # 3 | # They are the contact point for the Product Security Committee to reach out 4 | # to for triaging and handling of incoming issues. 5 | # 6 | # The below names agree to abide by the 7 | # [Embargo Policy](https://git.k8s.io/security/private-distributors-list.md#embargo-policy) 8 | # and will be removed and replaced if they violate that agreement. 9 | # 10 | # DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE 11 | # INSTRUCTIONS AT https://kubernetes.io/security/ 12 | 13 | deads2k 14 | lavalamp 15 | liggitt 16 | -------------------------------------------------------------------------------- /code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Kubernetes Community Code of Conduct 2 | 3 | Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md) 4 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2021 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package json // import "sigs.k8s.io/json" 18 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module sigs.k8s.io/json 2 | 3 | go 1.23 4 | -------------------------------------------------------------------------------- /hack/verify-apidiff.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2021 The Kubernetes Authors. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o nounset 19 | set -o pipefail 20 | 21 | function usage { 22 | local script="$(basename $0)" 23 | 24 | echo >&2 "Usage: ${script} [-r | -d ] 25 | 26 | This script should be run at the root of a module. 27 | 28 | -r 29 | Compare the exported API of the local working copy with the 30 | exported API of the local repo at the specified branch or tag. 31 | 32 | -d 33 | Compare the exported API of the local working copy with the 34 | exported API of the specified directory, which should point 35 | to the root of a different version of the same module. 36 | 37 | Examples: 38 | ${script} -r master 39 | ${script} -r v1.10.0 40 | ${script} -r release-1.10 41 | ${script} -d /path/to/historical/version 42 | " 43 | exit 1 44 | } 45 | 46 | ref="" 47 | dir="" 48 | while getopts r:d: o 49 | do case "$o" in 50 | r) ref="$OPTARG";; 51 | d) dir="$OPTARG";; 52 | [?]) usage;; 53 | esac 54 | done 55 | 56 | # If REF and DIR are empty, print usage and error 57 | if [[ -z "${ref}" && -z "${dir}" ]]; then 58 | usage; 59 | fi 60 | # If REF and DIR are both set, print usage and error 61 | if [[ -n "${ref}" && -n "${dir}" ]]; then 62 | usage; 63 | fi 64 | 65 | if ! which apidiff > /dev/null; then 66 | echo "Installing golang.org/x/exp/cmd/apidiff..." 67 | pushd "${TMPDIR:-/tmp}" > /dev/null 68 | go install golang.org/x/exp/cmd/apidiff@latest 69 | popd > /dev/null 70 | fi 71 | 72 | output=$(mktemp -d -t "apidiff.output.XXXX") 73 | cleanup_output () { rm -fr "${output}"; } 74 | trap cleanup_output EXIT 75 | 76 | # If ref is set, clone . to temp dir at $ref, and set $dir to the temp dir 77 | clone="" 78 | base="${dir}" 79 | if [[ -n "${ref}" ]]; then 80 | base="${ref}" 81 | clone=$(mktemp -d -t "apidiff.clone.XXXX") 82 | cleanup_clone_and_output () { rm -fr "${clone}"; cleanup_output; } 83 | trap cleanup_clone_and_output EXIT 84 | git clone . -q --no-tags -b "${ref}" "${clone}" 85 | dir="${clone}" 86 | fi 87 | 88 | pushd "${dir}" >/dev/null 89 | echo "Inspecting API of '${base}'..." 90 | go list . > "${output}/packages.txt" 91 | for pkg in $(cat ${output}/packages.txt); do 92 | mkdir -p "${output}/${pkg}" 93 | apidiff -w "${output}/${pkg}/apidiff.output" "${pkg}" 94 | done 95 | popd >/dev/null 96 | 97 | retval=0 98 | 99 | echo "Comparing with '${base}'..." 100 | for pkg in $(go list .); do 101 | # New packages are ok 102 | if [ ! -f "${output}/${pkg}/apidiff.output" ]; then 103 | continue 104 | fi 105 | 106 | # Check for incompatible changes to previous packages 107 | incompatible=$(apidiff -incompatible "${output}/${pkg}/apidiff.output" "${pkg}") 108 | if [[ -n "${incompatible}" ]]; then 109 | echo >&2 "FAIL: ${pkg} contains incompatible changes: 110 | ${incompatible} 111 | " 112 | retval=1 113 | fi 114 | done 115 | 116 | # Check for removed packages 117 | removed=$(comm -23 "${output}/packages.txt" <(go list .)) 118 | if [[ -n "${removed}" ]]; then 119 | echo >&2 "FAIL: removed packages: 120 | ${removed} 121 | " 122 | retval=1 123 | fi 124 | 125 | exit $retval 126 | -------------------------------------------------------------------------------- /internal/golang/encoding/json/bench_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Large data benchmark. 6 | // The JSON data is a summary of agl's changes in the 7 | // go, webkit, and chromium open source projects. 8 | // We benchmark converting between the JSON form 9 | // and in-memory data structures. 10 | 11 | package json 12 | 13 | import ( 14 | "bytes" 15 | "compress/gzip" 16 | "fmt" 17 | "io" 18 | "os" 19 | "reflect" 20 | "regexp" 21 | "runtime" 22 | "strings" 23 | "sync" 24 | "testing" 25 | ) 26 | 27 | type codeResponse struct { 28 | Tree *codeNode `json:"tree"` 29 | Username string `json:"username"` 30 | } 31 | 32 | type codeNode struct { 33 | Name string `json:"name"` 34 | Kids []*codeNode `json:"kids"` 35 | CLWeight float64 `json:"cl_weight"` 36 | Touches int `json:"touches"` 37 | MinT int64 `json:"min_t"` 38 | MaxT int64 `json:"max_t"` 39 | MeanT int64 `json:"mean_t"` 40 | } 41 | 42 | var codeJSON []byte 43 | var codeStruct codeResponse 44 | 45 | func codeInit() { 46 | f, err := os.Open("testdata/code.json.gz") 47 | if err != nil { 48 | panic(err) 49 | } 50 | defer f.Close() 51 | gz, err := gzip.NewReader(f) 52 | if err != nil { 53 | panic(err) 54 | } 55 | data, err := io.ReadAll(gz) 56 | if err != nil { 57 | panic(err) 58 | } 59 | 60 | codeJSON = data 61 | 62 | if err := Unmarshal(codeJSON, &codeStruct); err != nil { 63 | panic("unmarshal code.json: " + err.Error()) 64 | } 65 | 66 | if data, err = Marshal(&codeStruct); err != nil { 67 | panic("marshal code.json: " + err.Error()) 68 | } 69 | 70 | if !bytes.Equal(data, codeJSON) { 71 | println("different lengths", len(data), len(codeJSON)) 72 | for i := 0; i < len(data) && i < len(codeJSON); i++ { 73 | if data[i] != codeJSON[i] { 74 | println("re-marshal: changed at byte", i) 75 | println("orig: ", string(codeJSON[i-10:i+10])) 76 | println("new: ", string(data[i-10:i+10])) 77 | break 78 | } 79 | } 80 | panic("re-marshal code.json: different result") 81 | } 82 | } 83 | 84 | func BenchmarkCodeEncoder(b *testing.B) { 85 | b.ReportAllocs() 86 | if codeJSON == nil { 87 | b.StopTimer() 88 | codeInit() 89 | b.StartTimer() 90 | } 91 | b.RunParallel(func(pb *testing.PB) { 92 | enc := NewEncoder(io.Discard) 93 | for pb.Next() { 94 | if err := enc.Encode(&codeStruct); err != nil { 95 | b.Fatalf("Encode error: %v", err) 96 | } 97 | } 98 | }) 99 | b.SetBytes(int64(len(codeJSON))) 100 | } 101 | 102 | func BenchmarkCodeEncoderError(b *testing.B) { 103 | b.ReportAllocs() 104 | if codeJSON == nil { 105 | b.StopTimer() 106 | codeInit() 107 | b.StartTimer() 108 | } 109 | 110 | // Trigger an error in Marshal with cyclic data. 111 | type Dummy struct { 112 | Name string 113 | Next *Dummy 114 | } 115 | dummy := Dummy{Name: "Dummy"} 116 | dummy.Next = &dummy 117 | 118 | b.RunParallel(func(pb *testing.PB) { 119 | enc := NewEncoder(io.Discard) 120 | for pb.Next() { 121 | if err := enc.Encode(&codeStruct); err != nil { 122 | b.Fatalf("Encode error: %v", err) 123 | } 124 | if _, err := Marshal(dummy); err == nil { 125 | b.Fatal("Marshal error: got nil, want non-nil") 126 | } 127 | } 128 | }) 129 | b.SetBytes(int64(len(codeJSON))) 130 | } 131 | 132 | func BenchmarkCodeMarshal(b *testing.B) { 133 | b.ReportAllocs() 134 | if codeJSON == nil { 135 | b.StopTimer() 136 | codeInit() 137 | b.StartTimer() 138 | } 139 | b.RunParallel(func(pb *testing.PB) { 140 | for pb.Next() { 141 | if _, err := Marshal(&codeStruct); err != nil { 142 | b.Fatalf("Marshal error: %v", err) 143 | } 144 | } 145 | }) 146 | b.SetBytes(int64(len(codeJSON))) 147 | } 148 | 149 | func BenchmarkCodeMarshalError(b *testing.B) { 150 | b.ReportAllocs() 151 | if codeJSON == nil { 152 | b.StopTimer() 153 | codeInit() 154 | b.StartTimer() 155 | } 156 | 157 | // Trigger an error in Marshal with cyclic data. 158 | type Dummy struct { 159 | Name string 160 | Next *Dummy 161 | } 162 | dummy := Dummy{Name: "Dummy"} 163 | dummy.Next = &dummy 164 | 165 | b.RunParallel(func(pb *testing.PB) { 166 | for pb.Next() { 167 | if _, err := Marshal(&codeStruct); err != nil { 168 | b.Fatalf("Marshal error: %v", err) 169 | } 170 | if _, err := Marshal(dummy); err == nil { 171 | b.Fatal("Marshal error: got nil, want non-nil") 172 | } 173 | } 174 | }) 175 | b.SetBytes(int64(len(codeJSON))) 176 | } 177 | 178 | func benchMarshalBytes(n int) func(*testing.B) { 179 | sample := []byte("hello world") 180 | // Use a struct pointer, to avoid an allocation when passing it as an 181 | // interface parameter to Marshal. 182 | v := &struct { 183 | Bytes []byte 184 | }{ 185 | bytes.Repeat(sample, (n/len(sample))+1)[:n], 186 | } 187 | return func(b *testing.B) { 188 | for i := 0; i < b.N; i++ { 189 | if _, err := Marshal(v); err != nil { 190 | b.Fatalf("Marshal error: %v", err) 191 | } 192 | } 193 | } 194 | } 195 | 196 | func benchMarshalBytesError(n int) func(*testing.B) { 197 | sample := []byte("hello world") 198 | // Use a struct pointer, to avoid an allocation when passing it as an 199 | // interface parameter to Marshal. 200 | v := &struct { 201 | Bytes []byte 202 | }{ 203 | bytes.Repeat(sample, (n/len(sample))+1)[:n], 204 | } 205 | 206 | // Trigger an error in Marshal with cyclic data. 207 | type Dummy struct { 208 | Name string 209 | Next *Dummy 210 | } 211 | dummy := Dummy{Name: "Dummy"} 212 | dummy.Next = &dummy 213 | 214 | return func(b *testing.B) { 215 | for i := 0; i < b.N; i++ { 216 | if _, err := Marshal(v); err != nil { 217 | b.Fatalf("Marshal error: %v", err) 218 | } 219 | if _, err := Marshal(dummy); err == nil { 220 | b.Fatal("Marshal error: got nil, want non-nil") 221 | } 222 | } 223 | } 224 | } 225 | 226 | func BenchmarkMarshalBytes(b *testing.B) { 227 | b.ReportAllocs() 228 | // 32 fits within encodeState.scratch. 229 | b.Run("32", benchMarshalBytes(32)) 230 | // 256 doesn't fit in encodeState.scratch, but is small enough to 231 | // allocate and avoid the slower base64.NewEncoder. 232 | b.Run("256", benchMarshalBytes(256)) 233 | // 4096 is large enough that we want to avoid allocating for it. 234 | b.Run("4096", benchMarshalBytes(4096)) 235 | } 236 | 237 | func BenchmarkMarshalBytesError(b *testing.B) { 238 | b.ReportAllocs() 239 | // 32 fits within encodeState.scratch. 240 | b.Run("32", benchMarshalBytesError(32)) 241 | // 256 doesn't fit in encodeState.scratch, but is small enough to 242 | // allocate and avoid the slower base64.NewEncoder. 243 | b.Run("256", benchMarshalBytesError(256)) 244 | // 4096 is large enough that we want to avoid allocating for it. 245 | b.Run("4096", benchMarshalBytesError(4096)) 246 | } 247 | 248 | func BenchmarkMarshalMap(b *testing.B) { 249 | b.ReportAllocs() 250 | m := map[string]int{ 251 | "key3": 3, 252 | "key2": 2, 253 | "key1": 1, 254 | } 255 | b.RunParallel(func(pb *testing.PB) { 256 | for pb.Next() { 257 | if _, err := Marshal(m); err != nil { 258 | b.Fatal("Marshal:", err) 259 | } 260 | } 261 | }) 262 | } 263 | 264 | func BenchmarkCodeDecoder(b *testing.B) { 265 | b.ReportAllocs() 266 | if codeJSON == nil { 267 | b.StopTimer() 268 | codeInit() 269 | b.StartTimer() 270 | } 271 | b.RunParallel(func(pb *testing.PB) { 272 | var buf bytes.Buffer 273 | dec := NewDecoder(&buf) 274 | var r codeResponse 275 | for pb.Next() { 276 | buf.Write(codeJSON) 277 | // hide EOF 278 | buf.WriteByte('\n') 279 | buf.WriteByte('\n') 280 | buf.WriteByte('\n') 281 | if err := dec.Decode(&r); err != nil { 282 | b.Fatalf("Decode error: %v", err) 283 | } 284 | } 285 | }) 286 | b.SetBytes(int64(len(codeJSON))) 287 | } 288 | 289 | func BenchmarkUnicodeDecoder(b *testing.B) { 290 | b.ReportAllocs() 291 | j := []byte(`"\uD83D\uDE01"`) 292 | b.SetBytes(int64(len(j))) 293 | r := bytes.NewReader(j) 294 | dec := NewDecoder(r) 295 | var out string 296 | b.ResetTimer() 297 | for i := 0; i < b.N; i++ { 298 | if err := dec.Decode(&out); err != nil { 299 | b.Fatalf("Decode error: %v", err) 300 | } 301 | r.Seek(0, 0) 302 | } 303 | } 304 | 305 | func BenchmarkDecoderStream(b *testing.B) { 306 | b.ReportAllocs() 307 | b.StopTimer() 308 | var buf bytes.Buffer 309 | dec := NewDecoder(&buf) 310 | buf.WriteString(`"` + strings.Repeat("x", 1000000) + `"` + "\n\n\n") 311 | var x any 312 | if err := dec.Decode(&x); err != nil { 313 | b.Fatalf("Decode error: %v", err) 314 | } 315 | ones := strings.Repeat(" 1\n", 300000) + "\n\n\n" 316 | b.StartTimer() 317 | for i := 0; i < b.N; i++ { 318 | if i%300000 == 0 { 319 | buf.WriteString(ones) 320 | } 321 | x = nil 322 | switch err := dec.Decode(&x); { 323 | case err != nil: 324 | b.Fatalf("Decode error: %v", err) 325 | case x != 1.0: 326 | b.Fatalf("Decode: got %v want 1.0", i) 327 | } 328 | } 329 | } 330 | 331 | func BenchmarkCodeUnmarshal(b *testing.B) { 332 | b.ReportAllocs() 333 | if codeJSON == nil { 334 | b.StopTimer() 335 | codeInit() 336 | b.StartTimer() 337 | } 338 | b.RunParallel(func(pb *testing.PB) { 339 | for pb.Next() { 340 | var r codeResponse 341 | if err := Unmarshal(codeJSON, &r); err != nil { 342 | b.Fatalf("Unmarshal error: %v", err) 343 | } 344 | } 345 | }) 346 | b.SetBytes(int64(len(codeJSON))) 347 | } 348 | 349 | func BenchmarkCodeUnmarshalReuse(b *testing.B) { 350 | b.ReportAllocs() 351 | if codeJSON == nil { 352 | b.StopTimer() 353 | codeInit() 354 | b.StartTimer() 355 | } 356 | b.RunParallel(func(pb *testing.PB) { 357 | var r codeResponse 358 | for pb.Next() { 359 | if err := Unmarshal(codeJSON, &r); err != nil { 360 | b.Fatalf("Unmarshal error: %v", err) 361 | } 362 | } 363 | }) 364 | b.SetBytes(int64(len(codeJSON))) 365 | } 366 | 367 | func BenchmarkUnmarshalString(b *testing.B) { 368 | b.ReportAllocs() 369 | data := []byte(`"hello, world"`) 370 | b.RunParallel(func(pb *testing.PB) { 371 | var s string 372 | for pb.Next() { 373 | if err := Unmarshal(data, &s); err != nil { 374 | b.Fatalf("Unmarshal error: %v", err) 375 | } 376 | } 377 | }) 378 | } 379 | 380 | func BenchmarkUnmarshalFloat64(b *testing.B) { 381 | b.ReportAllocs() 382 | data := []byte(`3.14`) 383 | b.RunParallel(func(pb *testing.PB) { 384 | var f float64 385 | for pb.Next() { 386 | if err := Unmarshal(data, &f); err != nil { 387 | b.Fatalf("Unmarshal error: %v", err) 388 | } 389 | } 390 | }) 391 | } 392 | 393 | func BenchmarkUnmarshalInt64(b *testing.B) { 394 | b.ReportAllocs() 395 | data := []byte(`3`) 396 | b.RunParallel(func(pb *testing.PB) { 397 | var x int64 398 | for pb.Next() { 399 | if err := Unmarshal(data, &x); err != nil { 400 | b.Fatalf("Unmarshal error: %v", err) 401 | } 402 | } 403 | }) 404 | } 405 | 406 | func BenchmarkUnmarshalMap(b *testing.B) { 407 | b.ReportAllocs() 408 | data := []byte(`{"key1":"value1","key2":"value2","key3":"value3"}`) 409 | b.RunParallel(func(pb *testing.PB) { 410 | x := make(map[string]string, 3) 411 | for pb.Next() { 412 | if err := Unmarshal(data, &x); err != nil { 413 | b.Fatalf("Unmarshal error: %v", err) 414 | } 415 | } 416 | }) 417 | } 418 | 419 | func BenchmarkIssue10335(b *testing.B) { 420 | b.ReportAllocs() 421 | j := []byte(`{"a":{ }}`) 422 | b.RunParallel(func(pb *testing.PB) { 423 | var s struct{} 424 | for pb.Next() { 425 | if err := Unmarshal(j, &s); err != nil { 426 | b.Fatalf("Unmarshal error: %v", err) 427 | } 428 | } 429 | }) 430 | } 431 | 432 | func BenchmarkIssue34127(b *testing.B) { 433 | b.ReportAllocs() 434 | j := struct { 435 | Bar string `json:"bar,string"` 436 | }{ 437 | Bar: `foobar`, 438 | } 439 | b.RunParallel(func(pb *testing.PB) { 440 | for pb.Next() { 441 | if _, err := Marshal(&j); err != nil { 442 | b.Fatalf("Marshal error: %v", err) 443 | } 444 | } 445 | }) 446 | } 447 | 448 | func BenchmarkUnmapped(b *testing.B) { 449 | b.ReportAllocs() 450 | j := []byte(`{"s": "hello", "y": 2, "o": {"x": 0}, "a": [1, 99, {"x": 1}]}`) 451 | b.RunParallel(func(pb *testing.PB) { 452 | var s struct{} 453 | for pb.Next() { 454 | if err := Unmarshal(j, &s); err != nil { 455 | b.Fatalf("Unmarshal error: %v", err) 456 | } 457 | } 458 | }) 459 | } 460 | 461 | func BenchmarkTypeFieldsCache(b *testing.B) { 462 | b.ReportAllocs() 463 | var maxTypes int = 1e6 464 | maxTypes = 1e3 // restrict cache sizes on builders 465 | 466 | // Dynamically generate many new types. 467 | types := make([]reflect.Type, maxTypes) 468 | fs := []reflect.StructField{{ 469 | Type: reflect.TypeFor[string](), 470 | Index: []int{0}, 471 | }} 472 | for i := range types { 473 | fs[0].Name = fmt.Sprintf("TypeFieldsCache%d", i) 474 | types[i] = reflect.StructOf(fs) 475 | } 476 | 477 | // clearClear clears the cache. Other JSON operations, must not be running. 478 | clearCache := func() { 479 | fieldCache = sync.Map{} 480 | } 481 | 482 | // MissTypes tests the performance of repeated cache misses. 483 | // This measures the time to rebuild a cache of size nt. 484 | for nt := 1; nt <= maxTypes; nt *= 10 { 485 | ts := types[:nt] 486 | b.Run(fmt.Sprintf("MissTypes%d", nt), func(b *testing.B) { 487 | nc := runtime.GOMAXPROCS(0) 488 | for i := 0; i < b.N; i++ { 489 | clearCache() 490 | var wg sync.WaitGroup 491 | for j := 0; j < nc; j++ { 492 | wg.Add(1) 493 | go func(j int) { 494 | for _, t := range ts[(j*len(ts))/nc : ((j+1)*len(ts))/nc] { 495 | cachedTypeFields(t) 496 | } 497 | wg.Done() 498 | }(j) 499 | } 500 | wg.Wait() 501 | } 502 | }) 503 | } 504 | 505 | // HitTypes tests the performance of repeated cache hits. 506 | // This measures the average time of each cache lookup. 507 | for nt := 1; nt <= maxTypes; nt *= 10 { 508 | // Pre-warm a cache of size nt. 509 | clearCache() 510 | for _, t := range types[:nt] { 511 | cachedTypeFields(t) 512 | } 513 | b.Run(fmt.Sprintf("HitTypes%d", nt), func(b *testing.B) { 514 | b.RunParallel(func(pb *testing.PB) { 515 | for pb.Next() { 516 | cachedTypeFields(types[0]) 517 | } 518 | }) 519 | }) 520 | } 521 | } 522 | 523 | func BenchmarkEncodeMarshaler(b *testing.B) { 524 | b.ReportAllocs() 525 | 526 | m := struct { 527 | A int 528 | B RawMessage 529 | }{} 530 | 531 | b.RunParallel(func(pb *testing.PB) { 532 | enc := NewEncoder(io.Discard) 533 | 534 | for pb.Next() { 535 | if err := enc.Encode(&m); err != nil { 536 | b.Fatalf("Encode error: %v", err) 537 | } 538 | } 539 | }) 540 | } 541 | 542 | func BenchmarkEncoderEncode(b *testing.B) { 543 | b.ReportAllocs() 544 | type T struct { 545 | X, Y string 546 | } 547 | v := &T{"foo", "bar"} 548 | b.RunParallel(func(pb *testing.PB) { 549 | for pb.Next() { 550 | if err := NewEncoder(io.Discard).Encode(v); err != nil { 551 | b.Fatalf("Encode error: %v", err) 552 | } 553 | } 554 | }) 555 | } 556 | 557 | func BenchmarkNumberIsValid(b *testing.B) { 558 | s := "-61657.61667E+61673" 559 | for i := 0; i < b.N; i++ { 560 | isValidNumber(s) 561 | } 562 | } 563 | 564 | func BenchmarkNumberIsValidRegexp(b *testing.B) { 565 | var jsonNumberRegexp = regexp.MustCompile(`^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$`) 566 | s := "-61657.61667E+61673" 567 | for i := 0; i < b.N; i++ { 568 | jsonNumberRegexp.MatchString(s) 569 | } 570 | } 571 | 572 | func BenchmarkUnmarshalNumber(b *testing.B) { 573 | b.ReportAllocs() 574 | data := []byte(`"-61657.61667E+61673"`) 575 | var number Number 576 | for i := 0; i < b.N; i++ { 577 | if err := Unmarshal(data, &number); err != nil { 578 | b.Fatal("Unmarshal:", err) 579 | } 580 | } 581 | } 582 | -------------------------------------------------------------------------------- /internal/golang/encoding/json/encode_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import ( 8 | "bytes" 9 | "encoding" 10 | "fmt" 11 | "log" 12 | "math" 13 | "reflect" 14 | "regexp" 15 | "runtime/debug" 16 | "strconv" 17 | "testing" 18 | ) 19 | 20 | type Optionals struct { 21 | Sr string `json:"sr"` 22 | So string `json:"so,omitempty"` 23 | Sw string `json:"-"` 24 | 25 | Ir int `json:"omitempty"` // actually named omitempty, not an option 26 | Io int `json:"io,omitempty"` 27 | 28 | Slr []string `json:"slr,random"` 29 | Slo []string `json:"slo,omitempty"` 30 | 31 | Mr map[string]any `json:"mr"` 32 | Mo map[string]any `json:",omitempty"` 33 | 34 | Fr float64 `json:"fr"` 35 | Fo float64 `json:"fo,omitempty"` 36 | 37 | Br bool `json:"br"` 38 | Bo bool `json:"bo,omitempty"` 39 | 40 | Ur uint `json:"ur"` 41 | Uo uint `json:"uo,omitempty"` 42 | 43 | Str struct{} `json:"str"` 44 | Sto struct{} `json:"sto,omitempty"` 45 | } 46 | 47 | func TestOmitEmpty(t *testing.T) { 48 | var want = `{ 49 | "sr": "", 50 | "omitempty": 0, 51 | "slr": null, 52 | "mr": {}, 53 | "fr": 0, 54 | "br": false, 55 | "ur": 0, 56 | "str": {}, 57 | "sto": {} 58 | }` 59 | var o Optionals 60 | o.Sw = "something" 61 | o.Mr = map[string]any{} 62 | o.Mo = map[string]any{} 63 | 64 | got, err := MarshalIndent(&o, "", " ") 65 | if err != nil { 66 | t.Fatalf("MarshalIndent error: %v", err) 67 | } 68 | if got := string(got); got != want { 69 | t.Errorf("MarshalIndent:\n\tgot: %s\n\twant: %s\n", indentNewlines(got), indentNewlines(want)) 70 | } 71 | } 72 | 73 | type StringTag struct { 74 | BoolStr bool `json:",string"` 75 | IntStr int64 `json:",string"` 76 | UintptrStr uintptr `json:",string"` 77 | StrStr string `json:",string"` 78 | NumberStr Number `json:",string"` 79 | } 80 | 81 | func TestRoundtripStringTag(t *testing.T) { 82 | tests := []struct { 83 | CaseName 84 | in StringTag 85 | want string // empty to just test that we roundtrip 86 | }{{ 87 | CaseName: Name("AllTypes"), 88 | in: StringTag{ 89 | BoolStr: true, 90 | IntStr: 42, 91 | UintptrStr: 44, 92 | StrStr: "xzbit", 93 | NumberStr: "46", 94 | }, 95 | want: `{ 96 | "BoolStr": "true", 97 | "IntStr": "42", 98 | "UintptrStr": "44", 99 | "StrStr": "\"xzbit\"", 100 | "NumberStr": "46" 101 | }`, 102 | }, { 103 | // See golang.org/issues/38173. 104 | CaseName: Name("StringDoubleEscapes"), 105 | in: StringTag{ 106 | StrStr: "\b\f\n\r\t\"\\", 107 | NumberStr: "0", // just to satisfy the roundtrip 108 | }, 109 | want: `{ 110 | "BoolStr": "false", 111 | "IntStr": "0", 112 | "UintptrStr": "0", 113 | "StrStr": "\"\\b\\f\\n\\r\\t\\\"\\\\\"", 114 | "NumberStr": "0" 115 | }`, 116 | }} 117 | for _, tt := range tests { 118 | t.Run(tt.Name, func(t *testing.T) { 119 | got, err := MarshalIndent(&tt.in, "", "\t") 120 | if err != nil { 121 | t.Fatalf("%s: MarshalIndent error: %v", tt.Where, err) 122 | } 123 | if got := string(got); got != tt.want { 124 | t.Fatalf("%s: MarshalIndent:\n\tgot: %s\n\twant: %s", tt.Where, stripWhitespace(got), stripWhitespace(tt.want)) 125 | } 126 | 127 | // Verify that it round-trips. 128 | var s2 StringTag 129 | if err := Unmarshal(got, &s2); err != nil { 130 | t.Fatalf("%s: Decode error: %v", tt.Where, err) 131 | } 132 | if !reflect.DeepEqual(s2, tt.in) { 133 | t.Fatalf("%s: Decode:\n\tinput: %s\n\tgot: %#v\n\twant: %#v", tt.Where, indentNewlines(string(got)), s2, tt.in) 134 | } 135 | }) 136 | } 137 | } 138 | 139 | // byte slices are special even if they're renamed types. 140 | type renamedByte byte 141 | type renamedByteSlice []byte 142 | type renamedRenamedByteSlice []renamedByte 143 | 144 | func TestEncodeRenamedByteSlice(t *testing.T) { 145 | s := renamedByteSlice("abc") 146 | got, err := Marshal(s) 147 | if err != nil { 148 | t.Fatalf("Marshal error: %v", err) 149 | } 150 | want := `"YWJj"` 151 | if string(got) != want { 152 | t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want) 153 | } 154 | r := renamedRenamedByteSlice("abc") 155 | got, err = Marshal(r) 156 | if err != nil { 157 | t.Fatalf("Marshal error: %v", err) 158 | } 159 | if string(got) != want { 160 | t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want) 161 | } 162 | } 163 | 164 | type SamePointerNoCycle struct { 165 | Ptr1, Ptr2 *SamePointerNoCycle 166 | } 167 | 168 | var samePointerNoCycle = &SamePointerNoCycle{} 169 | 170 | type PointerCycle struct { 171 | Ptr *PointerCycle 172 | } 173 | 174 | var pointerCycle = &PointerCycle{} 175 | 176 | type PointerCycleIndirect struct { 177 | Ptrs []any 178 | } 179 | 180 | type RecursiveSlice []RecursiveSlice 181 | 182 | var ( 183 | pointerCycleIndirect = &PointerCycleIndirect{} 184 | mapCycle = make(map[string]any) 185 | sliceCycle = []any{nil} 186 | sliceNoCycle = []any{nil, nil} 187 | recursiveSliceCycle = []RecursiveSlice{nil} 188 | ) 189 | 190 | func init() { 191 | ptr := &SamePointerNoCycle{} 192 | samePointerNoCycle.Ptr1 = ptr 193 | samePointerNoCycle.Ptr2 = ptr 194 | 195 | pointerCycle.Ptr = pointerCycle 196 | pointerCycleIndirect.Ptrs = []any{pointerCycleIndirect} 197 | 198 | mapCycle["x"] = mapCycle 199 | sliceCycle[0] = sliceCycle 200 | sliceNoCycle[1] = sliceNoCycle[:1] 201 | for i := startDetectingCyclesAfter; i > 0; i-- { 202 | sliceNoCycle = []any{sliceNoCycle} 203 | } 204 | recursiveSliceCycle[0] = recursiveSliceCycle 205 | } 206 | 207 | func TestSamePointerNoCycle(t *testing.T) { 208 | if _, err := Marshal(samePointerNoCycle); err != nil { 209 | t.Fatalf("Marshal error: %v", err) 210 | } 211 | } 212 | 213 | func TestSliceNoCycle(t *testing.T) { 214 | if _, err := Marshal(sliceNoCycle); err != nil { 215 | t.Fatalf("Marshal error: %v", err) 216 | } 217 | } 218 | 219 | func TestUnsupportedValues(t *testing.T) { 220 | tests := []struct { 221 | CaseName 222 | in any 223 | }{ 224 | {Name(""), math.NaN()}, 225 | {Name(""), math.Inf(-1)}, 226 | {Name(""), math.Inf(1)}, 227 | {Name(""), pointerCycle}, 228 | {Name(""), pointerCycleIndirect}, 229 | {Name(""), mapCycle}, 230 | {Name(""), sliceCycle}, 231 | {Name(""), recursiveSliceCycle}, 232 | } 233 | for _, tt := range tests { 234 | t.Run(tt.Name, func(t *testing.T) { 235 | if _, err := Marshal(tt.in); err != nil { 236 | if _, ok := err.(*UnsupportedValueError); !ok { 237 | t.Errorf("%s: Marshal error:\n\tgot: %T\n\twant: %T", tt.Where, err, new(UnsupportedValueError)) 238 | } 239 | } else { 240 | t.Errorf("%s: Marshal error: got nil, want non-nil", tt.Where) 241 | } 242 | }) 243 | } 244 | } 245 | 246 | // Issue 43207 247 | func TestMarshalTextFloatMap(t *testing.T) { 248 | m := map[textfloat]string{ 249 | textfloat(math.NaN()): "1", 250 | textfloat(math.NaN()): "1", 251 | } 252 | got, err := Marshal(m) 253 | if err != nil { 254 | t.Errorf("Marshal error: %v", err) 255 | } 256 | want := `{"TF:NaN":"1","TF:NaN":"1"}` 257 | if string(got) != want { 258 | t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want) 259 | } 260 | } 261 | 262 | // Ref has Marshaler and Unmarshaler methods with pointer receiver. 263 | type Ref int 264 | 265 | func (*Ref) MarshalJSON() ([]byte, error) { 266 | return []byte(`"ref"`), nil 267 | } 268 | 269 | func (r *Ref) UnmarshalJSON([]byte) error { 270 | *r = 12 271 | return nil 272 | } 273 | 274 | // Val has Marshaler methods with value receiver. 275 | type Val int 276 | 277 | func (Val) MarshalJSON() ([]byte, error) { 278 | return []byte(`"val"`), nil 279 | } 280 | 281 | // RefText has Marshaler and Unmarshaler methods with pointer receiver. 282 | type RefText int 283 | 284 | func (*RefText) MarshalText() ([]byte, error) { 285 | return []byte(`"ref"`), nil 286 | } 287 | 288 | func (r *RefText) UnmarshalText([]byte) error { 289 | *r = 13 290 | return nil 291 | } 292 | 293 | // ValText has Marshaler methods with value receiver. 294 | type ValText int 295 | 296 | func (ValText) MarshalText() ([]byte, error) { 297 | return []byte(`"val"`), nil 298 | } 299 | 300 | func TestRefValMarshal(t *testing.T) { 301 | var s = struct { 302 | R0 Ref 303 | R1 *Ref 304 | R2 RefText 305 | R3 *RefText 306 | V0 Val 307 | V1 *Val 308 | V2 ValText 309 | V3 *ValText 310 | }{ 311 | R0: 12, 312 | R1: new(Ref), 313 | R2: 14, 314 | R3: new(RefText), 315 | V0: 13, 316 | V1: new(Val), 317 | V2: 15, 318 | V3: new(ValText), 319 | } 320 | const want = `{"R0":"ref","R1":"ref","R2":"\"ref\"","R3":"\"ref\"","V0":"val","V1":"val","V2":"\"val\"","V3":"\"val\""}` 321 | b, err := Marshal(&s) 322 | if err != nil { 323 | t.Fatalf("Marshal error: %v", err) 324 | } 325 | if got := string(b); got != want { 326 | t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want) 327 | } 328 | } 329 | 330 | // C implements Marshaler and returns unescaped JSON. 331 | type C int 332 | 333 | func (C) MarshalJSON() ([]byte, error) { 334 | return []byte(`"<&>"`), nil 335 | } 336 | 337 | // CText implements Marshaler and returns unescaped text. 338 | type CText int 339 | 340 | func (CText) MarshalText() ([]byte, error) { 341 | return []byte(`"<&>"`), nil 342 | } 343 | 344 | func TestMarshalerEscaping(t *testing.T) { 345 | var c C 346 | want := `"\u003c\u0026\u003e"` 347 | b, err := Marshal(c) 348 | if err != nil { 349 | t.Fatalf("Marshal error: %v", err) 350 | } 351 | if got := string(b); got != want { 352 | t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want) 353 | } 354 | 355 | var ct CText 356 | want = `"\"\u003c\u0026\u003e\""` 357 | b, err = Marshal(ct) 358 | if err != nil { 359 | t.Fatalf("Marshal error: %v", err) 360 | } 361 | if got := string(b); got != want { 362 | t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want) 363 | } 364 | } 365 | 366 | func TestAnonymousFields(t *testing.T) { 367 | tests := []struct { 368 | CaseName 369 | makeInput func() any // Function to create input value 370 | want string // Expected JSON output 371 | }{{ 372 | // Both S1 and S2 have a field named X. From the perspective of S, 373 | // it is ambiguous which one X refers to. 374 | // This should not serialize either field. 375 | CaseName: Name("AmbiguousField"), 376 | makeInput: func() any { 377 | type ( 378 | S1 struct{ x, X int } 379 | S2 struct{ x, X int } 380 | S struct { 381 | S1 382 | S2 383 | } 384 | ) 385 | return S{S1{1, 2}, S2{3, 4}} 386 | }, 387 | want: `{}`, 388 | }, { 389 | CaseName: Name("DominantField"), 390 | // Both S1 and S2 have a field named X, but since S has an X field as 391 | // well, it takes precedence over S1.X and S2.X. 392 | makeInput: func() any { 393 | type ( 394 | S1 struct{ x, X int } 395 | S2 struct{ x, X int } 396 | S struct { 397 | S1 398 | S2 399 | x, X int 400 | } 401 | ) 402 | return S{S1{1, 2}, S2{3, 4}, 5, 6} 403 | }, 404 | want: `{"X":6}`, 405 | }, { 406 | // Unexported embedded field of non-struct type should not be serialized. 407 | CaseName: Name("UnexportedEmbeddedInt"), 408 | makeInput: func() any { 409 | type ( 410 | myInt int 411 | S struct{ myInt } 412 | ) 413 | return S{5} 414 | }, 415 | want: `{}`, 416 | }, { 417 | // Exported embedded field of non-struct type should be serialized. 418 | CaseName: Name("ExportedEmbeddedInt"), 419 | makeInput: func() any { 420 | type ( 421 | MyInt int 422 | S struct{ MyInt } 423 | ) 424 | return S{5} 425 | }, 426 | want: `{"MyInt":5}`, 427 | }, { 428 | // Unexported embedded field of pointer to non-struct type 429 | // should not be serialized. 430 | CaseName: Name("UnexportedEmbeddedIntPointer"), 431 | makeInput: func() any { 432 | type ( 433 | myInt int 434 | S struct{ *myInt } 435 | ) 436 | s := S{new(myInt)} 437 | *s.myInt = 5 438 | return s 439 | }, 440 | want: `{}`, 441 | }, { 442 | // Exported embedded field of pointer to non-struct type 443 | // should be serialized. 444 | CaseName: Name("ExportedEmbeddedIntPointer"), 445 | makeInput: func() any { 446 | type ( 447 | MyInt int 448 | S struct{ *MyInt } 449 | ) 450 | s := S{new(MyInt)} 451 | *s.MyInt = 5 452 | return s 453 | }, 454 | want: `{"MyInt":5}`, 455 | }, { 456 | // Exported fields of embedded structs should have their 457 | // exported fields be serialized regardless of whether the struct types 458 | // themselves are exported. 459 | CaseName: Name("EmbeddedStruct"), 460 | makeInput: func() any { 461 | type ( 462 | s1 struct{ x, X int } 463 | S2 struct{ y, Y int } 464 | S struct { 465 | s1 466 | S2 467 | } 468 | ) 469 | return S{s1{1, 2}, S2{3, 4}} 470 | }, 471 | want: `{"X":2,"Y":4}`, 472 | }, { 473 | // Exported fields of pointers to embedded structs should have their 474 | // exported fields be serialized regardless of whether the struct types 475 | // themselves are exported. 476 | CaseName: Name("EmbeddedStructPointer"), 477 | makeInput: func() any { 478 | type ( 479 | s1 struct{ x, X int } 480 | S2 struct{ y, Y int } 481 | S struct { 482 | *s1 483 | *S2 484 | } 485 | ) 486 | return S{&s1{1, 2}, &S2{3, 4}} 487 | }, 488 | want: `{"X":2,"Y":4}`, 489 | }, { 490 | // Exported fields on embedded unexported structs at multiple levels 491 | // of nesting should still be serialized. 492 | CaseName: Name("NestedStructAndInts"), 493 | makeInput: func() any { 494 | type ( 495 | MyInt1 int 496 | MyInt2 int 497 | myInt int 498 | s2 struct { 499 | MyInt2 500 | myInt 501 | } 502 | s1 struct { 503 | MyInt1 504 | myInt 505 | s2 506 | } 507 | S struct { 508 | s1 509 | myInt 510 | } 511 | ) 512 | return S{s1{1, 2, s2{3, 4}}, 6} 513 | }, 514 | want: `{"MyInt1":1,"MyInt2":3}`, 515 | }, { 516 | // If an anonymous struct pointer field is nil, we should ignore 517 | // the embedded fields behind it. Not properly doing so may 518 | // result in the wrong output or reflect panics. 519 | CaseName: Name("EmbeddedFieldBehindNilPointer"), 520 | makeInput: func() any { 521 | type ( 522 | S2 struct{ Field string } 523 | S struct{ *S2 } 524 | ) 525 | return S{} 526 | }, 527 | want: `{}`, 528 | }} 529 | 530 | for _, tt := range tests { 531 | t.Run(tt.Name, func(t *testing.T) { 532 | b, err := Marshal(tt.makeInput()) 533 | if err != nil { 534 | t.Fatalf("%s: Marshal error: %v", tt.Where, err) 535 | } 536 | if string(b) != tt.want { 537 | t.Fatalf("%s: Marshal:\n\tgot: %s\n\twant: %s", tt.Where, b, tt.want) 538 | } 539 | }) 540 | } 541 | } 542 | 543 | type BugA struct { 544 | S string 545 | } 546 | 547 | type BugB struct { 548 | BugA 549 | S string 550 | } 551 | 552 | type BugC struct { 553 | S string 554 | } 555 | 556 | // Legal Go: We never use the repeated embedded field (S). 557 | type BugX struct { 558 | A int 559 | BugA 560 | BugB 561 | } 562 | 563 | // golang.org/issue/16042. 564 | // Even if a nil interface value is passed in, as long as 565 | // it implements Marshaler, it should be marshaled. 566 | type nilJSONMarshaler string 567 | 568 | func (nm *nilJSONMarshaler) MarshalJSON() ([]byte, error) { 569 | if nm == nil { 570 | return Marshal("0zenil0") 571 | } 572 | return Marshal("zenil:" + string(*nm)) 573 | } 574 | 575 | // golang.org/issue/34235. 576 | // Even if a nil interface value is passed in, as long as 577 | // it implements encoding.TextMarshaler, it should be marshaled. 578 | type nilTextMarshaler string 579 | 580 | func (nm *nilTextMarshaler) MarshalText() ([]byte, error) { 581 | if nm == nil { 582 | return []byte("0zenil0"), nil 583 | } 584 | return []byte("zenil:" + string(*nm)), nil 585 | } 586 | 587 | // See golang.org/issue/16042 and golang.org/issue/34235. 588 | func TestNilMarshal(t *testing.T) { 589 | tests := []struct { 590 | CaseName 591 | in any 592 | want string 593 | }{ 594 | {Name(""), nil, `null`}, 595 | {Name(""), new(float64), `0`}, 596 | {Name(""), []any(nil), `null`}, 597 | {Name(""), []string(nil), `null`}, 598 | {Name(""), map[string]string(nil), `null`}, 599 | {Name(""), []byte(nil), `null`}, 600 | {Name(""), struct{ M string }{"gopher"}, `{"M":"gopher"}`}, 601 | {Name(""), struct{ M Marshaler }{}, `{"M":null}`}, 602 | {Name(""), struct{ M Marshaler }{(*nilJSONMarshaler)(nil)}, `{"M":"0zenil0"}`}, 603 | {Name(""), struct{ M any }{(*nilJSONMarshaler)(nil)}, `{"M":null}`}, 604 | {Name(""), struct{ M encoding.TextMarshaler }{}, `{"M":null}`}, 605 | {Name(""), struct{ M encoding.TextMarshaler }{(*nilTextMarshaler)(nil)}, `{"M":"0zenil0"}`}, 606 | {Name(""), struct{ M any }{(*nilTextMarshaler)(nil)}, `{"M":null}`}, 607 | } 608 | for _, tt := range tests { 609 | t.Run(tt.Name, func(t *testing.T) { 610 | switch got, err := Marshal(tt.in); { 611 | case err != nil: 612 | t.Fatalf("%s: Marshal error: %v", tt.Where, err) 613 | case string(got) != tt.want: 614 | t.Fatalf("%s: Marshal:\n\tgot: %s\n\twant: %s", tt.Where, got, tt.want) 615 | } 616 | }) 617 | } 618 | } 619 | 620 | // Issue 5245. 621 | func TestEmbeddedBug(t *testing.T) { 622 | v := BugB{ 623 | BugA{"A"}, 624 | "B", 625 | } 626 | b, err := Marshal(v) 627 | if err != nil { 628 | t.Fatal("Marshal error:", err) 629 | } 630 | want := `{"S":"B"}` 631 | got := string(b) 632 | if got != want { 633 | t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want) 634 | } 635 | // Now check that the duplicate field, S, does not appear. 636 | x := BugX{ 637 | A: 23, 638 | } 639 | b, err = Marshal(x) 640 | if err != nil { 641 | t.Fatal("Marshal error:", err) 642 | } 643 | want = `{"A":23}` 644 | got = string(b) 645 | if got != want { 646 | t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want) 647 | } 648 | } 649 | 650 | type BugD struct { // Same as BugA after tagging. 651 | XXX string `json:"S"` 652 | } 653 | 654 | // BugD's tagged S field should dominate BugA's. 655 | type BugY struct { 656 | BugA 657 | BugD 658 | } 659 | 660 | // Test that a field with a tag dominates untagged fields. 661 | func TestTaggedFieldDominates(t *testing.T) { 662 | v := BugY{ 663 | BugA{"BugA"}, 664 | BugD{"BugD"}, 665 | } 666 | b, err := Marshal(v) 667 | if err != nil { 668 | t.Fatal("Marshal error:", err) 669 | } 670 | want := `{"S":"BugD"}` 671 | got := string(b) 672 | if got != want { 673 | t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want) 674 | } 675 | } 676 | 677 | // There are no tags here, so S should not appear. 678 | type BugZ struct { 679 | BugA 680 | BugC 681 | BugY // Contains a tagged S field through BugD; should not dominate. 682 | } 683 | 684 | func TestDuplicatedFieldDisappears(t *testing.T) { 685 | v := BugZ{ 686 | BugA{"BugA"}, 687 | BugC{"BugC"}, 688 | BugY{ 689 | BugA{"nested BugA"}, 690 | BugD{"nested BugD"}, 691 | }, 692 | } 693 | b, err := Marshal(v) 694 | if err != nil { 695 | t.Fatal("Marshal error:", err) 696 | } 697 | want := `{}` 698 | got := string(b) 699 | if got != want { 700 | t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want) 701 | } 702 | } 703 | 704 | func TestIssue10281(t *testing.T) { 705 | type Foo struct { 706 | N Number 707 | } 708 | x := Foo{Number(`invalid`)} 709 | 710 | if _, err := Marshal(&x); err == nil { 711 | t.Fatalf("Marshal error: got nil, want non-nil") 712 | } 713 | } 714 | 715 | func TestMarshalErrorAndReuseEncodeState(t *testing.T) { 716 | // Disable the GC temporarily to prevent encodeState's in Pool being cleaned away during the test. 717 | percent := debug.SetGCPercent(-1) 718 | defer debug.SetGCPercent(percent) 719 | 720 | // Trigger an error in Marshal with cyclic data. 721 | type Dummy struct { 722 | Name string 723 | Next *Dummy 724 | } 725 | dummy := Dummy{Name: "Dummy"} 726 | dummy.Next = &dummy 727 | if _, err := Marshal(dummy); err == nil { 728 | t.Errorf("Marshal error: got nil, want non-nil") 729 | } 730 | 731 | type Data struct { 732 | A string 733 | I int 734 | } 735 | want := Data{A: "a", I: 1} 736 | b, err := Marshal(want) 737 | if err != nil { 738 | t.Errorf("Marshal error: %v", err) 739 | } 740 | 741 | var got Data 742 | if err := Unmarshal(b, &got); err != nil { 743 | t.Errorf("Unmarshal error: %v", err) 744 | } 745 | if got != want { 746 | t.Errorf("Unmarshal:\n\tgot: %v\n\twant: %v", got, want) 747 | } 748 | } 749 | 750 | func TestHTMLEscape(t *testing.T) { 751 | var b, want bytes.Buffer 752 | m := `{"M":"foo &` + "\xe2\x80\xa8 \xe2\x80\xa9" + `"}` 753 | want.Write([]byte(`{"M":"\u003chtml\u003efoo \u0026\u2028 \u2029\u003c/html\u003e"}`)) 754 | HTMLEscape(&b, []byte(m)) 755 | if !bytes.Equal(b.Bytes(), want.Bytes()) { 756 | t.Errorf("HTMLEscape:\n\tgot: %s\n\twant: %s", b.Bytes(), want.Bytes()) 757 | } 758 | } 759 | 760 | // golang.org/issue/8582 761 | func TestEncodePointerString(t *testing.T) { 762 | type stringPointer struct { 763 | N *int64 `json:"n,string"` 764 | } 765 | var n int64 = 42 766 | b, err := Marshal(stringPointer{N: &n}) 767 | if err != nil { 768 | t.Fatalf("Marshal error: %v", err) 769 | } 770 | if got, want := string(b), `{"n":"42"}`; got != want { 771 | t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want) 772 | } 773 | var back stringPointer 774 | switch err = Unmarshal(b, &back); { 775 | case err != nil: 776 | t.Fatalf("Unmarshal error: %v", err) 777 | case back.N == nil: 778 | t.Fatalf("Unmarshal: back.N = nil, want non-nil") 779 | case *back.N != 42: 780 | t.Fatalf("Unmarshal: *back.N = %d, want 42", *back.N) 781 | } 782 | } 783 | 784 | var encodeStringTests = []struct { 785 | in string 786 | out string 787 | }{ 788 | {"\x00", `"\u0000"`}, 789 | {"\x01", `"\u0001"`}, 790 | {"\x02", `"\u0002"`}, 791 | {"\x03", `"\u0003"`}, 792 | {"\x04", `"\u0004"`}, 793 | {"\x05", `"\u0005"`}, 794 | {"\x06", `"\u0006"`}, 795 | {"\x07", `"\u0007"`}, 796 | {"\x08", `"\b"`}, 797 | {"\x09", `"\t"`}, 798 | {"\x0a", `"\n"`}, 799 | {"\x0b", `"\u000b"`}, 800 | {"\x0c", `"\f"`}, 801 | {"\x0d", `"\r"`}, 802 | {"\x0e", `"\u000e"`}, 803 | {"\x0f", `"\u000f"`}, 804 | {"\x10", `"\u0010"`}, 805 | {"\x11", `"\u0011"`}, 806 | {"\x12", `"\u0012"`}, 807 | {"\x13", `"\u0013"`}, 808 | {"\x14", `"\u0014"`}, 809 | {"\x15", `"\u0015"`}, 810 | {"\x16", `"\u0016"`}, 811 | {"\x17", `"\u0017"`}, 812 | {"\x18", `"\u0018"`}, 813 | {"\x19", `"\u0019"`}, 814 | {"\x1a", `"\u001a"`}, 815 | {"\x1b", `"\u001b"`}, 816 | {"\x1c", `"\u001c"`}, 817 | {"\x1d", `"\u001d"`}, 818 | {"\x1e", `"\u001e"`}, 819 | {"\x1f", `"\u001f"`}, 820 | } 821 | 822 | func TestEncodeString(t *testing.T) { 823 | for _, tt := range encodeStringTests { 824 | b, err := Marshal(tt.in) 825 | if err != nil { 826 | t.Errorf("Marshal(%q) error: %v", tt.in, err) 827 | continue 828 | } 829 | out := string(b) 830 | if out != tt.out { 831 | t.Errorf("Marshal(%q) = %#q, want %#q", tt.in, out, tt.out) 832 | } 833 | } 834 | } 835 | 836 | type jsonbyte byte 837 | 838 | func (b jsonbyte) MarshalJSON() ([]byte, error) { return tenc(`{"JB":%d}`, b) } 839 | 840 | type textbyte byte 841 | 842 | func (b textbyte) MarshalText() ([]byte, error) { return tenc(`TB:%d`, b) } 843 | 844 | type jsonint int 845 | 846 | func (i jsonint) MarshalJSON() ([]byte, error) { return tenc(`{"JI":%d}`, i) } 847 | 848 | type textint int 849 | 850 | func (i textint) MarshalText() ([]byte, error) { return tenc(`TI:%d`, i) } 851 | 852 | func tenc(format string, a ...any) ([]byte, error) { 853 | var buf bytes.Buffer 854 | fmt.Fprintf(&buf, format, a...) 855 | return buf.Bytes(), nil 856 | } 857 | 858 | type textfloat float64 859 | 860 | func (f textfloat) MarshalText() ([]byte, error) { return tenc(`TF:%0.2f`, f) } 861 | 862 | // Issue 13783 863 | func TestEncodeBytekind(t *testing.T) { 864 | tests := []struct { 865 | CaseName 866 | in any 867 | want string 868 | }{ 869 | {Name(""), byte(7), "7"}, 870 | {Name(""), jsonbyte(7), `{"JB":7}`}, 871 | {Name(""), textbyte(4), `"TB:4"`}, 872 | {Name(""), jsonint(5), `{"JI":5}`}, 873 | {Name(""), textint(1), `"TI:1"`}, 874 | {Name(""), []byte{0, 1}, `"AAE="`}, 875 | {Name(""), []jsonbyte{0, 1}, `[{"JB":0},{"JB":1}]`}, 876 | {Name(""), [][]jsonbyte{{0, 1}, {3}}, `[[{"JB":0},{"JB":1}],[{"JB":3}]]`}, 877 | {Name(""), []textbyte{2, 3}, `["TB:2","TB:3"]`}, 878 | {Name(""), []jsonint{5, 4}, `[{"JI":5},{"JI":4}]`}, 879 | {Name(""), []textint{9, 3}, `["TI:9","TI:3"]`}, 880 | {Name(""), []int{9, 3}, `[9,3]`}, 881 | {Name(""), []textfloat{12, 3}, `["TF:12.00","TF:3.00"]`}, 882 | } 883 | for _, tt := range tests { 884 | t.Run(tt.Name, func(t *testing.T) { 885 | b, err := Marshal(tt.in) 886 | if err != nil { 887 | t.Errorf("%s: Marshal error: %v", tt.Where, err) 888 | } 889 | got, want := string(b), tt.want 890 | if got != want { 891 | t.Errorf("%s: Marshal:\n\tgot: %s\n\twant: %s", tt.Where, got, want) 892 | } 893 | }) 894 | } 895 | } 896 | 897 | func TestTextMarshalerMapKeysAreSorted(t *testing.T) { 898 | got, err := Marshal(map[unmarshalerText]int{ 899 | {"x", "y"}: 1, 900 | {"y", "x"}: 2, 901 | {"a", "z"}: 3, 902 | {"z", "a"}: 4, 903 | }) 904 | if err != nil { 905 | t.Fatalf("Marshal error: %v", err) 906 | } 907 | const want = `{"a:z":3,"x:y":1,"y:x":2,"z:a":4}` 908 | if string(got) != want { 909 | t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want) 910 | } 911 | } 912 | 913 | // https://golang.org/issue/33675 914 | func TestNilMarshalerTextMapKey(t *testing.T) { 915 | got, err := Marshal(map[*unmarshalerText]int{ 916 | (*unmarshalerText)(nil): 1, 917 | {"A", "B"}: 2, 918 | }) 919 | if err != nil { 920 | t.Fatalf("Marshal error: %v", err) 921 | } 922 | const want = `{"":1,"A:B":2}` 923 | if string(got) != want { 924 | t.Errorf("Marshal:\n\tgot: %s\n\twant: %s", got, want) 925 | } 926 | } 927 | 928 | var re = regexp.MustCompile 929 | 930 | // syntactic checks on form of marshaled floating point numbers. 931 | var badFloatREs = []*regexp.Regexp{ 932 | re(`p`), // no binary exponential notation 933 | re(`^\+`), // no leading + sign 934 | re(`^-?0[^.]`), // no unnecessary leading zeros 935 | re(`^-?\.`), // leading zero required before decimal point 936 | re(`\.(e|$)`), // no trailing decimal 937 | re(`\.[0-9]+0(e|$)`), // no trailing zero in fraction 938 | re(`^-?(0|[0-9]{2,})\..*e`), // exponential notation must have normalized mantissa 939 | re(`e[0-9]`), // positive exponent must be signed 940 | re(`e[+-]0`), // exponent must not have leading zeros 941 | re(`e-[1-6]$`), // not tiny enough for exponential notation 942 | re(`e+(.|1.|20)$`), // not big enough for exponential notation 943 | re(`^-?0\.0000000`), // too tiny, should use exponential notation 944 | re(`^-?[0-9]{22}`), // too big, should use exponential notation 945 | re(`[1-9][0-9]{16}[1-9]`), // too many significant digits in integer 946 | re(`[1-9][0-9.]{17}[1-9]`), // too many significant digits in decimal 947 | // below here for float32 only 948 | re(`[1-9][0-9]{8}[1-9]`), // too many significant digits in integer 949 | re(`[1-9][0-9.]{9}[1-9]`), // too many significant digits in decimal 950 | } 951 | 952 | func TestMarshalFloat(t *testing.T) { 953 | t.Parallel() 954 | nfail := 0 955 | test := func(f float64, bits int) { 956 | vf := any(f) 957 | if bits == 32 { 958 | f = float64(float32(f)) // round 959 | vf = float32(f) 960 | } 961 | bout, err := Marshal(vf) 962 | if err != nil { 963 | t.Errorf("Marshal(%T(%g)) error: %v", vf, vf, err) 964 | nfail++ 965 | return 966 | } 967 | out := string(bout) 968 | 969 | // result must convert back to the same float 970 | g, err := strconv.ParseFloat(out, bits) 971 | if err != nil { 972 | t.Errorf("ParseFloat(%q) error: %v", out, err) 973 | nfail++ 974 | return 975 | } 976 | if f != g || fmt.Sprint(f) != fmt.Sprint(g) { // fmt.Sprint handles ±0 977 | t.Errorf("ParseFloat(%q):\n\tgot: %g\n\twant: %g", out, float32(g), vf) 978 | nfail++ 979 | return 980 | } 981 | 982 | bad := badFloatREs 983 | if bits == 64 { 984 | bad = bad[:len(bad)-2] 985 | } 986 | for _, re := range bad { 987 | if re.MatchString(out) { 988 | t.Errorf("Marshal(%T(%g)) = %q; must not match /%s/", vf, vf, out, re) 989 | nfail++ 990 | return 991 | } 992 | } 993 | } 994 | 995 | var ( 996 | bigger = math.Inf(+1) 997 | smaller = math.Inf(-1) 998 | ) 999 | 1000 | var digits = "1.2345678901234567890123" 1001 | for i := len(digits); i >= 2; i-- { 1002 | if testing.Short() && i < len(digits)-4 { 1003 | break 1004 | } 1005 | for exp := -30; exp <= 30; exp++ { 1006 | for _, sign := range "+-" { 1007 | for bits := 32; bits <= 64; bits += 32 { 1008 | s := fmt.Sprintf("%c%se%d", sign, digits[:i], exp) 1009 | f, err := strconv.ParseFloat(s, bits) 1010 | if err != nil { 1011 | log.Fatal(err) 1012 | } 1013 | next := math.Nextafter 1014 | if bits == 32 { 1015 | next = func(g, h float64) float64 { 1016 | return float64(math.Nextafter32(float32(g), float32(h))) 1017 | } 1018 | } 1019 | test(f, bits) 1020 | test(next(f, bigger), bits) 1021 | test(next(f, smaller), bits) 1022 | if nfail > 50 { 1023 | t.Fatalf("stopping test early") 1024 | } 1025 | } 1026 | } 1027 | } 1028 | } 1029 | test(0, 64) 1030 | test(math.Copysign(0, -1), 64) 1031 | test(0, 32) 1032 | test(math.Copysign(0, -1), 32) 1033 | } 1034 | 1035 | func TestMarshalRawMessageValue(t *testing.T) { 1036 | type ( 1037 | T1 struct { 1038 | M RawMessage `json:",omitempty"` 1039 | } 1040 | T2 struct { 1041 | M *RawMessage `json:",omitempty"` 1042 | } 1043 | ) 1044 | 1045 | var ( 1046 | rawNil = RawMessage(nil) 1047 | rawEmpty = RawMessage([]byte{}) 1048 | rawText = RawMessage([]byte(`"foo"`)) 1049 | ) 1050 | 1051 | tests := []struct { 1052 | CaseName 1053 | in any 1054 | want string 1055 | ok bool 1056 | }{ 1057 | // Test with nil RawMessage. 1058 | {Name(""), rawNil, "null", true}, 1059 | {Name(""), &rawNil, "null", true}, 1060 | {Name(""), []any{rawNil}, "[null]", true}, 1061 | {Name(""), &[]any{rawNil}, "[null]", true}, 1062 | {Name(""), []any{&rawNil}, "[null]", true}, 1063 | {Name(""), &[]any{&rawNil}, "[null]", true}, 1064 | {Name(""), struct{ M RawMessage }{rawNil}, `{"M":null}`, true}, 1065 | {Name(""), &struct{ M RawMessage }{rawNil}, `{"M":null}`, true}, 1066 | {Name(""), struct{ M *RawMessage }{&rawNil}, `{"M":null}`, true}, 1067 | {Name(""), &struct{ M *RawMessage }{&rawNil}, `{"M":null}`, true}, 1068 | {Name(""), map[string]any{"M": rawNil}, `{"M":null}`, true}, 1069 | {Name(""), &map[string]any{"M": rawNil}, `{"M":null}`, true}, 1070 | {Name(""), map[string]any{"M": &rawNil}, `{"M":null}`, true}, 1071 | {Name(""), &map[string]any{"M": &rawNil}, `{"M":null}`, true}, 1072 | {Name(""), T1{rawNil}, "{}", true}, 1073 | {Name(""), T2{&rawNil}, `{"M":null}`, true}, 1074 | {Name(""), &T1{rawNil}, "{}", true}, 1075 | {Name(""), &T2{&rawNil}, `{"M":null}`, true}, 1076 | 1077 | // Test with empty, but non-nil, RawMessage. 1078 | {Name(""), rawEmpty, "", false}, 1079 | {Name(""), &rawEmpty, "", false}, 1080 | {Name(""), []any{rawEmpty}, "", false}, 1081 | {Name(""), &[]any{rawEmpty}, "", false}, 1082 | {Name(""), []any{&rawEmpty}, "", false}, 1083 | {Name(""), &[]any{&rawEmpty}, "", false}, 1084 | {Name(""), struct{ X RawMessage }{rawEmpty}, "", false}, 1085 | {Name(""), &struct{ X RawMessage }{rawEmpty}, "", false}, 1086 | {Name(""), struct{ X *RawMessage }{&rawEmpty}, "", false}, 1087 | {Name(""), &struct{ X *RawMessage }{&rawEmpty}, "", false}, 1088 | {Name(""), map[string]any{"nil": rawEmpty}, "", false}, 1089 | {Name(""), &map[string]any{"nil": rawEmpty}, "", false}, 1090 | {Name(""), map[string]any{"nil": &rawEmpty}, "", false}, 1091 | {Name(""), &map[string]any{"nil": &rawEmpty}, "", false}, 1092 | {Name(""), T1{rawEmpty}, "{}", true}, 1093 | {Name(""), T2{&rawEmpty}, "", false}, 1094 | {Name(""), &T1{rawEmpty}, "{}", true}, 1095 | {Name(""), &T2{&rawEmpty}, "", false}, 1096 | 1097 | // Test with RawMessage with some text. 1098 | // 1099 | // The tests below marked with Issue6458 used to generate "ImZvbyI=" instead "foo". 1100 | // This behavior was intentionally changed in Go 1.8. 1101 | // See https://golang.org/issues/14493#issuecomment-255857318 1102 | {Name(""), rawText, `"foo"`, true}, // Issue6458 1103 | {Name(""), &rawText, `"foo"`, true}, 1104 | {Name(""), []any{rawText}, `["foo"]`, true}, // Issue6458 1105 | {Name(""), &[]any{rawText}, `["foo"]`, true}, // Issue6458 1106 | {Name(""), []any{&rawText}, `["foo"]`, true}, 1107 | {Name(""), &[]any{&rawText}, `["foo"]`, true}, 1108 | {Name(""), struct{ M RawMessage }{rawText}, `{"M":"foo"}`, true}, // Issue6458 1109 | {Name(""), &struct{ M RawMessage }{rawText}, `{"M":"foo"}`, true}, 1110 | {Name(""), struct{ M *RawMessage }{&rawText}, `{"M":"foo"}`, true}, 1111 | {Name(""), &struct{ M *RawMessage }{&rawText}, `{"M":"foo"}`, true}, 1112 | {Name(""), map[string]any{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458 1113 | {Name(""), &map[string]any{"M": rawText}, `{"M":"foo"}`, true}, // Issue6458 1114 | {Name(""), map[string]any{"M": &rawText}, `{"M":"foo"}`, true}, 1115 | {Name(""), &map[string]any{"M": &rawText}, `{"M":"foo"}`, true}, 1116 | {Name(""), T1{rawText}, `{"M":"foo"}`, true}, // Issue6458 1117 | {Name(""), T2{&rawText}, `{"M":"foo"}`, true}, 1118 | {Name(""), &T1{rawText}, `{"M":"foo"}`, true}, 1119 | {Name(""), &T2{&rawText}, `{"M":"foo"}`, true}, 1120 | } 1121 | 1122 | for _, tt := range tests { 1123 | t.Run(tt.Name, func(t *testing.T) { 1124 | b, err := Marshal(tt.in) 1125 | if ok := (err == nil); ok != tt.ok { 1126 | if err != nil { 1127 | t.Errorf("%s: Marshal error: %v", tt.Where, err) 1128 | } else { 1129 | t.Errorf("%s: Marshal error: got nil, want non-nil", tt.Where) 1130 | } 1131 | } 1132 | if got := string(b); got != tt.want { 1133 | t.Errorf("%s: Marshal:\n\tinput: %#v\n\tgot: %s\n\twant: %s", tt.Where, tt.in, got, tt.want) 1134 | } 1135 | }) 1136 | } 1137 | } 1138 | 1139 | type marshalPanic struct{} 1140 | 1141 | func (marshalPanic) MarshalJSON() ([]byte, error) { panic(0xdead) } 1142 | 1143 | func TestMarshalPanic(t *testing.T) { 1144 | defer func() { 1145 | if got := recover(); !reflect.DeepEqual(got, 0xdead) { 1146 | t.Errorf("panic() = (%T)(%v), want 0xdead", got, got) 1147 | } 1148 | }() 1149 | Marshal(&marshalPanic{}) 1150 | t.Error("Marshal should have panicked") 1151 | } 1152 | 1153 | func TestMarshalUncommonFieldNames(t *testing.T) { 1154 | v := struct { 1155 | A0, À, Aβ int 1156 | }{} 1157 | b, err := Marshal(v) 1158 | if err != nil { 1159 | t.Fatal("Marshal error:", err) 1160 | } 1161 | want := `{"A0":0,"À":0,"Aβ":0}` 1162 | got := string(b) 1163 | if got != want { 1164 | t.Fatalf("Marshal:\n\tgot: %s\n\twant: %s", got, want) 1165 | } 1166 | } 1167 | 1168 | func TestMarshalerError(t *testing.T) { 1169 | s := "test variable" 1170 | st := reflect.TypeOf(s) 1171 | const errText = "json: test error" 1172 | 1173 | tests := []struct { 1174 | CaseName 1175 | err *MarshalerError 1176 | want string 1177 | }{{ 1178 | Name(""), 1179 | &MarshalerError{st, fmt.Errorf(errText), ""}, 1180 | "json: error calling MarshalJSON for type " + st.String() + ": " + errText, 1181 | }, { 1182 | Name(""), 1183 | &MarshalerError{st, fmt.Errorf(errText), "TestMarshalerError"}, 1184 | "json: error calling TestMarshalerError for type " + st.String() + ": " + errText, 1185 | }} 1186 | 1187 | for _, tt := range tests { 1188 | t.Run(tt.Name, func(t *testing.T) { 1189 | got := tt.err.Error() 1190 | if got != tt.want { 1191 | t.Errorf("%s: Error:\n\tgot: %s\n\twant: %s", tt.Where, got, tt.want) 1192 | } 1193 | }) 1194 | } 1195 | } 1196 | 1197 | type marshaledValue string 1198 | 1199 | func (v marshaledValue) MarshalJSON() ([]byte, error) { 1200 | return []byte(v), nil 1201 | } 1202 | 1203 | func TestIssue63379(t *testing.T) { 1204 | for _, v := range []string{ 1205 | "[]<", 1206 | "[]>", 1207 | "[]&", 1208 | "[]\u2028", 1209 | "[]\u2029", 1210 | "{}<", 1211 | "{}>", 1212 | "{}&", 1213 | "{}\u2028", 1214 | "{}\u2029", 1215 | } { 1216 | _, err := Marshal(marshaledValue(v)) 1217 | if err == nil { 1218 | t.Errorf("expected error for %q", v) 1219 | } 1220 | } 1221 | } 1222 | -------------------------------------------------------------------------------- /internal/golang/encoding/json/example_marshaling_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json_test 6 | 7 | import ( 8 | "encoding/json" 9 | "fmt" 10 | "log" 11 | "strings" 12 | ) 13 | 14 | type Animal int 15 | 16 | const ( 17 | Unknown Animal = iota 18 | Gopher 19 | Zebra 20 | ) 21 | 22 | func (a *Animal) UnmarshalJSON(b []byte) error { 23 | var s string 24 | if err := json.Unmarshal(b, &s); err != nil { 25 | return err 26 | } 27 | switch strings.ToLower(s) { 28 | default: 29 | *a = Unknown 30 | case "gopher": 31 | *a = Gopher 32 | case "zebra": 33 | *a = Zebra 34 | } 35 | 36 | return nil 37 | } 38 | 39 | func (a Animal) MarshalJSON() ([]byte, error) { 40 | var s string 41 | switch a { 42 | default: 43 | s = "unknown" 44 | case Gopher: 45 | s = "gopher" 46 | case Zebra: 47 | s = "zebra" 48 | } 49 | 50 | return json.Marshal(s) 51 | } 52 | 53 | func Example_customMarshalJSON() { 54 | blob := `["gopher","armadillo","zebra","unknown","gopher","bee","gopher","zebra"]` 55 | var zoo []Animal 56 | if err := json.Unmarshal([]byte(blob), &zoo); err != nil { 57 | log.Fatal(err) 58 | } 59 | 60 | census := make(map[Animal]int) 61 | for _, animal := range zoo { 62 | census[animal] += 1 63 | } 64 | 65 | fmt.Printf("Zoo Census:\n* Gophers: %d\n* Zebras: %d\n* Unknown: %d\n", 66 | census[Gopher], census[Zebra], census[Unknown]) 67 | 68 | // Output: 69 | // Zoo Census: 70 | // * Gophers: 3 71 | // * Zebras: 2 72 | // * Unknown: 3 73 | } 74 | -------------------------------------------------------------------------------- /internal/golang/encoding/json/example_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2011 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json_test 6 | 7 | import ( 8 | "bytes" 9 | "encoding/json" 10 | "fmt" 11 | "io" 12 | "log" 13 | "os" 14 | "strings" 15 | ) 16 | 17 | func ExampleMarshal() { 18 | type ColorGroup struct { 19 | ID int 20 | Name string 21 | Colors []string 22 | } 23 | group := ColorGroup{ 24 | ID: 1, 25 | Name: "Reds", 26 | Colors: []string{"Crimson", "Red", "Ruby", "Maroon"}, 27 | } 28 | b, err := json.Marshal(group) 29 | if err != nil { 30 | fmt.Println("error:", err) 31 | } 32 | os.Stdout.Write(b) 33 | // Output: 34 | // {"ID":1,"Name":"Reds","Colors":["Crimson","Red","Ruby","Maroon"]} 35 | } 36 | 37 | func ExampleUnmarshal() { 38 | var jsonBlob = []byte(`[ 39 | {"Name": "Platypus", "Order": "Monotremata"}, 40 | {"Name": "Quoll", "Order": "Dasyuromorphia"} 41 | ]`) 42 | type Animal struct { 43 | Name string 44 | Order string 45 | } 46 | var animals []Animal 47 | err := json.Unmarshal(jsonBlob, &animals) 48 | if err != nil { 49 | fmt.Println("error:", err) 50 | } 51 | fmt.Printf("%+v", animals) 52 | // Output: 53 | // [{Name:Platypus Order:Monotremata} {Name:Quoll Order:Dasyuromorphia}] 54 | } 55 | 56 | // This example uses a Decoder to decode a stream of distinct JSON values. 57 | func ExampleDecoder() { 58 | const jsonStream = ` 59 | {"Name": "Ed", "Text": "Knock knock."} 60 | {"Name": "Sam", "Text": "Who's there?"} 61 | {"Name": "Ed", "Text": "Go fmt."} 62 | {"Name": "Sam", "Text": "Go fmt who?"} 63 | {"Name": "Ed", "Text": "Go fmt yourself!"} 64 | ` 65 | type Message struct { 66 | Name, Text string 67 | } 68 | dec := json.NewDecoder(strings.NewReader(jsonStream)) 69 | for { 70 | var m Message 71 | if err := dec.Decode(&m); err == io.EOF { 72 | break 73 | } else if err != nil { 74 | log.Fatal(err) 75 | } 76 | fmt.Printf("%s: %s\n", m.Name, m.Text) 77 | } 78 | // Output: 79 | // Ed: Knock knock. 80 | // Sam: Who's there? 81 | // Ed: Go fmt. 82 | // Sam: Go fmt who? 83 | // Ed: Go fmt yourself! 84 | } 85 | 86 | // This example uses a Decoder to decode a stream of distinct JSON values. 87 | func ExampleDecoder_Token() { 88 | const jsonStream = ` 89 | {"Message": "Hello", "Array": [1, 2, 3], "Null": null, "Number": 1.234} 90 | ` 91 | dec := json.NewDecoder(strings.NewReader(jsonStream)) 92 | for { 93 | t, err := dec.Token() 94 | if err == io.EOF { 95 | break 96 | } 97 | if err != nil { 98 | log.Fatal(err) 99 | } 100 | fmt.Printf("%T: %v", t, t) 101 | if dec.More() { 102 | fmt.Printf(" (more)") 103 | } 104 | fmt.Printf("\n") 105 | } 106 | // Output: 107 | // json.Delim: { (more) 108 | // string: Message (more) 109 | // string: Hello (more) 110 | // string: Array (more) 111 | // json.Delim: [ (more) 112 | // float64: 1 (more) 113 | // float64: 2 (more) 114 | // float64: 3 115 | // json.Delim: ] (more) 116 | // string: Null (more) 117 | // : (more) 118 | // string: Number (more) 119 | // float64: 1.234 120 | // json.Delim: } 121 | } 122 | 123 | // This example uses a Decoder to decode a streaming array of JSON objects. 124 | func ExampleDecoder_Decode_stream() { 125 | const jsonStream = ` 126 | [ 127 | {"Name": "Ed", "Text": "Knock knock."}, 128 | {"Name": "Sam", "Text": "Who's there?"}, 129 | {"Name": "Ed", "Text": "Go fmt."}, 130 | {"Name": "Sam", "Text": "Go fmt who?"}, 131 | {"Name": "Ed", "Text": "Go fmt yourself!"} 132 | ] 133 | ` 134 | type Message struct { 135 | Name, Text string 136 | } 137 | dec := json.NewDecoder(strings.NewReader(jsonStream)) 138 | 139 | // read open bracket 140 | t, err := dec.Token() 141 | if err != nil { 142 | log.Fatal(err) 143 | } 144 | fmt.Printf("%T: %v\n", t, t) 145 | 146 | // while the array contains values 147 | for dec.More() { 148 | var m Message 149 | // decode an array value (Message) 150 | err := dec.Decode(&m) 151 | if err != nil { 152 | log.Fatal(err) 153 | } 154 | 155 | fmt.Printf("%v: %v\n", m.Name, m.Text) 156 | } 157 | 158 | // read closing bracket 159 | t, err = dec.Token() 160 | if err != nil { 161 | log.Fatal(err) 162 | } 163 | fmt.Printf("%T: %v\n", t, t) 164 | 165 | // Output: 166 | // json.Delim: [ 167 | // Ed: Knock knock. 168 | // Sam: Who's there? 169 | // Ed: Go fmt. 170 | // Sam: Go fmt who? 171 | // Ed: Go fmt yourself! 172 | // json.Delim: ] 173 | } 174 | 175 | // This example uses RawMessage to delay parsing part of a JSON message. 176 | func ExampleRawMessage_unmarshal() { 177 | type Color struct { 178 | Space string 179 | Point json.RawMessage // delay parsing until we know the color space 180 | } 181 | type RGB struct { 182 | R uint8 183 | G uint8 184 | B uint8 185 | } 186 | type YCbCr struct { 187 | Y uint8 188 | Cb int8 189 | Cr int8 190 | } 191 | 192 | var j = []byte(`[ 193 | {"Space": "YCbCr", "Point": {"Y": 255, "Cb": 0, "Cr": -10}}, 194 | {"Space": "RGB", "Point": {"R": 98, "G": 218, "B": 255}} 195 | ]`) 196 | var colors []Color 197 | err := json.Unmarshal(j, &colors) 198 | if err != nil { 199 | log.Fatalln("error:", err) 200 | } 201 | 202 | for _, c := range colors { 203 | var dst any 204 | switch c.Space { 205 | case "RGB": 206 | dst = new(RGB) 207 | case "YCbCr": 208 | dst = new(YCbCr) 209 | } 210 | err := json.Unmarshal(c.Point, dst) 211 | if err != nil { 212 | log.Fatalln("error:", err) 213 | } 214 | fmt.Println(c.Space, dst) 215 | } 216 | // Output: 217 | // YCbCr &{255 0 -10} 218 | // RGB &{98 218 255} 219 | } 220 | 221 | // This example uses RawMessage to use a precomputed JSON during marshal. 222 | func ExampleRawMessage_marshal() { 223 | h := json.RawMessage(`{"precomputed": true}`) 224 | 225 | c := struct { 226 | Header *json.RawMessage `json:"header"` 227 | Body string `json:"body"` 228 | }{Header: &h, Body: "Hello Gophers!"} 229 | 230 | b, err := json.MarshalIndent(&c, "", "\t") 231 | if err != nil { 232 | fmt.Println("error:", err) 233 | } 234 | os.Stdout.Write(b) 235 | 236 | // Output: 237 | // { 238 | // "header": { 239 | // "precomputed": true 240 | // }, 241 | // "body": "Hello Gophers!" 242 | // } 243 | } 244 | 245 | func ExampleIndent() { 246 | type Road struct { 247 | Name string 248 | Number int 249 | } 250 | roads := []Road{ 251 | {"Diamond Fork", 29}, 252 | {"Sheep Creek", 51}, 253 | } 254 | 255 | b, err := json.Marshal(roads) 256 | if err != nil { 257 | log.Fatal(err) 258 | } 259 | 260 | var out bytes.Buffer 261 | json.Indent(&out, b, "=", "\t") 262 | out.WriteTo(os.Stdout) 263 | // Output: 264 | // [ 265 | // = { 266 | // = "Name": "Diamond Fork", 267 | // = "Number": 29 268 | // = }, 269 | // = { 270 | // = "Name": "Sheep Creek", 271 | // = "Number": 51 272 | // = } 273 | // =] 274 | } 275 | 276 | func ExampleMarshalIndent() { 277 | data := map[string]int{ 278 | "a": 1, 279 | "b": 2, 280 | } 281 | 282 | b, err := json.MarshalIndent(data, "", "") 283 | if err != nil { 284 | log.Fatal(err) 285 | } 286 | 287 | fmt.Println(string(b)) 288 | // Output: 289 | // { 290 | // "a": 1, 291 | // "b": 2 292 | // } 293 | } 294 | 295 | func ExampleValid() { 296 | goodJSON := `{"example": 1}` 297 | badJSON := `{"example":2:]}}` 298 | 299 | fmt.Println(json.Valid([]byte(goodJSON)), json.Valid([]byte(badJSON))) 300 | // Output: 301 | // true false 302 | } 303 | 304 | func ExampleHTMLEscape() { 305 | var out bytes.Buffer 306 | json.HTMLEscape(&out, []byte(`{"Name":"HTML content"}`)) 307 | out.WriteTo(os.Stdout) 308 | // Output: 309 | //{"Name":"\u003cb\u003eHTML content\u003c/b\u003e"} 310 | } 311 | -------------------------------------------------------------------------------- /internal/golang/encoding/json/example_text_marshaling_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json_test 6 | 7 | import ( 8 | "encoding/json" 9 | "fmt" 10 | "log" 11 | "strings" 12 | ) 13 | 14 | type Size int 15 | 16 | const ( 17 | Unrecognized Size = iota 18 | Small 19 | Large 20 | ) 21 | 22 | func (s *Size) UnmarshalText(text []byte) error { 23 | switch strings.ToLower(string(text)) { 24 | default: 25 | *s = Unrecognized 26 | case "small": 27 | *s = Small 28 | case "large": 29 | *s = Large 30 | } 31 | return nil 32 | } 33 | 34 | func (s Size) MarshalText() ([]byte, error) { 35 | var name string 36 | switch s { 37 | default: 38 | name = "unrecognized" 39 | case Small: 40 | name = "small" 41 | case Large: 42 | name = "large" 43 | } 44 | return []byte(name), nil 45 | } 46 | 47 | func Example_textMarshalJSON() { 48 | blob := `["small","regular","large","unrecognized","small","normal","small","large"]` 49 | var inventory []Size 50 | if err := json.Unmarshal([]byte(blob), &inventory); err != nil { 51 | log.Fatal(err) 52 | } 53 | 54 | counts := make(map[Size]int) 55 | for _, size := range inventory { 56 | counts[size] += 1 57 | } 58 | 59 | fmt.Printf("Inventory Counts:\n* Small: %d\n* Large: %d\n* Unrecognized: %d\n", 60 | counts[Small], counts[Large], counts[Unrecognized]) 61 | 62 | // Output: 63 | // Inventory Counts: 64 | // * Small: 3 65 | // * Large: 2 66 | // * Unrecognized: 3 67 | } 68 | -------------------------------------------------------------------------------- /internal/golang/encoding/json/fold.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import ( 8 | "unicode" 9 | "unicode/utf8" 10 | ) 11 | 12 | // foldName returns a folded string such that foldName(x) == foldName(y) 13 | // is identical to bytes.EqualFold(x, y). 14 | func foldName(in []byte) []byte { 15 | // This is inlinable to take advantage of "function outlining". 16 | var arr [32]byte // large enough for most JSON names 17 | return appendFoldedName(arr[:0], in) 18 | } 19 | 20 | func appendFoldedName(out, in []byte) []byte { 21 | for i := 0; i < len(in); { 22 | // Handle single-byte ASCII. 23 | if c := in[i]; c < utf8.RuneSelf { 24 | if 'a' <= c && c <= 'z' { 25 | c -= 'a' - 'A' 26 | } 27 | out = append(out, c) 28 | i++ 29 | continue 30 | } 31 | // Handle multi-byte Unicode. 32 | r, n := utf8.DecodeRune(in[i:]) 33 | out = utf8.AppendRune(out, foldRune(r)) 34 | i += n 35 | } 36 | return out 37 | } 38 | 39 | // foldRune is returns the smallest rune for all runes in the same fold set. 40 | func foldRune(r rune) rune { 41 | for { 42 | r2 := unicode.SimpleFold(r) 43 | if r2 <= r { 44 | return r2 45 | } 46 | r = r2 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /internal/golang/encoding/json/fold_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import ( 8 | "bytes" 9 | "testing" 10 | ) 11 | 12 | func FuzzEqualFold(f *testing.F) { 13 | for _, ss := range [][2]string{ 14 | {"", ""}, 15 | {"123abc", "123ABC"}, 16 | {"αβδ", "ΑΒΔ"}, 17 | {"abc", "xyz"}, 18 | {"abc", "XYZ"}, 19 | {"1", "2"}, 20 | {"hello, world!", "hello, world!"}, 21 | {"hello, world!", "Hello, World!"}, 22 | {"hello, world!", "HELLO, WORLD!"}, 23 | {"hello, world!", "jello, world!"}, 24 | {"γειά, κόσμε!", "γειά, κόσμε!"}, 25 | {"γειά, κόσμε!", "Γειά, Κόσμε!"}, 26 | {"γειά, κόσμε!", "ΓΕΙΆ, ΚΌΣΜΕ!"}, 27 | {"γειά, κόσμε!", "ΛΕΙΆ, ΚΌΣΜΕ!"}, 28 | {"AESKey", "aesKey"}, 29 | {"AESKEY", "aes_key"}, 30 | {"aes_key", "AES_KEY"}, 31 | {"AES_KEY", "aes-key"}, 32 | {"aes-key", "AES-KEY"}, 33 | {"AES-KEY", "aesKey"}, 34 | {"aesKey", "AesKey"}, 35 | {"AesKey", "AESKey"}, 36 | {"AESKey", "aeskey"}, 37 | {"DESKey", "aeskey"}, 38 | {"AES Key", "aeskey"}, 39 | } { 40 | f.Add([]byte(ss[0]), []byte(ss[1])) 41 | } 42 | equalFold := func(x, y []byte) bool { return string(foldName(x)) == string(foldName(y)) } 43 | f.Fuzz(func(t *testing.T, x, y []byte) { 44 | got := equalFold(x, y) 45 | want := bytes.EqualFold(x, y) 46 | if got != want { 47 | t.Errorf("equalFold(%q, %q) = %v, want %v", x, y, got, want) 48 | } 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /internal/golang/encoding/json/fuzz.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build gofuzz 6 | 7 | package json 8 | 9 | import ( 10 | "fmt" 11 | ) 12 | 13 | func Fuzz(data []byte) (score int) { 14 | for _, ctor := range []func() any{ 15 | func() any { return new(any) }, 16 | func() any { return new(map[string]any) }, 17 | func() any { return new([]any) }, 18 | } { 19 | v := ctor() 20 | err := Unmarshal(data, v) 21 | if err != nil { 22 | continue 23 | } 24 | score = 1 25 | 26 | m, err := Marshal(v) 27 | if err != nil { 28 | fmt.Printf("v=%#v\n", v) 29 | panic(err) 30 | } 31 | 32 | u := ctor() 33 | err = Unmarshal(m, u) 34 | if err != nil { 35 | fmt.Printf("v=%#v\n", v) 36 | fmt.Printf("m=%s\n", m) 37 | panic(err) 38 | } 39 | } 40 | 41 | return 42 | } 43 | -------------------------------------------------------------------------------- /internal/golang/encoding/json/fuzz_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import ( 8 | "bytes" 9 | "io" 10 | "testing" 11 | ) 12 | 13 | func FuzzUnmarshalJSON(f *testing.F) { 14 | f.Add([]byte(`{ 15 | "object": { 16 | "slice": [ 17 | 1, 18 | 2.0, 19 | "3", 20 | [4], 21 | {5: {}} 22 | ] 23 | }, 24 | "slice": [[]], 25 | "string": ":)", 26 | "int": 1e5, 27 | "float": 3e-9" 28 | }`)) 29 | 30 | f.Fuzz(func(t *testing.T, b []byte) { 31 | for _, typ := range []func() interface{}{ 32 | func() interface{} { return new(interface{}) }, 33 | func() interface{} { return new(map[string]interface{}) }, 34 | func() interface{} { return new([]interface{}) }, 35 | } { 36 | i := typ() 37 | if err := Unmarshal(b, i); err != nil { 38 | return 39 | } 40 | 41 | encoded, err := Marshal(i) 42 | if err != nil { 43 | t.Fatalf("failed to marshal: %s", err) 44 | } 45 | 46 | if err := Unmarshal(encoded, i); err != nil { 47 | t.Fatalf("failed to roundtrip: %s", err) 48 | } 49 | } 50 | }) 51 | } 52 | 53 | func FuzzDecoderToken(f *testing.F) { 54 | f.Add([]byte(`{ 55 | "object": { 56 | "slice": [ 57 | 1, 58 | 2.0, 59 | "3", 60 | [4], 61 | {5: {}} 62 | ] 63 | }, 64 | "slice": [[]], 65 | "string": ":)", 66 | "int": 1e5, 67 | "float": 3e-9" 68 | }`)) 69 | 70 | f.Fuzz(func(t *testing.T, b []byte) { 71 | r := bytes.NewReader(b) 72 | d := NewDecoder(r) 73 | for { 74 | _, err := d.Token() 75 | if err != nil { 76 | if err == io.EOF { 77 | break 78 | } 79 | return 80 | } 81 | } 82 | }) 83 | } 84 | -------------------------------------------------------------------------------- /internal/golang/encoding/json/indent.go: -------------------------------------------------------------------------------- 1 | // Copyright 2010 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package json 6 | 7 | import "bytes" 8 | 9 | // HTMLEscape appends to dst the JSON-encoded src with <, >, &, U+2028 and U+2029 10 | // characters inside string literals changed to \u003c, \u003e, \u0026, \u2028, \u2029 11 | // so that the JSON will be safe to embed inside HTML