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:
Sanne Raymaekers 2025-03-21 11:50:30 +01:00 committed by Ondřej Budai
parent c5cb0d0618
commit b2700903ae
403 changed files with 44758 additions and 16347 deletions

4
vendor/github.com/perimeterx/marshmallow/.gitignore generated vendored Normal file
View file

@ -0,0 +1,4 @@
/.idea
coverage.out
profile.out

49
vendor/github.com/perimeterx/marshmallow/CHANGELOG.md generated vendored Normal file
View 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/).

View 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

View 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
View 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
View file

@ -0,0 +1,205 @@
# Marshmallow
![Marshmallow Campfire](https://raw.githubusercontent.com/PerimeterX/marshmallow/assets/campfire.png)
[![CodeQL Status](https://img.shields.io/github/actions/workflow/status/perimeterx/marshmallow/codeql.yml?branch=main&logo=github&label=CodeQL)](https://github.com/PerimeterX/marshmallow/actions/workflows/codeql.yml?query=branch%3Amain++)
[![Run Tests](https://img.shields.io/github/actions/workflow/status/perimeterx/marshmallow/go.yml?branch=main&logo=github&label=Run%20Tests)](https://github.com/PerimeterX/marshmallow/actions/workflows/go.yml?query=branch%3Amain)
[![Dependency Review](https://img.shields.io/github/actions/workflow/status/perimeterx/marshmallow/dependency-review.yml?logo=github&label=Dependency%20Review)](https://github.com/PerimeterX/marshmallow/actions/workflows/dependency-review.yml?query=branch%3Amain)
[![Go Report Card](https://goreportcard.com/badge/github.com/perimeterx/marshmallow)](https://goreportcard.com/report/github.com/perimeterx/marshmallow)
![Manual Code Coverage](https://img.shields.io/badge/coverage-92.6%25-green)
[![Go Reference](https://pkg.go.dev/badge/github.com/perimeterx/marshmallow.svg)](https://pkg.go.dev/github.com/perimeterx/marshmallow)
[![Licence](https://img.shields.io/github/license/perimeterx/marshmallow)](LICENSE)
[![Latest Release](https://img.shields.io/github/v/release/perimeterx/marshmallow)](https://github.com/PerimeterX/marshmallow/releases)
![Top Languages](https://img.shields.io/github/languages/top/perimeterx/marshmallow)
[![Issues](https://img.shields.io/github/issues-closed/perimeterx/marshmallow?color=%238250df&logo=github)](https://github.com/PerimeterX/marshmallow/issues)
[![Pull Requests](https://img.shields.io/github/issues-pr-closed-raw/perimeterx/marshmallow?color=%238250df&label=merged%20pull%20requests&logo=github)](https://github.com/PerimeterX/marshmallow/pulls)
[![Commits](https://img.shields.io/github/last-commit/perimeterx/marshmallow)](https://github.com/PerimeterX/marshmallow/commits/main)
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](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 performance comparison](https://raw.githubusercontent.com/PerimeterX/marshmallow/e45088ca20d4ea5be4143d418d12da63a68d6dfd/performance-chart.svg)
**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 />
![Marshmallow Logo](https://raw.githubusercontent.com/PerimeterX/marshmallow/assets/marshmallow.png)

63
vendor/github.com/perimeterx/marshmallow/cache.go generated vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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('}')
}

View 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)
}
}