go.mod: bump github.com/getkin/kin-openapi to v0.131.0
As deepmap/oapi-codegen didn't work with this newer version, upgrade to oapi-codegen/oapi-codegen v2. Mitigating CVE-2025-30153
This commit is contained in:
parent
c5cb0d0618
commit
b2700903ae
403 changed files with 44758 additions and 16347 deletions
4
vendor/github.com/perimeterx/marshmallow/.gitignore
generated
vendored
Normal file
4
vendor/github.com/perimeterx/marshmallow/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
/.idea
|
||||
|
||||
coverage.out
|
||||
profile.out
|
||||
49
vendor/github.com/perimeterx/marshmallow/CHANGELOG.md
generated
vendored
Normal file
49
vendor/github.com/perimeterx/marshmallow/CHANGELOG.md
generated
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [[1.1.5](https://github.com/PerimeterX/marshmallow/compare/v1.1.4...v1.1.5)] - 2023-07-03
|
||||
|
||||
### Added
|
||||
|
||||
- Support for reporting errors from `HandleJSONData` - [info](https://github.com/PerimeterX/marshmallow/issues/27).
|
||||
|
||||
## [[1.1.4](https://github.com/PerimeterX/marshmallow/compare/v1.1.3...v1.1.4)] - 2022-11-10
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed problem with nested object implementing JSONDataHandler with skipPopulateStruct - [info](https://github.com/PerimeterX/marshmallow/issues/18).
|
||||
- Fixed problem with nested object implementing JSONDataHandler with skipPopulateStruct in ModeFailOverToOriginalValue - [info](https://github.com/PerimeterX/marshmallow/issues/19).
|
||||
|
||||
## [[1.1.3](https://github.com/PerimeterX/marshmallow/compare/v1.1.2...v1.1.3)] - 2022-08-31
|
||||
|
||||
### Added
|
||||
|
||||
- Support for excluding known fields from the result map - [info](https://github.com/PerimeterX/marshmallow/issues/16).
|
||||
|
||||
## [[1.1.2](https://github.com/PerimeterX/marshmallow/compare/v1.1.1...v1.1.2)] - 2022-08-23
|
||||
|
||||
### Added
|
||||
|
||||
- Support capturing nested unknown fields - [info](https://github.com/PerimeterX/marshmallow/issues/15).
|
||||
|
||||
## [[1.1.1](https://github.com/PerimeterX/marshmallow/compare/v1.1.0...v1.1.1)] - 2022-08-21
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix parsing bug for unknown nested fields - [info](https://github.com/PerimeterX/marshmallow/issues/12).
|
||||
|
||||
## [[1.1.0](https://github.com/PerimeterX/marshmallow/compare/v0.0.1...v1.1.0)] - 2022-07-10
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed an issue with embedded fields - [info](https://github.com/PerimeterX/marshmallow/issues/9).
|
||||
|
||||
## [[0.0.1](https://github.com/PerimeterX/marshmallow/tree/v0.0.1)] - 2022-04-21
|
||||
|
||||
### Added
|
||||
|
||||
- All functionality from our internal repository, after it has been stabilized on production for several months - [info](https://www.perimeterx.com/tech-blog/2022/boosting-up-json-performance-of-unstructured-structs-in-go/).
|
||||
133
vendor/github.com/perimeterx/marshmallow/CODE_OF_CONDUCT.md
generated
vendored
Normal file
133
vendor/github.com/perimeterx/marshmallow/CODE_OF_CONDUCT.md
generated
vendored
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||
identity and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the overall
|
||||
community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or advances of
|
||||
any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email address,
|
||||
without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
[opensource-conduct@humansecurity.com](mailto:opensource-conduct@humansecurity.com).
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series of
|
||||
actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or permanent
|
||||
ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||
community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.1, available at
|
||||
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
||||
[https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||
[FAQ]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
47
vendor/github.com/perimeterx/marshmallow/CONTRIBUTING.md
generated
vendored
Normal file
47
vendor/github.com/perimeterx/marshmallow/CONTRIBUTING.md
generated
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# How To Contribute
|
||||
|
||||
We'd love to accept your patches and contributions to this project. There are just a few guidelines you need to follow which are described in detail below.
|
||||
|
||||
## 1. Fork this repo
|
||||
|
||||
You should create a fork of this project in your account and work from there. You can create a fork by clicking the fork button in GitHub.
|
||||
|
||||
## 2. One feature, one branch
|
||||
|
||||
Work for each new feature/issue should occur in its own branch. To create a new branch from the command line:
|
||||
```shell
|
||||
git checkout -b my-new-feature
|
||||
```
|
||||
where "my-new-feature" describes what you're working on.
|
||||
|
||||
## 3. Add unit tests
|
||||
If your contribution modifies existing or adds new code please add corresponding unit tests for this.
|
||||
|
||||
## 4. Ensure that the build passes
|
||||
|
||||
Run
|
||||
```shell
|
||||
go test -v
|
||||
```
|
||||
and check that there are no errors.
|
||||
|
||||
## 5. Add documentation for new or updated functionality
|
||||
|
||||
Please review the [README.md](README.md) file in this project to see if they are impacted by your change and update them accordingly.
|
||||
|
||||
## 6. Add to CHANGELOG.md
|
||||
|
||||
Any notable changes should be recorded in the CHANGELOG.md following the [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) conventions.
|
||||
|
||||
## 7. Submit a pull request and describe the change
|
||||
|
||||
Push your changes to your branch and open a pull request against the parent repo on GitHub. The project administrators will review your pull request and respond with feedback.
|
||||
|
||||
# How your contribution gets merged
|
||||
|
||||
Upon pull request submission, your code will be reviewed by the maintainers. They will confirm at least the following:
|
||||
|
||||
- Tests run successfully (unit, coverage, style).
|
||||
- Contribution policy has been followed.
|
||||
|
||||
A (human) reviewer will need to sign off on your pull request before it can be merged.
|
||||
21
vendor/github.com/perimeterx/marshmallow/LICENSE
generated
vendored
Normal file
21
vendor/github.com/perimeterx/marshmallow/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2022 PerimeterX
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
205
vendor/github.com/perimeterx/marshmallow/README.md
generated
vendored
Normal file
205
vendor/github.com/perimeterx/marshmallow/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
# Marshmallow
|
||||
|
||||

|
||||
|
||||
[](https://github.com/PerimeterX/marshmallow/actions/workflows/codeql.yml?query=branch%3Amain++)
|
||||
[](https://github.com/PerimeterX/marshmallow/actions/workflows/go.yml?query=branch%3Amain)
|
||||
[](https://github.com/PerimeterX/marshmallow/actions/workflows/dependency-review.yml?query=branch%3Amain)
|
||||
[](https://goreportcard.com/report/github.com/perimeterx/marshmallow)
|
||||

|
||||
[](https://pkg.go.dev/github.com/perimeterx/marshmallow)
|
||||
[](LICENSE)
|
||||
[](https://github.com/PerimeterX/marshmallow/releases)
|
||||

|
||||
[](https://github.com/PerimeterX/marshmallow/issues)
|
||||
[](https://github.com/PerimeterX/marshmallow/pulls)
|
||||
[](https://github.com/PerimeterX/marshmallow/commits/main)
|
||||
[](CODE_OF_CONDUCT.md)
|
||||
|
||||
<img align="right" width="200" alt="marshmallow-gopher" src="https://raw.githubusercontent.com/PerimeterX/marshmallow/assets/sticker7.png">
|
||||
|
||||
Marshmallow package provides a simple API to perform flexible and performant JSON unmarshalling in Go.
|
||||
|
||||
Marshmallow specializes in dealing with **unstructured struct** - when some fields are known and some aren't,
|
||||
with zero performance overhead nor extra coding needed.
|
||||
While unmarshalling, marshmallow allows fully retaining the original data and access
|
||||
it via a typed struct and a dynamic map.
|
||||
|
||||
## Contents
|
||||
|
||||
- [Install](#install)
|
||||
- [Usage](#usage)
|
||||
- [Performance Benchmark And Alternatives](#performance-benchmark-and-alternatives)
|
||||
- [When Should I Use Marshmallow](#when-should-i-use-marshmallow)
|
||||
- [API](#api)
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
go get -u github.com/perimeterx/marshmallow
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/perimeterx/marshmallow"
|
||||
)
|
||||
|
||||
func main() {
|
||||
v := struct {
|
||||
Foo string `json:"foo"`
|
||||
Boo []int `json:"boo"`
|
||||
}{}
|
||||
result, err := marshmallow.Unmarshal([]byte(`{"foo":"bar","boo":[1,2,3],"goo":12.6}`), &v)
|
||||
fmt.Printf("v=%+v, result=%+v, err=%v", v, result, err)
|
||||
// Output: v={Foo:bar Boo:[1 2 3]}, result=map[boo:[1 2 3] foo:bar goo:12.6], err=<nil>
|
||||
}
|
||||
```
|
||||
|
||||
**Examples can be found [here](example_test.go)**
|
||||
|
||||
## Performance Benchmark And Alternatives
|
||||
|
||||
Marshmallow performs best when dealing with mixed data - when some fields are known and some are unknown.
|
||||
More info [below](#when-should-i-use-marshmallow).
|
||||
Other solutions are available for this kind of use case, each solution is explained and documented in the link below.
|
||||
The full benchmark test can be found
|
||||
[here](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go).
|
||||
|
||||
|Benchmark|Iterations|Time/Iteration|Bytes Allocated|Allocations|
|
||||
|--|--|--|--|--|
|
||||
|[unmarshall twice](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L40)|228693|5164 ns/op|1640 B/op|51 allocs/op|
|
||||
|[raw map](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L66)|232236|5116 ns/op|2296 B/op|53 allocs/op|
|
||||
|[go codec](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L121)|388442|3077 ns/op|2512 B/op|37 allocs/op|
|
||||
|[marshmallow](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L16)|626168|1853 ns/op|608 B/op|18 allocs/op|
|
||||
|[marshmallow without populating struct](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L162)|678616|1751 ns/op|608 B/op|18 allocs/op|
|
||||
|
||||

|
||||
|
||||
**Marshmallow provides the best performance (up to X3 faster) while not requiring any extra coding.**
|
||||
In fact, marshmallow performs as fast as normal `json.Unmarshal` call, however, such a call causes loss of data for all
|
||||
the fields that did not match the given struct. With marshmallow you never lose any data.
|
||||
|
||||
|Benchmark|Iterations|Time/Iteration|Bytes Allocated|Allocations|
|
||||
|--|--|--|--|--|
|
||||
|[marshmallow](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L16)|626168|1853 ns/op|608 B/op|18 allocs/op|
|
||||
|[native library](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L143)|652106|1845 ns/op|304 B/op|11 allocs/op|
|
||||
|[marshmallow without populating struct](https://github.com/PerimeterX/marshmallow/blob/8c5bba9e6dc0033f4324eca554737089a99f6e5e/benchmark_test.go#L162)|678616|1751 ns/op|608 B/op|18 allocs/op|
|
||||
|
||||
## When Should I Use Marshmallow
|
||||
|
||||
Marshmallow is best suited for use cases where you are interested in all the input data, but you have predetermined
|
||||
information only about a subset of it. For instance, if you plan to reference two specific fields from the data, then
|
||||
iterate all the data and apply some generic logic. How does it look with the native library:
|
||||
|
||||
```go
|
||||
func isAllowedToDrive(data []byte) (bool, error) {
|
||||
result := make(map[string]interface{})
|
||||
err := json.Unmarshal(data, &result)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
age, ok := result["age"]
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
a, ok := age.(float64)
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
if a < 17 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
hasDriversLicense, ok := result["has_drivers_license"]
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
h, ok := hasDriversLicense.(bool)
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
if !h {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
for key := range result {
|
||||
if strings.Contains(key, "prior_conviction") {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
```
|
||||
|
||||
And with marshmallow:
|
||||
|
||||
```go
|
||||
func isAllowedToDrive(data []byte) (bool, error) {
|
||||
v := struct {
|
||||
Age int `json:"age"`
|
||||
HasDriversLicense bool `json:"has_drivers_license"`
|
||||
}{}
|
||||
result, err := marshmallow.Unmarshal(data, &v)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if v.Age < 17 || !v.HasDriversLicense {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
for key := range result {
|
||||
if strings.Contains(key, "prior_conviction") {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
Marshmallow exposes two main API functions -
|
||||
[Unmarshal](https://github.com/PerimeterX/marshmallow/blob/0e0218ab860be8a4b5f57f5ff239f281c250c5da/unmarshal.go#L27)
|
||||
and
|
||||
[UnmarshalFromJSONMap](https://github.com/PerimeterX/marshmallow/blob/0e0218ab860be8a4b5f57f5ff239f281c250c5da/unmarshal_from_json_map.go#L37).
|
||||
While unmarshalling, marshmallow supports the following optional options:
|
||||
|
||||
* Setting the mode for handling invalid data using the [WithMode](https://github.com/PerimeterX/marshmallow/blob/0e0218ab860be8a4b5f57f5ff239f281c250c5da/options.go#L30) function.
|
||||
* Excluding known fields from the result map using the [WithExcludeKnownFieldsFromMap](https://github.com/PerimeterX/marshmallow/blob/457669ae9973895584f2636eabfc104140d3b700/options.go#L50) function.
|
||||
* Skipping struct population to boost performance using the [WithSkipPopulateStruct](https://github.com/PerimeterX/marshmallow/blob/0e0218ab860be8a4b5f57f5ff239f281c250c5da/options.go#L41) function.
|
||||
|
||||
In order to capture unknown nested fields, structs must implement [JSONDataErrorHandler](https://github.com/PerimeterX/marshmallow/blob/195c994aa6e3e0852601ad9cf65bcddef0dd7479/options.go#L76).
|
||||
More info [here](https://github.com/PerimeterX/marshmallow/issues/15).
|
||||
|
||||
Marshmallow also supports caching of refection information using
|
||||
[EnableCache](https://github.com/PerimeterX/marshmallow/blob/d3500aa5b0f330942b178b155da933c035dd3906/cache.go#L40)
|
||||
and
|
||||
[EnableCustomCache](https://github.com/PerimeterX/marshmallow/blob/d3500aa5b0f330942b178b155da933c035dd3906/cache.go#L35).
|
||||
|
||||
## Contact and Contribute
|
||||
|
||||
Reporting issues and requesting features may be done in our [GitHub issues page](https://github.com/PerimeterX/marshmallow/issues).
|
||||
Discussions may be conducted in our [GitHub discussions page](https://github.com/PerimeterX/marshmallow/discussions).
|
||||
For any further questions or comments you can reach us out at [open-source@humansecurity.com](mailto:open-source@humansecurity.com).
|
||||
|
||||
Any type of contribution is warmly welcome and appreciated ❤️
|
||||
Please read our [contribution](CONTRIBUTING.md) guide for more info.
|
||||
|
||||
If you're looking for something to get started with, tou can always follow our [issues page](https://github.com/PerimeterX/marshmallow/issues) and look for
|
||||
[good first issue](https://github.com/PerimeterX/marshmallow/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22) and
|
||||
[help wanted](https://github.com/PerimeterX/marshmallow/issues?q=is%3Aissue+label%3A%22help+wanted%22+is%3Aopen) labels.
|
||||
|
||||
## Marshmallow Logo
|
||||
|
||||
Marshmallow logo and assets by [Adva Rom](https://www.linkedin.com/in/adva-rom-7a6738127/) are licensed under a <a rel="license" href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.<br />
|
||||
|
||||

|
||||
63
vendor/github.com/perimeterx/marshmallow/cache.go
generated
vendored
Normal file
63
vendor/github.com/perimeterx/marshmallow/cache.go
generated
vendored
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2022 PerimeterX. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package marshmallow
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Cache allows unmarshalling to use a cached version of refection information about types.
|
||||
// Cache interface follows the implementation of sync.Map, but you may wrap any cache implementation
|
||||
// to match it. This allows you to control max cache size, eviction policies and any other caching aspect.
|
||||
type Cache interface {
|
||||
// Load returns the value stored in the map for a key, or nil if no value is present.
|
||||
// The ok result indicates whether value was found in the map.
|
||||
Load(key interface{}) (interface{}, bool)
|
||||
// Store sets the value for a key.
|
||||
Store(key, value interface{})
|
||||
}
|
||||
|
||||
// EnableCustomCache enables unmarshalling cache. It allows reuse of refection information about types needed
|
||||
// to perform the unmarshalling. A use of such cache can boost up unmarshalling by x1.4.
|
||||
// Check out benchmark_test.go for an example.
|
||||
//
|
||||
// EnableCustomCache is not thread safe! Do not use it while performing unmarshalling, or it will
|
||||
// cause an unsafe race condition. Typically, EnableCustomCache should be called once when the process boots.
|
||||
//
|
||||
// Caching is disabled by default. The use of this function allows enabling it and controlling the
|
||||
// behavior of the cache. Typically, the use of sync.Map should be good enough. The caching mechanism
|
||||
// stores a single map per struct type. If you plan to unmarshal a huge amount of distinct
|
||||
// struct it may get to consume a lot of resources, in which case you have the control to choose
|
||||
// the caching implementation you like and its setup.
|
||||
func EnableCustomCache(c Cache) {
|
||||
cache = c
|
||||
}
|
||||
|
||||
// EnableCache enables unmarshalling cache with default implementation. More info at EnableCustomCache.
|
||||
func EnableCache() {
|
||||
EnableCustomCache(&sync.Map{})
|
||||
}
|
||||
|
||||
var cache Cache
|
||||
|
||||
func cacheLookup(t reflect.Type) map[string]reflectionInfo {
|
||||
if cache == nil {
|
||||
return nil
|
||||
}
|
||||
value, exists := cache.Load(t)
|
||||
if !exists {
|
||||
return nil
|
||||
}
|
||||
result, _ := value.(map[string]reflectionInfo)
|
||||
return result
|
||||
}
|
||||
|
||||
func cacheStore(t reflect.Type, fields map[string]reflectionInfo) {
|
||||
if cache == nil {
|
||||
return
|
||||
}
|
||||
cache.Store(t, fields)
|
||||
}
|
||||
10
vendor/github.com/perimeterx/marshmallow/doc.go
generated
vendored
Normal file
10
vendor/github.com/perimeterx/marshmallow/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
Package marshmallow provides a simple API to perform flexible and performant JSON unmarshalling.
|
||||
Unlike other packages, marshmallow supports unmarshalling of some known and some unknown fields
|
||||
with zero performance overhead nor extra coding needed. While unmarshalling,
|
||||
marshmallow allows fully retaining the original data and access it via a typed struct and a
|
||||
dynamic map.
|
||||
|
||||
https://github.com/perimeterx/marshmallow
|
||||
*/
|
||||
package marshmallow
|
||||
101
vendor/github.com/perimeterx/marshmallow/errors.go
generated
vendored
Normal file
101
vendor/github.com/perimeterx/marshmallow/errors.go
generated
vendored
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
// Copyright 2022 PerimeterX. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package marshmallow
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/mailru/easyjson/jlexer"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrInvalidInput indicates the input JSON is invalid
|
||||
ErrInvalidInput = errors.New("invalid JSON input")
|
||||
|
||||
// ErrInvalidValue indicates the target struct has invalid type
|
||||
ErrInvalidValue = errors.New("unexpected non struct value")
|
||||
)
|
||||
|
||||
// MultipleLexerError indicates one or more unmarshalling errors during JSON bytes decode
|
||||
type MultipleLexerError struct {
|
||||
Errors []*jlexer.LexerError
|
||||
}
|
||||
|
||||
func (m *MultipleLexerError) Error() string {
|
||||
errs := make([]string, len(m.Errors))
|
||||
for i, lexerError := range m.Errors {
|
||||
errs[i] = lexerError.Error()
|
||||
}
|
||||
return strings.Join(errs, ", ")
|
||||
}
|
||||
|
||||
// MultipleError indicates one or more unmarshalling errors during JSON map decode
|
||||
type MultipleError struct {
|
||||
Errors []error
|
||||
}
|
||||
|
||||
func (m *MultipleError) Error() string {
|
||||
errs := make([]string, len(m.Errors))
|
||||
for i, lexerError := range m.Errors {
|
||||
errs[i] = lexerError.Error()
|
||||
}
|
||||
return strings.Join(errs, ", ")
|
||||
}
|
||||
|
||||
// ParseError indicates a JSON map decode error
|
||||
type ParseError struct {
|
||||
Reason string
|
||||
Path string
|
||||
}
|
||||
|
||||
func (p *ParseError) Error() string {
|
||||
return fmt.Sprintf("parse error: %s in %s", p.Reason, p.Path)
|
||||
}
|
||||
|
||||
func newUnexpectedTypeParseError(expectedType reflect.Type, path []string) *ParseError {
|
||||
return &ParseError{
|
||||
Reason: fmt.Sprintf("expected type %s", externalTypeName(expectedType)),
|
||||
Path: strings.Join(path, "."),
|
||||
}
|
||||
}
|
||||
|
||||
func newUnsupportedTypeParseError(unsupportedType reflect.Type, path []string) *ParseError {
|
||||
return &ParseError{
|
||||
Reason: fmt.Sprintf("unsupported type %s", externalTypeName(unsupportedType)),
|
||||
Path: strings.Join(path, "."),
|
||||
}
|
||||
}
|
||||
|
||||
func addUnexpectedTypeLexerError(lexer *jlexer.Lexer, expectedType reflect.Type) {
|
||||
lexer.AddNonFatalError(fmt.Errorf("expected type %s", externalTypeName(expectedType)))
|
||||
}
|
||||
|
||||
func addUnsupportedTypeLexerError(lexer *jlexer.Lexer, unsupportedType reflect.Type) {
|
||||
lexer.AddNonFatalError(fmt.Errorf("unsupported type %s", externalTypeName(unsupportedType)))
|
||||
}
|
||||
|
||||
func externalTypeName(t reflect.Type) string {
|
||||
switch t.Kind() {
|
||||
case reflect.String:
|
||||
return "string"
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint,
|
||||
reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32,
|
||||
reflect.Float64, reflect.Complex64, reflect.Complex128:
|
||||
return "number"
|
||||
case reflect.Bool:
|
||||
return "boolean"
|
||||
case reflect.Array, reflect.Slice:
|
||||
return "array"
|
||||
case reflect.Interface:
|
||||
return "any"
|
||||
case reflect.Map, reflect.Struct:
|
||||
return "object"
|
||||
case reflect.Ptr:
|
||||
return externalTypeName(t.Elem())
|
||||
}
|
||||
return "invalid"
|
||||
}
|
||||
96
vendor/github.com/perimeterx/marshmallow/options.go
generated
vendored
Normal file
96
vendor/github.com/perimeterx/marshmallow/options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
// Copyright 2022 PerimeterX. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package marshmallow
|
||||
|
||||
// Mode dictates the unmarshalling mode.
|
||||
// Each mode is self documented below.
|
||||
type Mode uint8
|
||||
|
||||
const (
|
||||
// ModeFailOnFirstError is the default mode. It makes unmarshalling terminate
|
||||
// immediately on any kind of error. This error will then be returned.
|
||||
ModeFailOnFirstError Mode = iota
|
||||
|
||||
// ModeAllowMultipleErrors mode makes unmarshalling keep decoding even if
|
||||
// errors are encountered. In case of such error, the erroneous value will be omitted from the result.
|
||||
// Eventually, all errors will all be returned, alongside the partial result.
|
||||
ModeAllowMultipleErrors
|
||||
|
||||
// ModeFailOverToOriginalValue mode makes unmarshalling keep decoding even if
|
||||
// errors are encountered. In case of such error, the original external value be placed in the
|
||||
// result data, even though it does not meet the schematic requirements.
|
||||
// Eventually, all errors will be returned, alongside the full result. Note that the result map
|
||||
// will contain values that do not match the struct schema.
|
||||
ModeFailOverToOriginalValue
|
||||
)
|
||||
|
||||
// WithMode is an UnmarshalOption function to set the unmarshalling mode.
|
||||
func WithMode(mode Mode) UnmarshalOption {
|
||||
return func(options *unmarshalOptions) {
|
||||
options.mode = mode
|
||||
}
|
||||
}
|
||||
|
||||
// WithSkipPopulateStruct is an UnmarshalOption function to set the skipPopulateStruct option.
|
||||
// Skipping populate struct is set to false by default.
|
||||
// If you do not intend to use the struct value once unmarshalling is finished, set this
|
||||
// option to true to boost performance. This would mean the struct fields will not be set
|
||||
// with values, but rather it will only be used as the target schema when populating the result map.
|
||||
func WithSkipPopulateStruct(skipPopulateStruct bool) UnmarshalOption {
|
||||
return func(options *unmarshalOptions) {
|
||||
options.skipPopulateStruct = skipPopulateStruct
|
||||
}
|
||||
}
|
||||
|
||||
// WithExcludeKnownFieldsFromMap is an UnmarshalOption function to set the excludeKnownFieldsFromMap option.
|
||||
// Exclude known fields flag is set to false by default.
|
||||
// When the flag is set to true, fields specified in the input struct (known fields) will be excluded from the result map
|
||||
func WithExcludeKnownFieldsFromMap(excludeKnownFields bool) UnmarshalOption {
|
||||
return func(options *unmarshalOptions) {
|
||||
options.excludeKnownFieldsFromMap = excludeKnownFields
|
||||
}
|
||||
}
|
||||
|
||||
type UnmarshalOption func(*unmarshalOptions)
|
||||
|
||||
type unmarshalOptions struct {
|
||||
mode Mode
|
||||
skipPopulateStruct bool
|
||||
excludeKnownFieldsFromMap bool
|
||||
}
|
||||
|
||||
func buildUnmarshalOptions(options []UnmarshalOption) *unmarshalOptions {
|
||||
result := &unmarshalOptions{}
|
||||
for _, option := range options {
|
||||
option(result)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// JSONDataErrorHandler allow types to handle JSON data as maps.
|
||||
// Types should implement this interface if they wish to act on the map representation of parsed JSON input.
|
||||
// This is mainly used to allow nested objects to capture unknown fields and leverage marshmallow's abilities.
|
||||
// If HandleJSONData returns an error, it will be propagated as an unmarshal error
|
||||
type JSONDataErrorHandler interface {
|
||||
HandleJSONData(data map[string]interface{}) error
|
||||
}
|
||||
|
||||
// Deprecated: use JSONDataErrorHandler instead
|
||||
type JSONDataHandler interface {
|
||||
HandleJSONData(data map[string]interface{})
|
||||
}
|
||||
|
||||
func asJSONDataHandler(value interface{}) (func(map[string]interface{}) error, bool) {
|
||||
if handler, ok := value.(JSONDataErrorHandler); ok {
|
||||
return handler.HandleJSONData, true
|
||||
}
|
||||
if handler, ok := value.(JSONDataHandler); ok {
|
||||
return func(m map[string]interface{}) error {
|
||||
handler.HandleJSONData(m)
|
||||
return nil
|
||||
}, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
197
vendor/github.com/perimeterx/marshmallow/reflection.go
generated
vendored
Normal file
197
vendor/github.com/perimeterx/marshmallow/reflection.go
generated
vendored
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
// Copyright 2022 PerimeterX. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package marshmallow
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var unmarshalerType = reflect.TypeOf((*json.Unmarshaler)(nil)).Elem()
|
||||
|
||||
type reflectionInfo struct {
|
||||
path []int
|
||||
t reflect.Type
|
||||
}
|
||||
|
||||
func (r reflectionInfo) field(target reflect.Value) reflect.Value {
|
||||
current := target
|
||||
for _, i := range r.path {
|
||||
current = current.Field(i)
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
func mapStructFields(target interface{}) map[string]reflectionInfo {
|
||||
t := reflectStructType(target)
|
||||
result := cacheLookup(t)
|
||||
if result != nil {
|
||||
return result
|
||||
}
|
||||
result = make(map[string]reflectionInfo, t.NumField())
|
||||
mapTypeFields(t, result, nil)
|
||||
cacheStore(t, result)
|
||||
return result
|
||||
}
|
||||
|
||||
func mapTypeFields(t reflect.Type, result map[string]reflectionInfo, path []int) {
|
||||
num := t.NumField()
|
||||
for i := 0; i < num; i++ {
|
||||
field := t.Field(i)
|
||||
fieldPath := append(path, i)
|
||||
if field.Anonymous && field.Type.Kind() == reflect.Struct {
|
||||
mapTypeFields(field.Type, result, fieldPath)
|
||||
continue
|
||||
}
|
||||
name := field.Tag.Get("json")
|
||||
if name == "" || name == "-" {
|
||||
continue
|
||||
}
|
||||
if index := strings.Index(name, ","); index > -1 {
|
||||
name = name[:index]
|
||||
}
|
||||
result[name] = reflectionInfo{
|
||||
path: fieldPath,
|
||||
t: field.Type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func reflectStructValue(target interface{}) reflect.Value {
|
||||
v := reflect.ValueOf(target)
|
||||
for v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func reflectStructType(target interface{}) reflect.Type {
|
||||
t := reflect.TypeOf(target)
|
||||
for t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
var primitiveConverters = map[reflect.Kind]func(v interface{}) (interface{}, bool){
|
||||
reflect.Bool: func(v interface{}) (interface{}, bool) {
|
||||
res, ok := v.(bool)
|
||||
return res, ok
|
||||
},
|
||||
reflect.Int: func(v interface{}) (interface{}, bool) {
|
||||
res, ok := v.(float64)
|
||||
if ok {
|
||||
return int(res), true
|
||||
}
|
||||
return v, false
|
||||
},
|
||||
reflect.Int8: func(v interface{}) (interface{}, bool) {
|
||||
res, ok := v.(float64)
|
||||
if ok {
|
||||
return int8(res), true
|
||||
}
|
||||
return v, false
|
||||
},
|
||||
reflect.Int16: func(v interface{}) (interface{}, bool) {
|
||||
res, ok := v.(float64)
|
||||
if ok {
|
||||
return int16(res), true
|
||||
}
|
||||
return v, false
|
||||
},
|
||||
reflect.Int32: func(v interface{}) (interface{}, bool) {
|
||||
res, ok := v.(float64)
|
||||
if ok {
|
||||
return int32(res), true
|
||||
}
|
||||
return v, false
|
||||
},
|
||||
reflect.Int64: func(v interface{}) (interface{}, bool) {
|
||||
res, ok := v.(float64)
|
||||
if ok {
|
||||
return int64(res), true
|
||||
}
|
||||
return v, false
|
||||
},
|
||||
reflect.Uint: func(v interface{}) (interface{}, bool) {
|
||||
res, ok := v.(float64)
|
||||
if ok {
|
||||
return uint(res), true
|
||||
}
|
||||
return v, false
|
||||
},
|
||||
reflect.Uint8: func(v interface{}) (interface{}, bool) {
|
||||
res, ok := v.(float64)
|
||||
if ok {
|
||||
return uint8(res), true
|
||||
}
|
||||
return v, false
|
||||
},
|
||||
reflect.Uint16: func(v interface{}) (interface{}, bool) {
|
||||
res, ok := v.(float64)
|
||||
if ok {
|
||||
return uint16(res), true
|
||||
}
|
||||
return v, false
|
||||
},
|
||||
reflect.Uint32: func(v interface{}) (interface{}, bool) {
|
||||
res, ok := v.(float64)
|
||||
if ok {
|
||||
return uint32(res), true
|
||||
}
|
||||
return v, false
|
||||
},
|
||||
reflect.Uint64: func(v interface{}) (interface{}, bool) {
|
||||
res, ok := v.(float64)
|
||||
if ok {
|
||||
return uint64(res), true
|
||||
}
|
||||
return v, false
|
||||
},
|
||||
reflect.Float32: func(v interface{}) (interface{}, bool) {
|
||||
res, ok := v.(float64)
|
||||
if ok {
|
||||
return float32(res), true
|
||||
}
|
||||
return v, false
|
||||
},
|
||||
reflect.Float64: func(v interface{}) (interface{}, bool) {
|
||||
res, ok := v.(float64)
|
||||
if ok {
|
||||
return res, true
|
||||
}
|
||||
return v, false
|
||||
},
|
||||
reflect.Interface: func(v interface{}) (interface{}, bool) {
|
||||
return v, true
|
||||
},
|
||||
reflect.String: func(v interface{}) (interface{}, bool) {
|
||||
res, ok := v.(string)
|
||||
return res, ok
|
||||
},
|
||||
}
|
||||
|
||||
func assignValue(field reflect.Value, value interface{}) {
|
||||
if value == nil {
|
||||
return
|
||||
}
|
||||
reflectValue := reflect.ValueOf(value)
|
||||
if reflectValue.Type().AssignableTo(field.Type()) {
|
||||
field.Set(reflectValue)
|
||||
}
|
||||
}
|
||||
|
||||
func isValidValue(v interface{}) bool {
|
||||
value := reflect.ValueOf(v)
|
||||
return value.Kind() == reflect.Ptr && value.Elem().Kind() == reflect.Struct && !value.IsNil()
|
||||
}
|
||||
|
||||
func safeReflectValue(t reflect.Type, v interface{}) reflect.Value {
|
||||
if v == nil {
|
||||
return reflect.Zero(t)
|
||||
}
|
||||
return reflect.ValueOf(v)
|
||||
}
|
||||
383
vendor/github.com/perimeterx/marshmallow/unmarshal.go
generated
vendored
Normal file
383
vendor/github.com/perimeterx/marshmallow/unmarshal.go
generated
vendored
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
// Copyright 2022 PerimeterX. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package marshmallow
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/mailru/easyjson/jlexer"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Unmarshal parses the JSON-encoded object in data and stores the values
|
||||
// in the struct pointed to by v and in the returned map.
|
||||
// If v is nil or not a pointer to a struct, Unmarshal returns an ErrInvalidValue.
|
||||
// If data is not a valid JSON or not a JSON object Unmarshal returns an ErrInvalidInput.
|
||||
//
|
||||
// Unmarshal follows the rules of json.Unmarshal with the following exceptions:
|
||||
// - All input fields are stored in the resulting map, including fields that do not exist in the
|
||||
// struct pointed by v.
|
||||
// - Unmarshal only operates on JSON object inputs. It will reject all other types of input
|
||||
// by returning ErrInvalidInput.
|
||||
// - Unmarshal only operates on struct values. It will reject all other types of v by
|
||||
// returning ErrInvalidValue.
|
||||
// - Unmarshal supports three types of Mode values. Each mode is self documented and affects
|
||||
// how Unmarshal behaves.
|
||||
func Unmarshal(data []byte, v interface{}, options ...UnmarshalOption) (map[string]interface{}, error) {
|
||||
if !isValidValue(v) {
|
||||
return nil, ErrInvalidValue
|
||||
}
|
||||
opts := buildUnmarshalOptions(options)
|
||||
useMultipleErrors := opts.mode == ModeAllowMultipleErrors || opts.mode == ModeFailOverToOriginalValue
|
||||
d := &decoder{options: opts, lexer: &jlexer.Lexer{Data: data, UseMultipleErrors: useMultipleErrors}}
|
||||
result := make(map[string]interface{})
|
||||
if d.lexer.IsNull() {
|
||||
d.lexer.Skip()
|
||||
} else if !d.lexer.IsDelim('{') {
|
||||
return nil, ErrInvalidInput
|
||||
} else {
|
||||
d.populateStruct(false, v, result)
|
||||
}
|
||||
d.lexer.Consumed()
|
||||
if useMultipleErrors {
|
||||
errors := d.lexer.GetNonFatalErrors()
|
||||
if len(errors) == 0 {
|
||||
return result, nil
|
||||
}
|
||||
return result, &MultipleLexerError{Errors: errors}
|
||||
}
|
||||
err := d.lexer.Error()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type decoder struct {
|
||||
options *unmarshalOptions
|
||||
lexer *jlexer.Lexer
|
||||
}
|
||||
|
||||
func (d *decoder) populateStruct(forcePopulate bool, structInstance interface{}, result map[string]interface{}) (interface{}, bool) {
|
||||
doPopulate := !d.options.skipPopulateStruct || forcePopulate
|
||||
var structValue reflect.Value
|
||||
if doPopulate {
|
||||
structValue = reflectStructValue(structInstance)
|
||||
}
|
||||
fields := mapStructFields(structInstance)
|
||||
var clone map[string]interface{}
|
||||
if d.options.mode == ModeFailOverToOriginalValue {
|
||||
clone = make(map[string]interface{}, len(fields))
|
||||
}
|
||||
d.lexer.Delim('{')
|
||||
for !d.lexer.IsDelim('}') {
|
||||
key := d.lexer.UnsafeFieldName(false)
|
||||
d.lexer.WantColon()
|
||||
refInfo, exists := fields[key]
|
||||
if exists {
|
||||
value, isValidType := d.valueByReflectType(refInfo.t)
|
||||
if isValidType {
|
||||
if value != nil && doPopulate {
|
||||
field := refInfo.field(structValue)
|
||||
assignValue(field, value)
|
||||
}
|
||||
if !d.options.excludeKnownFieldsFromMap {
|
||||
if result != nil {
|
||||
result[key] = value
|
||||
}
|
||||
if clone != nil {
|
||||
clone[key] = value
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch d.options.mode {
|
||||
case ModeFailOnFirstError:
|
||||
return nil, false
|
||||
case ModeFailOverToOriginalValue:
|
||||
if !forcePopulate {
|
||||
result[key] = value
|
||||
} else {
|
||||
clone[key] = value
|
||||
d.lexer.WantComma()
|
||||
d.drainLexerMap(clone)
|
||||
return clone, false
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
value := d.lexer.Interface()
|
||||
if result != nil {
|
||||
result[key] = value
|
||||
}
|
||||
if clone != nil {
|
||||
clone[key] = value
|
||||
}
|
||||
}
|
||||
d.lexer.WantComma()
|
||||
}
|
||||
d.lexer.Delim('}')
|
||||
return structInstance, true
|
||||
}
|
||||
|
||||
func (d *decoder) valueByReflectType(t reflect.Type) (interface{}, bool) {
|
||||
if t.Implements(unmarshalerType) {
|
||||
result := reflect.New(t.Elem()).Interface()
|
||||
d.valueFromCustomUnmarshaler(result.(json.Unmarshaler))
|
||||
return result, true
|
||||
}
|
||||
if reflect.PtrTo(t).Implements(unmarshalerType) {
|
||||
value := reflect.New(t)
|
||||
d.valueFromCustomUnmarshaler(value.Interface().(json.Unmarshaler))
|
||||
return value.Elem().Interface(), true
|
||||
}
|
||||
kind := t.Kind()
|
||||
if converter := primitiveConverters[kind]; converter != nil {
|
||||
v := d.lexer.Interface()
|
||||
if v == nil {
|
||||
return nil, true
|
||||
}
|
||||
converted, ok := converter(v)
|
||||
if !ok {
|
||||
addUnexpectedTypeLexerError(d.lexer, t)
|
||||
return v, false
|
||||
}
|
||||
return converted, true
|
||||
}
|
||||
switch kind {
|
||||
case reflect.Slice:
|
||||
return d.buildSlice(t)
|
||||
case reflect.Array:
|
||||
return d.buildArray(t)
|
||||
case reflect.Map:
|
||||
return d.buildMap(t)
|
||||
case reflect.Struct:
|
||||
value, valid := d.buildStruct(t)
|
||||
if value == nil {
|
||||
return nil, valid
|
||||
}
|
||||
if !valid {
|
||||
return value, false
|
||||
}
|
||||
return reflect.ValueOf(value).Elem().Interface(), valid
|
||||
case reflect.Ptr:
|
||||
if t.Elem().Kind() == reflect.Struct {
|
||||
return d.buildStruct(t.Elem())
|
||||
}
|
||||
value, valid := d.valueByReflectType(t.Elem())
|
||||
if value == nil {
|
||||
return nil, valid
|
||||
}
|
||||
if !valid {
|
||||
return value, false
|
||||
}
|
||||
result := reflect.New(reflect.TypeOf(value))
|
||||
result.Elem().Set(reflect.ValueOf(value))
|
||||
return result.Interface(), valid
|
||||
}
|
||||
addUnsupportedTypeLexerError(d.lexer, t)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (d *decoder) buildSlice(sliceType reflect.Type) (interface{}, bool) {
|
||||
if d.lexer.IsNull() {
|
||||
d.lexer.Skip()
|
||||
return nil, true
|
||||
}
|
||||
if !d.lexer.IsDelim('[') {
|
||||
addUnexpectedTypeLexerError(d.lexer, sliceType)
|
||||
return d.lexer.Interface(), false
|
||||
}
|
||||
elemType := sliceType.Elem()
|
||||
d.lexer.Delim('[')
|
||||
var sliceValue reflect.Value
|
||||
if !d.lexer.IsDelim(']') {
|
||||
sliceValue = reflect.MakeSlice(sliceType, 0, 4)
|
||||
} else {
|
||||
sliceValue = reflect.MakeSlice(sliceType, 0, 0)
|
||||
}
|
||||
for !d.lexer.IsDelim(']') {
|
||||
current, valid := d.valueByReflectType(elemType)
|
||||
if !valid {
|
||||
if d.options.mode != ModeFailOverToOriginalValue {
|
||||
d.drainLexerArray(nil)
|
||||
return nil, true
|
||||
}
|
||||
result := d.cloneReflectArray(sliceValue, -1)
|
||||
result = append(result, current)
|
||||
return d.drainLexerArray(result), true
|
||||
}
|
||||
sliceValue = reflect.Append(sliceValue, safeReflectValue(elemType, current))
|
||||
d.lexer.WantComma()
|
||||
}
|
||||
d.lexer.Delim(']')
|
||||
return sliceValue.Interface(), true
|
||||
}
|
||||
|
||||
func (d *decoder) buildArray(arrayType reflect.Type) (interface{}, bool) {
|
||||
if d.lexer.IsNull() {
|
||||
d.lexer.Skip()
|
||||
return nil, true
|
||||
}
|
||||
if !d.lexer.IsDelim('[') {
|
||||
addUnexpectedTypeLexerError(d.lexer, arrayType)
|
||||
return d.lexer.Interface(), false
|
||||
}
|
||||
elemType := arrayType.Elem()
|
||||
arrayValue := reflect.New(arrayType).Elem()
|
||||
d.lexer.Delim('[')
|
||||
for i := 0; !d.lexer.IsDelim(']'); i++ {
|
||||
current, valid := d.valueByReflectType(elemType)
|
||||
if !valid {
|
||||
if d.options.mode != ModeFailOverToOriginalValue {
|
||||
d.drainLexerArray(nil)
|
||||
return nil, true
|
||||
}
|
||||
result := d.cloneReflectArray(arrayValue, i)
|
||||
result = append(result, current)
|
||||
return d.drainLexerArray(result), true
|
||||
}
|
||||
if current != nil {
|
||||
arrayValue.Index(i).Set(reflect.ValueOf(current))
|
||||
}
|
||||
d.lexer.WantComma()
|
||||
}
|
||||
d.lexer.Delim(']')
|
||||
return arrayValue.Interface(), true
|
||||
}
|
||||
|
||||
func (d *decoder) buildMap(mapType reflect.Type) (interface{}, bool) {
|
||||
if d.lexer.IsNull() {
|
||||
d.lexer.Skip()
|
||||
return nil, true
|
||||
}
|
||||
if !d.lexer.IsDelim('{') {
|
||||
addUnexpectedTypeLexerError(d.lexer, mapType)
|
||||
return d.lexer.Interface(), false
|
||||
}
|
||||
d.lexer.Delim('{')
|
||||
keyType := mapType.Key()
|
||||
valueType := mapType.Elem()
|
||||
mapValue := reflect.MakeMap(mapType)
|
||||
for !d.lexer.IsDelim('}') {
|
||||
key, valid := d.valueByReflectType(keyType)
|
||||
if !valid {
|
||||
if d.options.mode != ModeFailOverToOriginalValue {
|
||||
d.lexer.WantColon()
|
||||
d.lexer.Interface()
|
||||
d.lexer.WantComma()
|
||||
d.drainLexerMap(make(map[string]interface{}))
|
||||
return nil, true
|
||||
}
|
||||
strKey, _ := key.(string)
|
||||
d.lexer.WantColon()
|
||||
value := d.lexer.Interface()
|
||||
result := d.cloneReflectMap(mapValue)
|
||||
result[strKey] = value
|
||||
d.lexer.WantComma()
|
||||
d.drainLexerMap(result)
|
||||
return result, true
|
||||
}
|
||||
d.lexer.WantColon()
|
||||
value, valid := d.valueByReflectType(valueType)
|
||||
if !valid {
|
||||
if d.options.mode != ModeFailOverToOriginalValue {
|
||||
d.lexer.WantComma()
|
||||
d.drainLexerMap(make(map[string]interface{}))
|
||||
return nil, true
|
||||
}
|
||||
strKey, _ := key.(string)
|
||||
result := d.cloneReflectMap(mapValue)
|
||||
result[strKey] = value
|
||||
d.lexer.WantComma()
|
||||
d.drainLexerMap(result)
|
||||
return result, true
|
||||
}
|
||||
mapValue.SetMapIndex(safeReflectValue(keyType, key), safeReflectValue(valueType, value))
|
||||
d.lexer.WantComma()
|
||||
}
|
||||
d.lexer.Delim('}')
|
||||
return mapValue.Interface(), true
|
||||
}
|
||||
|
||||
func (d *decoder) buildStruct(structType reflect.Type) (interface{}, bool) {
|
||||
if d.lexer.IsNull() {
|
||||
d.lexer.Skip()
|
||||
return nil, true
|
||||
}
|
||||
if !d.lexer.IsDelim('{') {
|
||||
addUnexpectedTypeLexerError(d.lexer, structType)
|
||||
return d.lexer.Interface(), false
|
||||
}
|
||||
value := reflect.New(structType).Interface()
|
||||
handler, ok := asJSONDataHandler(value)
|
||||
if !ok {
|
||||
return d.populateStruct(true, value, nil)
|
||||
}
|
||||
data := make(map[string]interface{})
|
||||
result, valid := d.populateStruct(true, value, data)
|
||||
if !valid {
|
||||
return result, false
|
||||
}
|
||||
err := handler(data)
|
||||
if err != nil {
|
||||
d.lexer.AddNonFatalError(err)
|
||||
return result, false
|
||||
}
|
||||
return result, true
|
||||
}
|
||||
|
||||
func (d *decoder) valueFromCustomUnmarshaler(unmarshaler json.Unmarshaler) {
|
||||
data := d.lexer.Raw()
|
||||
if !d.lexer.Ok() {
|
||||
return
|
||||
}
|
||||
err := unmarshaler.UnmarshalJSON(data)
|
||||
if err != nil {
|
||||
d.lexer.AddNonFatalError(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *decoder) cloneReflectArray(value reflect.Value, length int) []interface{} {
|
||||
if length == -1 {
|
||||
length = value.Len()
|
||||
}
|
||||
result := make([]interface{}, length)
|
||||
for i := 0; i < length; i++ {
|
||||
result[i] = value.Index(i).Interface()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (d *decoder) cloneReflectMap(mapValue reflect.Value) map[string]interface{} {
|
||||
l := mapValue.Len()
|
||||
result := make(map[string]interface{}, l)
|
||||
for _, key := range mapValue.MapKeys() {
|
||||
value := mapValue.MapIndex(key)
|
||||
strKey, _ := key.Interface().(string)
|
||||
result[strKey] = value.Interface()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (d *decoder) drainLexerArray(target []interface{}) interface{} {
|
||||
d.lexer.WantComma()
|
||||
for !d.lexer.IsDelim(']') {
|
||||
current := d.lexer.Interface()
|
||||
target = append(target, current)
|
||||
d.lexer.WantComma()
|
||||
}
|
||||
d.lexer.Delim(']')
|
||||
return target
|
||||
}
|
||||
|
||||
func (d *decoder) drainLexerMap(target map[string]interface{}) {
|
||||
for !d.lexer.IsDelim('}') {
|
||||
key := d.lexer.String()
|
||||
d.lexer.WantColon()
|
||||
value := d.lexer.Interface()
|
||||
target[key] = value
|
||||
d.lexer.WantComma()
|
||||
}
|
||||
d.lexer.Delim('}')
|
||||
}
|
||||
295
vendor/github.com/perimeterx/marshmallow/unmarshal_from_json_map.go
generated
vendored
Normal file
295
vendor/github.com/perimeterx/marshmallow/unmarshal_from_json_map.go
generated
vendored
Normal file
|
|
@ -0,0 +1,295 @@
|
|||
// Copyright 2022 PerimeterX. All rights reserved.
|
||||
// Use of this source code is governed by a MIT style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package marshmallow
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// UnmarshalerFromJSONMap is the interface implemented by types
|
||||
// that can unmarshal a JSON description of themselves.
|
||||
// In case you want to implement custom unmarshalling, json.Unmarshaler only supports
|
||||
// receiving the data as []byte. However, while unmarshalling from JSON map,
|
||||
// the data is not available as a raw []byte and converting to it will significantly
|
||||
// hurt performance. Thus, if you wish to implement a custom unmarshalling on a type
|
||||
// that is being unmarshalled from a JSON map, you need to implement
|
||||
// UnmarshalerFromJSONMap interface.
|
||||
type UnmarshalerFromJSONMap interface {
|
||||
UnmarshalJSONFromMap(data interface{}) error
|
||||
}
|
||||
|
||||
// UnmarshalFromJSONMap parses the JSON map data and stores the values
|
||||
// in the struct pointed to by v and in the returned map.
|
||||
// If v is nil or not a pointer to a struct, UnmarshalFromJSONMap returns an ErrInvalidValue.
|
||||
//
|
||||
// UnmarshalFromJSONMap follows the rules of json.Unmarshal with the following exceptions:
|
||||
// - All input fields are stored in the resulting map, including fields that do not exist in the
|
||||
// struct pointed by v.
|
||||
// - UnmarshalFromJSONMap receive a JSON map instead of raw bytes. The given input map is assumed
|
||||
// to be a JSON map, meaning it should only contain the following types: bool, string, float64,
|
||||
// []interface, and map[string]interface{}. Other types will cause decoding to return unexpected results.
|
||||
// - UnmarshalFromJSONMap only operates on struct values. It will reject all other types of v by
|
||||
// returning ErrInvalidValue.
|
||||
// - UnmarshalFromJSONMap supports three types of Mode values. Each mode is self documented and affects
|
||||
// how UnmarshalFromJSONMap behaves.
|
||||
func UnmarshalFromJSONMap(data map[string]interface{}, v interface{}, options ...UnmarshalOption) (map[string]interface{}, error) {
|
||||
if !isValidValue(v) {
|
||||
return nil, ErrInvalidValue
|
||||
}
|
||||
opts := buildUnmarshalOptions(options)
|
||||
d := &mapDecoder{options: opts}
|
||||
result := make(map[string]interface{})
|
||||
if data != nil {
|
||||
d.populateStruct(false, nil, data, v, result)
|
||||
}
|
||||
if opts.mode == ModeAllowMultipleErrors || opts.mode == ModeFailOverToOriginalValue {
|
||||
if len(d.errs) == 0 {
|
||||
return result, nil
|
||||
}
|
||||
return result, &MultipleError{Errors: d.errs}
|
||||
}
|
||||
if d.err != nil {
|
||||
return nil, d.err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
var unmarshalerFromJSONMapType = reflect.TypeOf((*UnmarshalerFromJSONMap)(nil)).Elem()
|
||||
|
||||
type mapDecoder struct {
|
||||
options *unmarshalOptions
|
||||
err error
|
||||
errs []error
|
||||
}
|
||||
|
||||
func (m *mapDecoder) populateStruct(forcePopulate bool, path []string, data map[string]interface{}, structInstance interface{}, result map[string]interface{}) (interface{}, bool) {
|
||||
doPopulate := !m.options.skipPopulateStruct || forcePopulate
|
||||
var structValue reflect.Value
|
||||
if doPopulate {
|
||||
structValue = reflectStructValue(structInstance)
|
||||
}
|
||||
fields := mapStructFields(structInstance)
|
||||
for key, inputValue := range data {
|
||||
refInfo, exists := fields[key]
|
||||
if exists {
|
||||
value, isValidType := m.valueByReflectType(append(path, key), inputValue, refInfo.t)
|
||||
if isValidType {
|
||||
if value != nil && doPopulate {
|
||||
field := refInfo.field(structValue)
|
||||
assignValue(field, value)
|
||||
}
|
||||
if !m.options.excludeKnownFieldsFromMap {
|
||||
if result != nil {
|
||||
result[key] = value
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch m.options.mode {
|
||||
case ModeFailOnFirstError:
|
||||
return nil, false
|
||||
case ModeFailOverToOriginalValue:
|
||||
if !forcePopulate {
|
||||
result[key] = value
|
||||
} else {
|
||||
return data, false
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if result != nil {
|
||||
result[key] = inputValue
|
||||
}
|
||||
}
|
||||
}
|
||||
return structInstance, true
|
||||
}
|
||||
|
||||
func (m *mapDecoder) valueByReflectType(path []string, v interface{}, t reflect.Type) (interface{}, bool) {
|
||||
if t.Implements(unmarshalerFromJSONMapType) {
|
||||
result := reflect.New(t.Elem()).Interface()
|
||||
m.valueFromCustomUnmarshaler(v, result.(UnmarshalerFromJSONMap))
|
||||
return result, true
|
||||
}
|
||||
if reflect.PtrTo(t).Implements(unmarshalerFromJSONMapType) {
|
||||
value := reflect.New(t)
|
||||
m.valueFromCustomUnmarshaler(v, value.Interface().(UnmarshalerFromJSONMap))
|
||||
return value.Elem().Interface(), true
|
||||
}
|
||||
kind := t.Kind()
|
||||
if converter := primitiveConverters[kind]; converter != nil {
|
||||
if v == nil {
|
||||
return nil, true
|
||||
}
|
||||
converted, ok := converter(v)
|
||||
if !ok {
|
||||
m.addError(newUnexpectedTypeParseError(t, path))
|
||||
return v, false
|
||||
}
|
||||
return converted, true
|
||||
}
|
||||
switch kind {
|
||||
case reflect.Slice:
|
||||
return m.buildSlice(path, v, t)
|
||||
case reflect.Array:
|
||||
return m.buildArray(path, v, t)
|
||||
case reflect.Map:
|
||||
return m.buildMap(path, v, t)
|
||||
case reflect.Struct:
|
||||
value, valid := m.buildStruct(path, v, t)
|
||||
if value == nil {
|
||||
return nil, valid
|
||||
}
|
||||
if !valid {
|
||||
return value, false
|
||||
}
|
||||
return reflect.ValueOf(value).Elem().Interface(), valid
|
||||
case reflect.Ptr:
|
||||
if t.Elem().Kind() == reflect.Struct {
|
||||
return m.buildStruct(path, v, t.Elem())
|
||||
}
|
||||
value, valid := m.valueByReflectType(path, v, t.Elem())
|
||||
if value == nil {
|
||||
return nil, valid
|
||||
}
|
||||
if !valid {
|
||||
return value, false
|
||||
}
|
||||
result := reflect.New(reflect.TypeOf(value))
|
||||
result.Elem().Set(reflect.ValueOf(value))
|
||||
return result.Interface(), valid
|
||||
}
|
||||
m.addError(newUnsupportedTypeParseError(t, path))
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (m *mapDecoder) buildSlice(path []string, v interface{}, sliceType reflect.Type) (interface{}, bool) {
|
||||
if v == nil {
|
||||
return nil, true
|
||||
}
|
||||
arr, ok := v.([]interface{})
|
||||
if !ok {
|
||||
m.addError(newUnexpectedTypeParseError(sliceType, path))
|
||||
return v, false
|
||||
}
|
||||
elemType := sliceType.Elem()
|
||||
var sliceValue reflect.Value
|
||||
if len(arr) > 0 {
|
||||
sliceValue = reflect.MakeSlice(sliceType, 0, 4)
|
||||
} else {
|
||||
sliceValue = reflect.MakeSlice(sliceType, 0, 0)
|
||||
}
|
||||
for _, element := range arr {
|
||||
current, valid := m.valueByReflectType(path, element, elemType)
|
||||
if !valid {
|
||||
if m.options.mode != ModeFailOverToOriginalValue {
|
||||
return nil, true
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
sliceValue = reflect.Append(sliceValue, safeReflectValue(elemType, current))
|
||||
}
|
||||
return sliceValue.Interface(), true
|
||||
}
|
||||
|
||||
func (m *mapDecoder) buildArray(path []string, v interface{}, arrayType reflect.Type) (interface{}, bool) {
|
||||
if v == nil {
|
||||
return nil, true
|
||||
}
|
||||
arr, ok := v.([]interface{})
|
||||
if !ok {
|
||||
m.addError(newUnexpectedTypeParseError(arrayType, path))
|
||||
return v, false
|
||||
}
|
||||
elemType := arrayType.Elem()
|
||||
arrayValue := reflect.New(arrayType).Elem()
|
||||
for i, element := range arr {
|
||||
current, valid := m.valueByReflectType(path, element, elemType)
|
||||
if !valid {
|
||||
if m.options.mode != ModeFailOverToOriginalValue {
|
||||
return nil, true
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
if current != nil {
|
||||
arrayValue.Index(i).Set(reflect.ValueOf(current))
|
||||
}
|
||||
}
|
||||
return arrayValue.Interface(), true
|
||||
}
|
||||
|
||||
func (m *mapDecoder) buildMap(path []string, v interface{}, mapType reflect.Type) (interface{}, bool) {
|
||||
if v == nil {
|
||||
return nil, true
|
||||
}
|
||||
mp, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
m.addError(newUnexpectedTypeParseError(mapType, path))
|
||||
return v, false
|
||||
}
|
||||
keyType := mapType.Key()
|
||||
valueType := mapType.Elem()
|
||||
mapValue := reflect.MakeMap(mapType)
|
||||
for inputKey, inputValue := range mp {
|
||||
keyPath := append(path, inputKey)
|
||||
key, valid := m.valueByReflectType(keyPath, inputKey, keyType)
|
||||
if !valid {
|
||||
if m.options.mode != ModeFailOverToOriginalValue {
|
||||
return nil, true
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
value, valid := m.valueByReflectType(keyPath, inputValue, valueType)
|
||||
if !valid {
|
||||
if m.options.mode != ModeFailOverToOriginalValue {
|
||||
return nil, true
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
mapValue.SetMapIndex(safeReflectValue(keyType, key), safeReflectValue(valueType, value))
|
||||
}
|
||||
return mapValue.Interface(), true
|
||||
}
|
||||
|
||||
func (m *mapDecoder) buildStruct(path []string, v interface{}, structType reflect.Type) (interface{}, bool) {
|
||||
if v == nil {
|
||||
return nil, true
|
||||
}
|
||||
mp, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
m.addError(newUnexpectedTypeParseError(structType, path))
|
||||
return v, false
|
||||
}
|
||||
value := reflect.New(structType).Interface()
|
||||
handler, ok := asJSONDataHandler(value)
|
||||
if !ok {
|
||||
return m.populateStruct(true, path, mp, value, nil)
|
||||
}
|
||||
data := make(map[string]interface{})
|
||||
result, valid := m.populateStruct(true, path, mp, value, data)
|
||||
if !valid {
|
||||
return result, false
|
||||
}
|
||||
err := handler(data)
|
||||
if err != nil {
|
||||
m.addError(err)
|
||||
return result, false
|
||||
}
|
||||
return result, true
|
||||
}
|
||||
|
||||
func (m *mapDecoder) valueFromCustomUnmarshaler(data interface{}, unmarshaler UnmarshalerFromJSONMap) {
|
||||
err := unmarshaler.UnmarshalJSONFromMap(data)
|
||||
if err != nil {
|
||||
m.addError(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mapDecoder) addError(err error) {
|
||||
if m.options.mode == ModeFailOnFirstError {
|
||||
m.err = err
|
||||
} else {
|
||||
m.errs = append(m.errs, err)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue