mirror of
https://gitee.com/johng/gf
synced 2026-06-08 10:37:44 +08:00
Compare commits
10 Commits
fix/4086-f
...
feat/auto-
| Author | SHA1 | Date | |
|---|---|---|---|
| 7be7760037 | |||
| 2982398379 | |||
| f45f71149e | |||
| bb696bb281 | |||
| 029f324c5c | |||
| f8331bad6e | |||
| bcda48bf82 | |||
| a8a055f122 | |||
| dfe088f5cd | |||
| f4074cd815 |
20
.github/PULL_REQUEST_TEMPLATE.MD
vendored
20
.github/PULL_REQUEST_TEMPLATE.MD
vendored
@ -1,16 +1,16 @@
|
||||
**Please ensure you adhere to every item in this list.**
|
||||
+ The PR title is formatted as follows: `<type>[optional scope]: <description>` For example, `fix(os/gtime): fix time zone issue`
|
||||
+ `<type>` is mandatory and can be one of `fix`, `feat`, `build`, `ci`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`
|
||||
+ fix: Used when a bug has been fixed.
|
||||
+ feat: Used when a new feature has been added.
|
||||
+ build: Used for modifications to the project build system, such as changes to dependencies, external interfaces, or upgrading Node version.
|
||||
+ ci: Used for modifications to continuous integration processes, such as changes to Travis, Jenkins workflow configurations.
|
||||
+ docs: Used for modifications to documentation, such as changes to README files, API documentation, etc.
|
||||
+ style: Used for changes to code style, such as adjustments to indentation, spaces, blank lines, etc.
|
||||
+ refactor: Used for code refactoring, such as changes to code structure, variable names, function names, without altering functionality.
|
||||
+ perf: Used for performance optimization, such as improving code performance, reducing memory usage, etc.
|
||||
+ test: Used for modifications to test cases, such as adding, deleting, or modifying test cases for code.
|
||||
+ chore: Used for modifications to non-business-related code, such as changes to build processes or tool configurations.
|
||||
+ `fix`: Used when a bug has been fixed.
|
||||
+ `feat`: Used when a new feature has been added.
|
||||
+ `build`: Used for modifications to the project build system, such as changes to dependencies, external interfaces, or upgrading Node version.
|
||||
+ `ci`: Used for modifications to continuous integration processes, such as changes to Travis, Jenkins workflow configurations.
|
||||
+ `docs`: Used for modifications to documentation, such as changes to README files, API documentation, etc.
|
||||
+ `style`: Used for changes to code style, such as adjustments to indentation, spaces, blank lines, etc.
|
||||
+ `refactor`: Used for code refactoring, such as changes to code structure, variable names, function names, without altering functionality.
|
||||
+ `perf`: Used for performance optimization, such as improving code performance, reducing memory usage, etc.
|
||||
+ `test`: Used for modifications to test cases, such as adding, deleting, or modifying test cases for code.
|
||||
+ `chore`: Used for modifications to non-business-related code, such as changes to build processes or tool configurations.
|
||||
+ After `<type>`, specify the affected package name or scope in parentheses, for example, `(os/gtime)`.
|
||||
+ The part after the colon uses the verb tense + phrase that completes the blank in
|
||||
+ Lowercase verb after the colon
|
||||
|
||||
6
.github/workflows/ci-main.yml
vendored
6
.github/workflows/ci-main.yml
vendored
@ -239,15 +239,15 @@ jobs:
|
||||
export PATH="$PATH:$(go env GOPATH)/bin"
|
||||
|
||||
- name: Before Script
|
||||
run: bash .github/workflows/before_script.sh
|
||||
run: bash .github/workflows/scripts/before_script.sh
|
||||
|
||||
- name: Build & Test
|
||||
if: ${{ (github.event_name == 'push' && github.ref != 'refs/heads/master') || github.event_name == 'pull_request' }}
|
||||
run: bash .github/workflows/ci-main.sh
|
||||
run: bash .github/workflows/scripts/ci-main.sh
|
||||
|
||||
- name: Build & Test & Coverage
|
||||
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
|
||||
run: bash .github/workflows/ci-main.sh coverage
|
||||
run: bash .github/workflows/scripts/ci-main.sh coverage
|
||||
|
||||
- name: Stop Redis Cluster Containers
|
||||
run: docker compose -f ".github/workflows/redis/docker-compose.yml" down
|
||||
|
||||
4
.github/workflows/ci-sub.yml
vendored
4
.github/workflows/ci-sub.yml
vendored
@ -64,9 +64,9 @@ jobs:
|
||||
cache-dependency-path: '**/go.sum'
|
||||
|
||||
- name: Before Script
|
||||
run: bash .github/workflows/before_script.sh
|
||||
run: bash .github/workflows/scripts/before_script.sh
|
||||
|
||||
- name: Build & Test
|
||||
run: bash .github/workflows/ci-sub.sh
|
||||
run: bash .github/workflows/scripts/ci-sub.sh
|
||||
|
||||
|
||||
|
||||
38
.github/workflows/doc-build.yml
vendored
38
.github/workflows/doc-build.yml
vendored
@ -1,38 +0,0 @@
|
||||
name: Deploy to GitHub Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'doc-build'
|
||||
schedule:
|
||||
- cron: '0 15 * * *'
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: Deploy to GitHub Pages
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: doc-build
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
cache: npm
|
||||
- name: Set Up Golang Environment
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: 1.23.4
|
||||
cache: false
|
||||
- name: download goframe docs
|
||||
run: ./download.sh
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Build website
|
||||
run: npm run build
|
||||
- name: Deploy to GitHub Pages
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./build
|
||||
cname: pages.goframe.org
|
||||
@ -3,7 +3,10 @@
|
||||
coverage=$1
|
||||
|
||||
# update code of submodules
|
||||
make subup
|
||||
git clone https://github.com/gogf/examples
|
||||
|
||||
# update go.mod in examples directory to replace github.com/gogf/gf packages with local directory
|
||||
bash .github/workflows/scripts/replace_examples_gomod.sh
|
||||
|
||||
# find all path that contains go.mod.
|
||||
for file in `find . -name go.mod`; do
|
||||
@ -22,14 +25,14 @@ for file in `find . -name go.mod`; do
|
||||
fi
|
||||
|
||||
# Check if it's a contrib directory or examples directory
|
||||
if [[ $dirpath =~ "/contrib/" ]] || [ "examples" = $(basename $dirpath) ]; then
|
||||
if [[ $dirpath =~ "/contrib/" ]] || [[ $dirpath =~ "/examples/" ]]; then
|
||||
# Check if go version meets the requirement
|
||||
if ! go version | grep -qE "go${LATEST_GO_VERSION}"; then
|
||||
echo "ignore path $dirpath as go version is not ${LATEST_GO_VERSION}: $(go version)"
|
||||
continue 1
|
||||
fi
|
||||
# If it's examples directory, only build without tests
|
||||
if [ "examples" = $(basename $dirpath) ]; then
|
||||
if [[ $dirpath =~ "/examples/" ]]; then
|
||||
echo "the examples directory only needs to be built, not unit tests and coverage tests."
|
||||
cd $dirpath
|
||||
go mod tidy
|
||||
81
.github/workflows/scripts/replace_examples_gomod.sh
vendored
Executable file
81
.github/workflows/scripts/replace_examples_gomod.sh
vendored
Executable file
@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Get the absolute path to the repository root
|
||||
repo_root=$(pwd)
|
||||
workdir=$repo_root/examples
|
||||
|
||||
echo "Prepare to process go.mod files in the ${workdir} directory"
|
||||
|
||||
# Check if examples directory exists
|
||||
if [ ! -d "${workdir}" ]; then
|
||||
echo "Error: examples directory not found at ${workdir}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if find command is available
|
||||
if ! command -v find &> /dev/null; then
|
||||
echo "Error: find command not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for file in `find ${workdir} -name go.mod`; do
|
||||
goModPath=$(dirname $file)
|
||||
echo ""
|
||||
echo "Processing dir: $goModPath"
|
||||
|
||||
# Calculate relative path to root
|
||||
# First get the relative path from go.mod to repo root
|
||||
relativePath=""
|
||||
current="$goModPath"
|
||||
while [ "$current" != "$repo_root" ]; do
|
||||
relativePath="../$relativePath"
|
||||
current=$(dirname "$current")
|
||||
done
|
||||
relativePath=${relativePath%/} # Remove trailing slash
|
||||
echo "Relative path to root: $relativePath"
|
||||
|
||||
# Get all github.com/gogf/gf dependencies
|
||||
# Use awk to get package names without version numbers
|
||||
dependencies=$(awk '/^[[:space:]]*github\.com\/gogf\/gf\// {print $1}' "$file" | sort -u)
|
||||
|
||||
if [ -n "$dependencies" ]; then
|
||||
echo "Found GoFrame dependencies:"
|
||||
echo "$dependencies"
|
||||
echo "Adding replace directives..."
|
||||
|
||||
# Create temporary file
|
||||
temp_file="${file}.tmp"
|
||||
# Remove existing replace directives and copy to temp file
|
||||
sed '/^replace.*github\.com\/gogf\/gf.*/d' "$file" > "$temp_file"
|
||||
|
||||
# Add new replace block
|
||||
echo "" >> "$temp_file"
|
||||
echo "replace (" >> "$temp_file"
|
||||
|
||||
while IFS= read -r dep; do
|
||||
# Skip empty lines
|
||||
[ -z "$dep" ] && continue
|
||||
|
||||
# Calculate the relative path for the replacement
|
||||
if [[ "$dep" == "github.com/gogf/gf/v2" ]]; then
|
||||
replacement="$relativePath"
|
||||
else
|
||||
# Extract the path after v2 and remove trailing version
|
||||
subpath=$(echo "$dep" | sed -E 's/github\.com\/gogf\/gf\/(contrib\/[^/]+\/[^/]+)\/v2.*/\1/')
|
||||
replacement="$relativePath/$subpath"
|
||||
fi
|
||||
|
||||
echo " $dep => $replacement/" >> "$temp_file"
|
||||
done <<< "$dependencies"
|
||||
|
||||
echo ")" >> "$temp_file"
|
||||
|
||||
# Replace original file with temporary file
|
||||
mv "$temp_file" "$file"
|
||||
echo "Replace directives added to $file"
|
||||
else
|
||||
echo "No GoFrame dependencies found in $file"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "\nAll go.mod files have been processed successfully."
|
||||
33
.make_tidy.sh
Executable file
33
.make_tidy.sh
Executable file
@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
workdir=.
|
||||
echo "Prepare to tidy all go.mod files in the ${workdir} directory"
|
||||
|
||||
# check find command support or not
|
||||
output=$(find "${workdir}" -name go.mod 2>&1)
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Error: please use bash or zsh to run!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for file in `find ${workdir} -name go.mod`; do
|
||||
goModPath=$(dirname $file)
|
||||
echo ""
|
||||
echo "processing dir: $goModPath"
|
||||
|
||||
if [[ $goModPath =~ "/testdata/" ]]; then
|
||||
echo "ignore testdata path $goModPath"
|
||||
continue 1
|
||||
fi
|
||||
|
||||
if [[ $goModPath =~ "/examples/" ]]; then
|
||||
echo "ignore examples path $goModPath"
|
||||
continue 1
|
||||
fi
|
||||
|
||||
cd $goModPath
|
||||
go mod tidy
|
||||
# Remove toolchain line if exists
|
||||
sed -i '' '/^toolchain/d' go.mod
|
||||
cd - > /dev/null
|
||||
done
|
||||
@ -17,7 +17,7 @@ fi
|
||||
|
||||
workdir=.
|
||||
newVersion=$2
|
||||
echo "Prepare to replace the GF library version numbers in all go.mod files in the ${workdir} directory with ${newVersion}"
|
||||
echo "Prepare to replace the GoFrame library version numbers in all go.mod files in the ${workdir} directory with ${newVersion}"
|
||||
|
||||
# check find command support or not
|
||||
output=$(find "${workdir}" -name go.mod 2>&1)
|
||||
@ -49,6 +49,11 @@ for file in `find ${workdir} -name go.mod`; do
|
||||
continue 1
|
||||
fi
|
||||
|
||||
if [[ $goModPath =~ "/examples/" ]]; then
|
||||
echo "ignore examples path $goModPath"
|
||||
continue 1
|
||||
fi
|
||||
|
||||
cd $goModPath
|
||||
if [ $goModPath = "./cmd/gf" ]; then
|
||||
mv go.work go.work.version.bak
|
||||
@ -59,15 +64,18 @@ for file in `find ${workdir} -name go.mod`; do
|
||||
go mod edit -replace github.com/gogf/gf/contrib/drivers/oracle/v2=../../contrib/drivers/oracle
|
||||
go mod edit -replace github.com/gogf/gf/contrib/drivers/pgsql/v2=../../contrib/drivers/pgsql
|
||||
go mod edit -replace github.com/gogf/gf/contrib/drivers/sqlite/v2=../../contrib/drivers/sqlite
|
||||
# else
|
||||
# cd -
|
||||
# continue 1
|
||||
fi
|
||||
go mod tidy
|
||||
# Upgrading only GF related libraries, sometimes even if a version number is specified, it may not be possible to successfully upgrade. Please confirm before submitting the code
|
||||
# Remove toolchain line if exists
|
||||
sed -i '' '/^toolchain/d' go.mod
|
||||
|
||||
# Upgrading only GoFrame related libraries, sometimes even if a version number is specified,
|
||||
# it may not be possible to successfully upgrade. Please confirm before submitting the code
|
||||
go list -f "{{if and (not .Indirect) (not .Main)}}{{.Path}}@${newVersion}{{end}}" -m all | grep "^github.com/gogf/gf"
|
||||
go list -f "{{if and (not .Indirect) (not .Main)}}{{.Path}}@${newVersion}{{end}}" -m all | grep "^github.com/gogf/gf" | xargs -L1 go get -v
|
||||
go mod tidy
|
||||
# Remove toolchain line if exists
|
||||
sed -i '' '/^toolchain/d' go.mod
|
||||
if [ $goModPath = "./cmd/gf" ]; then
|
||||
go mod edit -dropreplace github.com/gogf/gf/v2
|
||||
go mod edit -dropreplace github.com/gogf/gf/contrib/drivers/clickhouse/v2
|
||||
19
Makefile
19
Makefile
@ -3,17 +3,7 @@ SHELL := /bin/bash
|
||||
# execute "go mod tidy" on all folders that have go.mod file
|
||||
.PHONY: tidy
|
||||
tidy:
|
||||
$(eval files=$(shell find . -name go.mod))
|
||||
@set -e; \
|
||||
for file in ${files}; do \
|
||||
goModPath=$$(dirname $$file); \
|
||||
if ! echo $$goModPath | grep -q "testdata"; then \
|
||||
echo "handle: $$goModPath"; \
|
||||
cd $$goModPath; \
|
||||
go mod tidy; \
|
||||
cd -; \
|
||||
fi \
|
||||
done
|
||||
./.make_tidy.sh
|
||||
|
||||
# execute "golangci-lint" to check code style
|
||||
.PHONY: lint
|
||||
@ -25,7 +15,7 @@ lint:
|
||||
version:
|
||||
@set -e; \
|
||||
newVersion=$(to); \
|
||||
./.set_version.sh ./ $$newVersion; \
|
||||
./.make_version.sh ./ $$newVersion; \
|
||||
echo "make version to=$(to) done"
|
||||
|
||||
|
||||
@ -33,10 +23,9 @@ version:
|
||||
.PHONY: subup
|
||||
subup:
|
||||
@set -e; \
|
||||
cd examples; \
|
||||
echo "Updating submodules..."; \
|
||||
git pull origin; \
|
||||
cd ..;
|
||||
git submodule init;\
|
||||
git submodule update;
|
||||
|
||||
# update and commit submodules
|
||||
.PHONY: subsync
|
||||
|
||||
@ -36,7 +36,7 @@ A powerful framework for faster, easier, and more efficient project development.
|
||||
💖 [Thanks to all the contributors who made GoFrame possible](https://github.com/gogf/gf/graphs/contributors) 💖
|
||||
|
||||
<a href="https://github.com/gogf/gf/graphs/contributors">
|
||||
<img src="https://goframe.org/img/contributors.svg?version=v2.8.3" alt="goframe contributors"/>
|
||||
<img src="https://goframe.org/img/contributors.svg?version=v2.9.0-beta" alt="goframe contributors"/>
|
||||
</a>
|
||||
|
||||
# License
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
module github.com/gogf/gf/cmd/gf/v2
|
||||
|
||||
go 1.20
|
||||
go 1.22
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.8.3
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.8.3
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.8.3
|
||||
github.com/gogf/gf/contrib/drivers/oracle/v2 v2.8.3
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.8.3
|
||||
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.9.0-beta
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.9.0-beta
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.9.0-beta
|
||||
github.com/gogf/gf/contrib/drivers/oracle/v2 v2.9.0-beta
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.9.0-beta
|
||||
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.9.0-beta
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
github.com/gogf/selfupdate v0.0.0-20231215043001-5c48c528462f
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/schollz/progressbar/v3 v3.15.0
|
||||
@ -48,10 +48,10 @@ require (
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/shopspring/decimal v1.3.1 // indirect
|
||||
github.com/sijms/go-ora/v2 v2.7.10 // indirect
|
||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.32.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.32.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
golang.org/x/sync v0.10.0 // indirect
|
||||
|
||||
@ -1,11 +1,17 @@
|
||||
aead.dev/minisign v0.2.0 h1:kAWrq/hBRu4AARY6AlciO83xhNnW9UaC8YipS2uhLPk=
|
||||
aead.dev/minisign v0.2.0/go.mod h1:zdq6LdSd9TbuSxchxwhpA9zEb9YXcVGoE8JakuiGaIQ=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 h1:lGlwhPtrX6EVml1hO0ivjkUxsSyl4dsiw9qcA1k/3IQ=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1 h1:sO0/P7g68FrryJzljemN+6GTssUXdANk6aJ7T1ZxnsQ=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.1/go.mod h1:h8hyGFDsU5HMivxiS2iYFZsgDbU9OnnJ163x5UGVKYo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1 h1:6oNBlSdi1QqM1PNW7FPA6xOGA5UNsXnkaYZz9vdPGhA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1 h1:DzHpqpoJVaCgOUdVHxE8QB52S6NiVdDQvGlny1qvPqA=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
|
||||
@ -40,24 +46,11 @@ github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.8.3 h1:b/AQMTxiHKPHsidEdk471AC5pkfoK88a5cPmKnzE53U=
|
||||
github.com/gogf/gf/contrib/drivers/clickhouse/v2 v2.8.3/go.mod h1:qYrF+x5urXLhce3pMcUAyccIsw3Oec0htynoDE4Boi4=
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.8.3 h1:F7Gt1y6YsYOIvgrUlRK07H29BL77dEgLPXilTqqVC80=
|
||||
github.com/gogf/gf/contrib/drivers/mssql/v2 v2.8.3/go.mod h1:K5prIMZwHANSZrqZbfm6PoEIMfLtd0PwR7u+hZD9HFs=
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.8.3 h1:RtoBg5HWACFrgIrFkpzH94kxSd5EWefNAq5k6olNY6c=
|
||||
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.8.3/go.mod h1:elZjckHRCejwml5Kdx2zfhOUDiAV3r5i4BgXcKAeH00=
|
||||
github.com/gogf/gf/contrib/drivers/oracle/v2 v2.8.3 h1:10/RCoWmvQ6PSm+leoS6CsKijH4dB38HOXLgP5+aScQ=
|
||||
github.com/gogf/gf/contrib/drivers/oracle/v2 v2.8.3/go.mod h1:XSaHf3/vTlzj/zioUbzKmaffPuoKvPV639fT91caheM=
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.8.3 h1:DvpoiVac1cwGVDTqC6wzFbDb+gXNzcceRgZUIcuTmaI=
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.8.3/go.mod h1:zugvYVb6c/X9rJ8Gb6b5WkMe+bFz2BsxQ5OLf4RSZos=
|
||||
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.8.3 h1:3pdibfm4UOiTGGh6UD8jfMyGZBGH9ikrrIMU8i/XANA=
|
||||
github.com/gogf/gf/contrib/drivers/sqlite/v2 v2.8.3/go.mod h1:G5rfcFkBhtmZT4+CU7A3fJH3sNmP4WRIaJ+4JFeVE08=
|
||||
github.com/gogf/gf/v2 v2.8.3 h1:h9Px3lqJnnH6It0AqHRz4/1hx0JmvaSf1IvUir5x1rA=
|
||||
github.com/gogf/gf/v2 v2.8.3/go.mod h1:n++xPYGUUMadw6IygLEgGZqc6y6DRLrJKg5kqCrPLWY=
|
||||
github.com/gogf/selfupdate v0.0.0-20231215043001-5c48c528462f h1:7xfXR/BhG3JDqO1s45n65Oyx9t4E/UqDOXep6jXdLCM=
|
||||
github.com/gogf/selfupdate v0.0.0-20231215043001-5c48c528462f/go.mod h1:HnYoio6S7VaFJdryKcD/r9HgX+4QzYfr00XiXUo/xz0=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
@ -66,7 +59,9 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
@ -80,7 +75,12 @@ github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhB
|
||||
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
@ -109,6 +109,7 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi
|
||||
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
|
||||
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
@ -117,6 +118,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/schollz/progressbar/v3 v3.15.0 h1:cNZmcNiVyea6oofBTg80ZhVXxf3wG/JoAhqCCwopkQo=
|
||||
github.com/schollz/progressbar/v3 v3.15.0/go.mod h1:ncBdc++eweU0dQoeZJ3loXoAc+bjaallHRIm8pVVeQM=
|
||||
github.com/shirou/gopsutil v2.19.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
@ -131,22 +134,23 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
|
||||
github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
go.opentelemetry.io/otel v1.7.0/go.mod h1:5BdUoMIz5WEs0vt0CUEMtSSaTSHBBVwrhnz7+nrD5xk=
|
||||
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
|
||||
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
|
||||
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
||||
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
||||
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
|
||||
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
|
||||
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
|
||||
go.opentelemetry.io/otel/trace v1.7.0/go.mod h1:fzLSB9nqR2eXzxPXb2JW9IKE+ScyXA48yyE4TNvoHqU=
|
||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
|
||||
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
@ -202,8 +206,9 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
go 1.20
|
||||
go 1.22
|
||||
|
||||
use (
|
||||
./
|
||||
|
||||
@ -36,7 +36,7 @@ type (
|
||||
Link string `name:"link" short:"l" brief:"{CGenDaoBriefLink}"`
|
||||
Tables string `name:"tables" short:"t" brief:"{CGenDaoBriefTables}"`
|
||||
TablesEx string `name:"tablesEx" short:"x" brief:"{CGenDaoBriefTablesEx}"`
|
||||
ShardingPattern []string `name:"ShardingPattern" short:"sp" brief:"{CGenDaoBriefShardingPattern}"`
|
||||
ShardingPattern []string `name:"shardingPattern" short:"sp" brief:"{CGenDaoBriefShardingPattern}"`
|
||||
Group string `name:"group" short:"g" brief:"{CGenDaoBriefGroup}" d:"default"`
|
||||
Prefix string `name:"prefix" short:"f" brief:"{CGenDaoBriefPrefix}"`
|
||||
RemovePrefix string `name:"removePrefix" short:"r" brief:"{CGenDaoBriefRemovePrefix}"`
|
||||
|
||||
@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
// Val returns the current value of `v`.
|
||||
func (v *Var) Val() interface{} {
|
||||
func (v *Var) Val() any {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
@ -25,7 +25,7 @@ func (v *Var) Val() interface{} {
|
||||
}
|
||||
|
||||
// Interface is alias of Val.
|
||||
func (v *Var) Interface() interface{} {
|
||||
func (v *Var) Interface() any {
|
||||
return v.Val()
|
||||
}
|
||||
|
||||
|
||||
@ -2,11 +2,9 @@ module github.com/gogf/gf/contrib/config/apollo/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
github.com/apolloconfig/agollo/v4 v4.3.1
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/config/consul/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
github.com/hashicorp/consul/api v1.24.0
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2
|
||||
)
|
||||
|
||||
@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/config/kubecm/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
k8s.io/api v0.27.4
|
||||
k8s.io/apimachinery v0.27.4
|
||||
k8s.io/client-go v0.27.4
|
||||
|
||||
@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/config/nacos/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
github.com/nacos-group/nacos-sdk-go/v2 v2.2.5
|
||||
)
|
||||
|
||||
|
||||
@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/config/polaris/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
github.com/polarismesh/polaris-go v1.5.8
|
||||
)
|
||||
|
||||
|
||||
@ -2,11 +2,9 @@ module github.com/gogf/gf/contrib/drivers/clickhouse/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
github.com/ClickHouse/clickhouse-go/v2 v2.0.15
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/shopspring/decimal v1.3.1
|
||||
)
|
||||
|
||||
@ -2,13 +2,11 @@ module github.com/gogf/gf/contrib/drivers/dm/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
replace github.com/gogf/gf/v2 => ../../../
|
||||
|
||||
require (
|
||||
gitee.com/chunanyong/dm v1.8.12
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/drivers/mssql/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
github.com/microsoft/go-mssqldb v1.7.1
|
||||
)
|
||||
|
||||
|
||||
@ -2,11 +2,9 @@ module github.com/gogf/gf/contrib/drivers/mysql/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
github.com/go-sql-driver/mysql v1.7.1
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@ -758,7 +758,7 @@ func Test_Model_Value_WithCache(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
t.Assert(value.Int(), 0)
|
||||
})
|
||||
return
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).Data(g.MapStrAny{
|
||||
"id": 1,
|
||||
|
||||
@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/drivers/oracle/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
github.com/sijms/go-ora/v2 v2.7.10
|
||||
)
|
||||
|
||||
|
||||
@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/drivers/pgsql/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
github.com/lib/pq v1.10.9
|
||||
)
|
||||
|
||||
|
||||
@ -2,11 +2,9 @@ module github.com/gogf/gf/contrib/drivers/sqlite/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
github.com/glebarez/go-sqlite v1.21.2
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@ -17,6 +17,7 @@ import (
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
@ -3988,14 +3989,14 @@ func Test_ScanList_NoRecreate_PtrAttribute(t *testing.T) {
|
||||
)
|
||||
r1 := gdb.Result{
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(1),
|
||||
"name": gdb.NewValue("john"),
|
||||
"age": gdb.NewValue(16),
|
||||
"id": gvar.New(1),
|
||||
"name": gvar.New("john"),
|
||||
"age": gvar.New(16),
|
||||
},
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(2),
|
||||
"name": gdb.NewValue("smith"),
|
||||
"age": gdb.NewValue(18),
|
||||
"id": gvar.New(2),
|
||||
"name": gvar.New("smith"),
|
||||
"age": gvar.New(18),
|
||||
},
|
||||
}
|
||||
err = r1.ScanList(&s, "One")
|
||||
@ -4008,12 +4009,12 @@ func Test_ScanList_NoRecreate_PtrAttribute(t *testing.T) {
|
||||
|
||||
r2 := gdb.Result{
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(1),
|
||||
"age": gdb.NewValue(20),
|
||||
"id": gvar.New(1),
|
||||
"age": gvar.New(20),
|
||||
},
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(2),
|
||||
"age": gdb.NewValue(21),
|
||||
"id": gvar.New(2),
|
||||
"age": gvar.New(21),
|
||||
},
|
||||
}
|
||||
err = r2.ScanList(&s, "One", "One", "id:Id")
|
||||
@ -4044,14 +4045,14 @@ func Test_ScanList_NoRecreate_StructAttribute(t *testing.T) {
|
||||
)
|
||||
r1 := gdb.Result{
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(1),
|
||||
"name": gdb.NewValue("john"),
|
||||
"age": gdb.NewValue(16),
|
||||
"id": gvar.New(1),
|
||||
"name": gvar.New("john"),
|
||||
"age": gvar.New(16),
|
||||
},
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(2),
|
||||
"name": gdb.NewValue("smith"),
|
||||
"age": gdb.NewValue(18),
|
||||
"id": gvar.New(2),
|
||||
"name": gvar.New("smith"),
|
||||
"age": gvar.New(18),
|
||||
},
|
||||
}
|
||||
err = r1.ScanList(&s, "One")
|
||||
@ -4064,12 +4065,12 @@ func Test_ScanList_NoRecreate_StructAttribute(t *testing.T) {
|
||||
|
||||
r2 := gdb.Result{
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(1),
|
||||
"age": gdb.NewValue(20),
|
||||
"id": gvar.New(1),
|
||||
"age": gvar.New(20),
|
||||
},
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(2),
|
||||
"age": gdb.NewValue(21),
|
||||
"id": gvar.New(2),
|
||||
"age": gvar.New(21),
|
||||
},
|
||||
}
|
||||
err = r2.ScanList(&s, "One", "One", "id:Id")
|
||||
@ -4108,14 +4109,14 @@ func Test_ScanList_NoRecreate_SliceAttribute_Ptr(t *testing.T) {
|
||||
)
|
||||
r1 := gdb.Result{
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(1),
|
||||
"name": gdb.NewValue("john"),
|
||||
"age": gdb.NewValue(16),
|
||||
"id": gvar.New(1),
|
||||
"name": gvar.New("john"),
|
||||
"age": gvar.New(16),
|
||||
},
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(2),
|
||||
"name": gdb.NewValue("smith"),
|
||||
"age": gdb.NewValue(18),
|
||||
"id": gvar.New(2),
|
||||
"name": gvar.New("smith"),
|
||||
"age": gvar.New(18),
|
||||
},
|
||||
}
|
||||
err = r1.ScanList(&s, "One")
|
||||
@ -4128,16 +4129,16 @@ func Test_ScanList_NoRecreate_SliceAttribute_Ptr(t *testing.T) {
|
||||
|
||||
r2 := gdb.Result{
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(100),
|
||||
"pid": gdb.NewValue(1),
|
||||
"age": gdb.NewValue(30),
|
||||
"name": gdb.NewValue("john"),
|
||||
"id": gvar.New(100),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(30),
|
||||
"name": gvar.New("john"),
|
||||
},
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(200),
|
||||
"pid": gdb.NewValue(1),
|
||||
"age": gdb.NewValue(31),
|
||||
"name": gdb.NewValue("smith"),
|
||||
"id": gvar.New(200),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(31),
|
||||
"name": gvar.New("smith"),
|
||||
},
|
||||
}
|
||||
err = r2.ScanList(&s, "Many", "One", "pid:Id")
|
||||
@ -4158,14 +4159,14 @@ func Test_ScanList_NoRecreate_SliceAttribute_Ptr(t *testing.T) {
|
||||
|
||||
r3 := gdb.Result{
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(100),
|
||||
"pid": gdb.NewValue(1),
|
||||
"age": gdb.NewValue(40),
|
||||
"id": gvar.New(100),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(40),
|
||||
},
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(200),
|
||||
"pid": gdb.NewValue(1),
|
||||
"age": gdb.NewValue(41),
|
||||
"id": gvar.New(200),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(41),
|
||||
},
|
||||
}
|
||||
err = r3.ScanList(&s, "Many", "One", "pid:Id")
|
||||
@ -4212,14 +4213,14 @@ func Test_ScanList_NoRecreate_SliceAttribute_Struct(t *testing.T) {
|
||||
)
|
||||
r1 := gdb.Result{
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(1),
|
||||
"name": gdb.NewValue("john"),
|
||||
"age": gdb.NewValue(16),
|
||||
"id": gvar.New(1),
|
||||
"name": gvar.New("john"),
|
||||
"age": gvar.New(16),
|
||||
},
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(2),
|
||||
"name": gdb.NewValue("smith"),
|
||||
"age": gdb.NewValue(18),
|
||||
"id": gvar.New(2),
|
||||
"name": gvar.New("smith"),
|
||||
"age": gvar.New(18),
|
||||
},
|
||||
}
|
||||
err = r1.ScanList(&s, "One")
|
||||
@ -4232,16 +4233,16 @@ func Test_ScanList_NoRecreate_SliceAttribute_Struct(t *testing.T) {
|
||||
|
||||
r2 := gdb.Result{
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(100),
|
||||
"pid": gdb.NewValue(1),
|
||||
"age": gdb.NewValue(30),
|
||||
"name": gdb.NewValue("john"),
|
||||
"id": gvar.New(100),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(30),
|
||||
"name": gvar.New("john"),
|
||||
},
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(200),
|
||||
"pid": gdb.NewValue(1),
|
||||
"age": gdb.NewValue(31),
|
||||
"name": gdb.NewValue("smith"),
|
||||
"id": gvar.New(200),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(31),
|
||||
"name": gvar.New("smith"),
|
||||
},
|
||||
}
|
||||
err = r2.ScanList(&s, "Many", "One", "pid:Id")
|
||||
@ -4262,14 +4263,14 @@ func Test_ScanList_NoRecreate_SliceAttribute_Struct(t *testing.T) {
|
||||
|
||||
r3 := gdb.Result{
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(100),
|
||||
"pid": gdb.NewValue(1),
|
||||
"age": gdb.NewValue(40),
|
||||
"id": gvar.New(100),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(40),
|
||||
},
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(200),
|
||||
"pid": gdb.NewValue(1),
|
||||
"age": gdb.NewValue(41),
|
||||
"id": gvar.New(200),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(41),
|
||||
},
|
||||
}
|
||||
err = r3.ScanList(&s, "Many", "One", "pid:Id")
|
||||
@ -4300,8 +4301,8 @@ func TestResult_Structs1(t *testing.T) {
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
r := gdb.Result{
|
||||
gdb.Record{"id": gdb.NewValue(nil), "name": gdb.NewValue("john")},
|
||||
gdb.Record{"id": gdb.NewValue(nil), "name": gdb.NewValue("smith")},
|
||||
gdb.Record{"id": gvar.New(nil), "name": gvar.New("john")},
|
||||
gdb.Record{"id": gvar.New(nil), "name": gvar.New("smith")},
|
||||
}
|
||||
array := make([]*B, 2)
|
||||
err := r.Structs(&array)
|
||||
|
||||
@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/drivers/sqlitecgo/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
github.com/mattn/go-sqlite3 v1.14.17
|
||||
)
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ import (
|
||||
|
||||
"github.com/gogf/gf/v2/container/garray"
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/encoding/gjson"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
@ -4036,14 +4037,14 @@ func Test_ScanList_NoRecreate_PtrAttribute(t *testing.T) {
|
||||
)
|
||||
r1 := gdb.Result{
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(1),
|
||||
"name": gdb.NewValue("john"),
|
||||
"age": gdb.NewValue(16),
|
||||
"id": gvar.New(1),
|
||||
"name": gvar.New("john"),
|
||||
"age": gvar.New(16),
|
||||
},
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(2),
|
||||
"name": gdb.NewValue("smith"),
|
||||
"age": gdb.NewValue(18),
|
||||
"id": gvar.New(2),
|
||||
"name": gvar.New("smith"),
|
||||
"age": gvar.New(18),
|
||||
},
|
||||
}
|
||||
err = r1.ScanList(&s, "One")
|
||||
@ -4056,12 +4057,12 @@ func Test_ScanList_NoRecreate_PtrAttribute(t *testing.T) {
|
||||
|
||||
r2 := gdb.Result{
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(1),
|
||||
"age": gdb.NewValue(20),
|
||||
"id": gvar.New(1),
|
||||
"age": gvar.New(20),
|
||||
},
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(2),
|
||||
"age": gdb.NewValue(21),
|
||||
"id": gvar.New(2),
|
||||
"age": gvar.New(21),
|
||||
},
|
||||
}
|
||||
err = r2.ScanList(&s, "One", "One", "id:Id")
|
||||
@ -4092,14 +4093,14 @@ func Test_ScanList_NoRecreate_StructAttribute(t *testing.T) {
|
||||
)
|
||||
r1 := gdb.Result{
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(1),
|
||||
"name": gdb.NewValue("john"),
|
||||
"age": gdb.NewValue(16),
|
||||
"id": gvar.New(1),
|
||||
"name": gvar.New("john"),
|
||||
"age": gvar.New(16),
|
||||
},
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(2),
|
||||
"name": gdb.NewValue("smith"),
|
||||
"age": gdb.NewValue(18),
|
||||
"id": gvar.New(2),
|
||||
"name": gvar.New("smith"),
|
||||
"age": gvar.New(18),
|
||||
},
|
||||
}
|
||||
err = r1.ScanList(&s, "One")
|
||||
@ -4112,12 +4113,12 @@ func Test_ScanList_NoRecreate_StructAttribute(t *testing.T) {
|
||||
|
||||
r2 := gdb.Result{
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(1),
|
||||
"age": gdb.NewValue(20),
|
||||
"id": gvar.New(1),
|
||||
"age": gvar.New(20),
|
||||
},
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(2),
|
||||
"age": gdb.NewValue(21),
|
||||
"id": gvar.New(2),
|
||||
"age": gvar.New(21),
|
||||
},
|
||||
}
|
||||
err = r2.ScanList(&s, "One", "One", "id:Id")
|
||||
@ -4156,14 +4157,14 @@ func Test_ScanList_NoRecreate_SliceAttribute_Ptr(t *testing.T) {
|
||||
)
|
||||
r1 := gdb.Result{
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(1),
|
||||
"name": gdb.NewValue("john"),
|
||||
"age": gdb.NewValue(16),
|
||||
"id": gvar.New(1),
|
||||
"name": gvar.New("john"),
|
||||
"age": gvar.New(16),
|
||||
},
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(2),
|
||||
"name": gdb.NewValue("smith"),
|
||||
"age": gdb.NewValue(18),
|
||||
"id": gvar.New(2),
|
||||
"name": gvar.New("smith"),
|
||||
"age": gvar.New(18),
|
||||
},
|
||||
}
|
||||
err = r1.ScanList(&s, "One")
|
||||
@ -4176,16 +4177,16 @@ func Test_ScanList_NoRecreate_SliceAttribute_Ptr(t *testing.T) {
|
||||
|
||||
r2 := gdb.Result{
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(100),
|
||||
"pid": gdb.NewValue(1),
|
||||
"age": gdb.NewValue(30),
|
||||
"name": gdb.NewValue("john"),
|
||||
"id": gvar.New(100),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(30),
|
||||
"name": gvar.New("john"),
|
||||
},
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(200),
|
||||
"pid": gdb.NewValue(1),
|
||||
"age": gdb.NewValue(31),
|
||||
"name": gdb.NewValue("smith"),
|
||||
"id": gvar.New(200),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(31),
|
||||
"name": gvar.New("smith"),
|
||||
},
|
||||
}
|
||||
err = r2.ScanList(&s, "Many", "One", "pid:Id")
|
||||
@ -4206,14 +4207,14 @@ func Test_ScanList_NoRecreate_SliceAttribute_Ptr(t *testing.T) {
|
||||
|
||||
r3 := gdb.Result{
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(100),
|
||||
"pid": gdb.NewValue(1),
|
||||
"age": gdb.NewValue(40),
|
||||
"id": gvar.New(100),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(40),
|
||||
},
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(200),
|
||||
"pid": gdb.NewValue(1),
|
||||
"age": gdb.NewValue(41),
|
||||
"id": gvar.New(200),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(41),
|
||||
},
|
||||
}
|
||||
err = r3.ScanList(&s, "Many", "One", "pid:Id")
|
||||
@ -4260,14 +4261,14 @@ func Test_ScanList_NoRecreate_SliceAttribute_Struct(t *testing.T) {
|
||||
)
|
||||
r1 := gdb.Result{
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(1),
|
||||
"name": gdb.NewValue("john"),
|
||||
"age": gdb.NewValue(16),
|
||||
"id": gvar.New(1),
|
||||
"name": gvar.New("john"),
|
||||
"age": gvar.New(16),
|
||||
},
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(2),
|
||||
"name": gdb.NewValue("smith"),
|
||||
"age": gdb.NewValue(18),
|
||||
"id": gvar.New(2),
|
||||
"name": gvar.New("smith"),
|
||||
"age": gvar.New(18),
|
||||
},
|
||||
}
|
||||
err = r1.ScanList(&s, "One")
|
||||
@ -4280,16 +4281,16 @@ func Test_ScanList_NoRecreate_SliceAttribute_Struct(t *testing.T) {
|
||||
|
||||
r2 := gdb.Result{
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(100),
|
||||
"pid": gdb.NewValue(1),
|
||||
"age": gdb.NewValue(30),
|
||||
"name": gdb.NewValue("john"),
|
||||
"id": gvar.New(100),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(30),
|
||||
"name": gvar.New("john"),
|
||||
},
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(200),
|
||||
"pid": gdb.NewValue(1),
|
||||
"age": gdb.NewValue(31),
|
||||
"name": gdb.NewValue("smith"),
|
||||
"id": gvar.New(200),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(31),
|
||||
"name": gvar.New("smith"),
|
||||
},
|
||||
}
|
||||
err = r2.ScanList(&s, "Many", "One", "pid:Id")
|
||||
@ -4310,14 +4311,14 @@ func Test_ScanList_NoRecreate_SliceAttribute_Struct(t *testing.T) {
|
||||
|
||||
r3 := gdb.Result{
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(100),
|
||||
"pid": gdb.NewValue(1),
|
||||
"age": gdb.NewValue(40),
|
||||
"id": gvar.New(100),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(40),
|
||||
},
|
||||
gdb.Record{
|
||||
"id": gdb.NewValue(200),
|
||||
"pid": gdb.NewValue(1),
|
||||
"age": gdb.NewValue(41),
|
||||
"id": gvar.New(200),
|
||||
"pid": gvar.New(1),
|
||||
"age": gvar.New(41),
|
||||
},
|
||||
}
|
||||
err = r3.ScanList(&s, "Many", "One", "pid:Id")
|
||||
@ -4348,8 +4349,8 @@ func TestResult_Structs1(t *testing.T) {
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
r := gdb.Result{
|
||||
gdb.Record{"id": gdb.NewValue(nil), "name": gdb.NewValue("john")},
|
||||
gdb.Record{"id": gdb.NewValue(nil), "name": gdb.NewValue("smith")},
|
||||
gdb.Record{"id": gvar.New(nil), "name": gvar.New("john")},
|
||||
gdb.Record{"id": gvar.New(nil), "name": gvar.New("smith")},
|
||||
}
|
||||
array := make([]*B, 2)
|
||||
err := r.Structs(&array)
|
||||
|
||||
@ -3,7 +3,7 @@ module github.com/gogf/gf/contrib/metric/otelmetric/v2
|
||||
go 1.22
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
github.com/prometheus/client_golang v1.19.1
|
||||
go.opentelemetry.io/contrib/instrumentation/runtime v0.49.0
|
||||
go.opentelemetry.io/otel v1.32.0
|
||||
|
||||
@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/nosql/redis/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
github.com/redis/go-redis/v9 v9.7.0
|
||||
go.opentelemetry.io/otel v1.32.0
|
||||
go.opentelemetry.io/otel/trace v1.32.0
|
||||
|
||||
@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/registry/consul/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
github.com/hashicorp/consul/api v1.26.1
|
||||
)
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ module github.com/gogf/gf/contrib/registry/etcd/v2
|
||||
go 1.22
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
go.etcd.io/etcd/client/v3 v3.5.17
|
||||
google.golang.org/grpc v1.59.0
|
||||
)
|
||||
|
||||
@ -45,6 +45,7 @@ func Test_HTTP_Registry(t *testing.T) {
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetDiscovery(gsvc.GetRegistry())
|
||||
client.SetPrefix(fmt.Sprintf("http://%s", svcName))
|
||||
// GET
|
||||
t.Assert(client.GetContent(ctx, "/http-registry"), svcName)
|
||||
@ -71,6 +72,7 @@ func Test_HTTP_Discovery_Disable(t *testing.T) {
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetDiscovery(gsvc.GetRegistry())
|
||||
client.SetPrefix(fmt.Sprintf("http://%s", svcName))
|
||||
result, err := client.Get(ctx, "/http-registry")
|
||||
defer result.Close()
|
||||
|
||||
@ -2,9 +2,7 @@ module github.com/gogf/gf/contrib/registry/file/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require github.com/gogf/gf/v2 v2.8.3
|
||||
require github.com/gogf/gf/v2 v2.9.0-beta
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
||||
|
||||
@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/registry/nacos/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
github.com/nacos-group/nacos-sdk-go/v2 v2.2.7
|
||||
)
|
||||
|
||||
|
||||
@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/registry/polaris/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
github.com/polarismesh/polaris-go v1.5.8
|
||||
)
|
||||
|
||||
|
||||
@ -2,11 +2,9 @@ module github.com/gogf/gf/contrib/registry/zookeeper/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
github.com/go-zookeeper/zk v1.0.3
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
golang.org/x/sync v0.10.0
|
||||
)
|
||||
|
||||
|
||||
@ -2,11 +2,9 @@ module github.com/gogf/gf/contrib/rpc/grpcx/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/contrib/registry/file/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/contrib/registry/file/v2 v2.9.0-beta
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
go.opentelemetry.io/otel v1.32.0
|
||||
go.opentelemetry.io/otel/trace v1.32.0
|
||||
google.golang.org/grpc v1.64.1
|
||||
|
||||
@ -2,9 +2,7 @@ module github.com/gogf/gf/contrib/sdk/httpclient/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require github.com/gogf/gf/v2 v2.8.3
|
||||
require github.com/gogf/gf/v2 v2.9.0-beta
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
||||
|
||||
@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/trace/otlpgrpc/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
go.opentelemetry.io/otel v1.32.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.24.0
|
||||
|
||||
@ -2,10 +2,8 @@ module github.com/gogf/gf/contrib/trace/otlphttp/v2
|
||||
|
||||
go 1.22
|
||||
|
||||
toolchain go1.22.0
|
||||
|
||||
require (
|
||||
github.com/gogf/gf/v2 v2.8.3
|
||||
github.com/gogf/gf/v2 v2.9.0-beta
|
||||
go.opentelemetry.io/otel v1.32.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.24.0
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.24.0
|
||||
|
||||
82
database/gdb/gdb_converter.go
Normal file
82
database/gdb/gdb_converter.go
Normal file
@ -0,0 +1,82 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// iVal is used for type assert api for Val().
|
||||
type iVal interface {
|
||||
Val() any
|
||||
}
|
||||
|
||||
var (
|
||||
// converter is the internal type converter for gdb.
|
||||
converter = gconv.NewConverter()
|
||||
)
|
||||
|
||||
func init() {
|
||||
converter.RegisterAnyConverterFunc(
|
||||
sliceTypeConverterFunc,
|
||||
reflect.TypeOf([]string{}),
|
||||
reflect.TypeOf([]float32{}),
|
||||
reflect.TypeOf([]float64{}),
|
||||
reflect.TypeOf([]int{}),
|
||||
reflect.TypeOf([]int32{}),
|
||||
reflect.TypeOf([]int64{}),
|
||||
reflect.TypeOf([]uint{}),
|
||||
reflect.TypeOf([]uint32{}),
|
||||
reflect.TypeOf([]uint64{}),
|
||||
)
|
||||
}
|
||||
|
||||
// GetConverter returns the internal type converter for gdb.
|
||||
func GetConverter() gconv.Converter {
|
||||
return converter
|
||||
}
|
||||
|
||||
func sliceTypeConverterFunc(from any, to reflect.Value) (err error) {
|
||||
v, ok := from.(iVal)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
fromVal := v.Val()
|
||||
switch x := fromVal.(type) {
|
||||
case []byte:
|
||||
dst := to.Addr().Interface()
|
||||
err = json.Unmarshal(x, dst)
|
||||
case string:
|
||||
dst := to.Addr().Interface()
|
||||
err = json.Unmarshal([]byte(x), dst)
|
||||
default:
|
||||
fromType := reflect.TypeOf(fromVal)
|
||||
switch fromType.Kind() {
|
||||
case reflect.Slice:
|
||||
convertOption := gconv.ConvertOption{
|
||||
SliceOption: gconv.SliceOption{ContinueOnError: true},
|
||||
MapOption: gconv.MapOption{ContinueOnError: true},
|
||||
StructOption: gconv.StructOption{ContinueOnError: true},
|
||||
}
|
||||
dv, err := converter.ConvertWithTypeName(fromVal, to.Type().String(), convertOption)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
to.Set(reflect.ValueOf(dv))
|
||||
default:
|
||||
err = gerror.Newf(
|
||||
`unsupported type converting from type "%T" to type "%T"`,
|
||||
fromVal, to,
|
||||
)
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -225,7 +225,6 @@ func (c *Core) GetScan(ctx context.Context, pointer interface{}, sql string, arg
|
||||
return c.db.GetCore().doGetStruct(ctx, pointer, sql, args...)
|
||||
|
||||
default:
|
||||
|
||||
}
|
||||
return gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
|
||||
@ -496,22 +496,6 @@ func (c *Core) RowsToResult(ctx context.Context, rows *sql.Rows) (Result, error)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (c *Core) getLocalTypeForFieldWithCache(
|
||||
ctx context.Context, fieldType string, fieldValue any,
|
||||
) (localType LocalType, err error) {
|
||||
v := c.localTypeMap.GetOrSetFuncLock(fieldType, func() any {
|
||||
localType, err = c.db.CheckLocalTypeForField(ctx, fieldType, fieldValue)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return localType
|
||||
})
|
||||
if err != nil {
|
||||
return LocalTypeUndefined, err
|
||||
}
|
||||
return v.(LocalType), nil
|
||||
}
|
||||
|
||||
// OrderRandomFunction returns the SQL function for random ordering.
|
||||
func (c *Core) OrderRandomFunction() string {
|
||||
return "RAND()"
|
||||
|
||||
@ -53,7 +53,10 @@ func (r Record) Struct(pointer interface{}) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return gconv.StructTag(r, pointer, OrmTagForStruct)
|
||||
return converter.Struct(r, pointer, gconv.StructOption{
|
||||
PriorityTag: OrmTagForStruct,
|
||||
ContinueOnError: true,
|
||||
})
|
||||
}
|
||||
|
||||
// IsEmpty checks and returns whether `r` is empty.
|
||||
|
||||
@ -200,5 +200,12 @@ func (r Result) Structs(pointer interface{}) (err error) {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return gconv.StructsTag(r, pointer, OrmTagForStruct)
|
||||
var (
|
||||
sliceOption = gconv.SliceOption{ContinueOnError: true}
|
||||
mapOption = gconv.StructOption{
|
||||
PriorityTag: OrmTagForStruct,
|
||||
ContinueOnError: true,
|
||||
}
|
||||
)
|
||||
return converter.Structs(r, pointer, sliceOption, mapOption)
|
||||
}
|
||||
|
||||
@ -14,6 +14,15 @@ import (
|
||||
"github.com/gogf/gf/v2/text/gregex"
|
||||
)
|
||||
|
||||
func Test_GetConverter(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
c := GetConverter()
|
||||
s, err := c.String(1)
|
||||
t.AssertNil(err)
|
||||
t.AssertEQ(s, "1")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_HookSelect_Regex(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
|
||||
2
examples
2
examples
Submodule examples updated: fbf44fb499...bf0ab5ac16
@ -72,7 +72,7 @@ func New() *Client {
|
||||
header: make(map[string]string),
|
||||
cookies: make(map[string]string),
|
||||
builder: gsel.GetBuilder(),
|
||||
discovery: gsvc.GetRegistry(),
|
||||
discovery: nil,
|
||||
}
|
||||
c.header[httpHeaderUserAgent] = defaultClientAgent
|
||||
// It enables OpenTelemetry for client in default.
|
||||
|
||||
@ -16,7 +16,6 @@ import (
|
||||
"github.com/gogf/gf/v2/internal/intlog"
|
||||
"github.com/gogf/gf/v2/net/gsel"
|
||||
"github.com/gogf/gf/v2/net/gsvc"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
type discoveryNode struct {
|
||||
@ -39,7 +38,7 @@ var clientSelectorMap = gmap.New(true)
|
||||
|
||||
// internalMiddlewareDiscovery is a client middleware that enables service discovery feature for client.
|
||||
func internalMiddlewareDiscovery(c *Client, r *http.Request) (response *Response, err error) {
|
||||
if c.discovery == nil && !isServiceName(r.URL.Host) {
|
||||
if c.discovery == nil {
|
||||
return c.Next(r)
|
||||
}
|
||||
var (
|
||||
@ -107,11 +106,3 @@ func updateSelectorNodesByService(ctx context.Context, selector gsel.Selector, s
|
||||
}
|
||||
return selector.Update(ctx, nodes)
|
||||
}
|
||||
|
||||
// isServiceName checks and returns whether given input parameter is service name or not.
|
||||
// It checks by whether the parameter is address by containing port delimiter character ':'.
|
||||
//
|
||||
// It does not contain any port number if using service discovery.
|
||||
func isServiceName(serviceNameOrAddress string) bool {
|
||||
return !gstr.Contains(serviceNameOrAddress, gsvc.EndpointHostPortDelimiter)
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ type Request struct {
|
||||
// =================================================================================================================
|
||||
|
||||
handlers []*HandlerItemParsed // All matched handlers containing handler, hook and middleware for this request.
|
||||
serveHandler *HandlerItemParsed // Real handler serving for this request, not hook or middleware.
|
||||
serveHandler *HandlerItemParsed // Real business handler serving for this request, not hook or middleware handler.
|
||||
handlerResponse interface{} // Handler response object for Request/Response handler.
|
||||
hasHookHandler bool // A bool marking whether there's hook handler in the handlers for performance purpose.
|
||||
hasServeHandler bool // A bool marking whether there's serving handler in the handlers for performance purpose.
|
||||
@ -278,13 +278,3 @@ func (r *Request) ReloadParam() {
|
||||
r.parsedQuery = false
|
||||
r.bodyContent = nil
|
||||
}
|
||||
|
||||
// GetHandlerResponse retrieves and returns the handler response object and its error.
|
||||
func (r *Request) GetHandlerResponse() interface{} {
|
||||
return r.handlerResponse
|
||||
}
|
||||
|
||||
// GetServeHandler retrieves and returns the user defined handler used to serve this request.
|
||||
func (r *Request) GetServeHandler() *HandlerItemParsed {
|
||||
return r.serveHandler
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"mime/multipart"
|
||||
"net/http"
|
||||
"reflect"
|
||||
@ -107,7 +108,6 @@ func (r *Request) doParse(pointer interface{}, requestType int) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// TODO: https://github.com/gogf/gf/pull/2450
|
||||
// Validation.
|
||||
if err = gvalid.New().
|
||||
Bail().
|
||||
@ -234,22 +234,46 @@ func (r *Request) parseBody() {
|
||||
if r.ContentLength == 0 {
|
||||
return
|
||||
}
|
||||
// If it's a multipart request, it does not parse the body content.
|
||||
contentType := r.Header.Get("Content-Type")
|
||||
if gstr.Contains(contentType, "multipart/") {
|
||||
return
|
||||
}
|
||||
|
||||
// Skip binary content types, which should not be parsed.
|
||||
if r.isBinaryContentType(contentType) {
|
||||
return
|
||||
}
|
||||
|
||||
if body := r.GetBody(); len(body) > 0 {
|
||||
// Trim space/new line characters.
|
||||
body = bytes.TrimSpace(body)
|
||||
// JSON format checks.
|
||||
if body[0] == '{' && body[len(body)-1] == '}' {
|
||||
|
||||
// json/xml content type checks.
|
||||
if gstr.Contains(contentType, "/json") {
|
||||
_ = json.UnmarshalUseNumber(body, &r.bodyMap)
|
||||
return
|
||||
}
|
||||
// XML format checks.
|
||||
if len(body) > 5 && bytes.EqualFold(body[:5], xmlHeaderBytes) {
|
||||
if gstr.Contains(contentType, "/xml") {
|
||||
r.bodyMap, _ = gxml.DecodeWithoutRoot(body)
|
||||
return
|
||||
}
|
||||
if body[0] == '<' && body[len(body)-1] == '>' {
|
||||
r.bodyMap, _ = gxml.DecodeWithoutRoot(body)
|
||||
// Auto decoding body content.
|
||||
if r.Server.config.AutoDecodingBody {
|
||||
// JSON format checks.
|
||||
if body[0] == '{' && body[len(body)-1] == '}' {
|
||||
_ = json.UnmarshalUseNumber(body, &r.bodyMap)
|
||||
}
|
||||
// XML format checks.
|
||||
if len(body) > 5 && bytes.EqualFold(body[:5], xmlHeaderBytes) {
|
||||
r.bodyMap, _ = gxml.DecodeWithoutRoot(body)
|
||||
}
|
||||
if body[0] == '<' && body[len(body)-1] == '>' {
|
||||
r.bodyMap, _ = gxml.DecodeWithoutRoot(body)
|
||||
}
|
||||
}
|
||||
// Default parameters decoding.
|
||||
if contentType := r.Header.Get("Content-Type"); (contentType == "" || !gstr.Contains(contentType, "multipart/")) && r.bodyMap == nil {
|
||||
if r.bodyMap == nil {
|
||||
r.bodyMap, _ = gstr.Parse(r.GetBodyString())
|
||||
}
|
||||
}
|
||||
@ -384,3 +408,42 @@ func (r *Request) GetMultipartFiles(name string) []*multipart.FileHeader {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// isBinaryContentType check the content type is binary or not.
|
||||
func (r *Request) isBinaryContentType(contentType string) bool {
|
||||
// parseMediaType
|
||||
mimeType, _, err := mime.ParseMediaType(contentType)
|
||||
// If the content type is invalid, it's treated as binary.
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// Lowercase the MIME type for easier comparison
|
||||
mimeType = strings.ToLower(mimeType)
|
||||
|
||||
// if the MIME type is text, then it's definitely not binary
|
||||
if strings.HasPrefix(mimeType, "text/") {
|
||||
return false
|
||||
}
|
||||
|
||||
// defined non-binary MIME types
|
||||
nonBinaryTypes := map[string]struct{}{
|
||||
"application/json": {},
|
||||
"application/xml": {},
|
||||
"application/x-www-form-urlencoded": {},
|
||||
"application/javascript": {},
|
||||
"application/xhtml+xml": {},
|
||||
}
|
||||
if _, ok := nonBinaryTypes[mimeType]; ok {
|
||||
return false
|
||||
}
|
||||
|
||||
// if the MIME type is JSON or XML, it's definitely not binary
|
||||
if strings.HasSuffix(mimeType, "+json") || strings.HasSuffix(mimeType, "+xml") {
|
||||
return false
|
||||
}
|
||||
|
||||
// otherwise, it's binary
|
||||
// (this includes application/octet-stream、image/*、video/*、audio/*)
|
||||
return true
|
||||
}
|
||||
|
||||
41
net/ghttp/ghttp_request_param_handler.go
Normal file
41
net/ghttp/ghttp_request_param_handler.go
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package ghttp
|
||||
|
||||
import "github.com/gogf/gf/v2/util/gmeta"
|
||||
|
||||
// GetHandlerResponse retrieves and returns the handler response object and its error.
|
||||
func (r *Request) GetHandlerResponse() interface{} {
|
||||
return r.handlerResponse
|
||||
}
|
||||
|
||||
// GetServeHandler retrieves and returns the user defined handler used to serve this request.
|
||||
func (r *Request) GetServeHandler() *HandlerItemParsed {
|
||||
return r.serveHandler
|
||||
}
|
||||
|
||||
// GetMetaTag retrieves and returns the metadata value associated with the given key from the request struct.
|
||||
// The meta value is from struct tags from g.Meta/gmeta.Meta type.
|
||||
// For example:
|
||||
//
|
||||
// type GetMetaTagReq struct {
|
||||
// g.Meta `path:"/test" method:"post" summary:"meta_tag" tags:"meta"`
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// r.GetServeHandler().GetMetaTag("summary") // returns "meta_tag"
|
||||
// r.GetServeHandler().GetMetaTag("method") // returns "post"
|
||||
func (h *HandlerItemParsed) GetMetaTag(key string) string {
|
||||
if h == nil || h.Handler == nil {
|
||||
return ""
|
||||
}
|
||||
metaValue := gmeta.Get(h.Handler.Info.Type.In(1), key)
|
||||
if metaValue != nil {
|
||||
return metaValue.String()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@ -8,7 +8,6 @@ package ghttp
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/net/goai"
|
||||
"github.com/gogf/gf/v2/os/gstructs"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
@ -178,15 +177,17 @@ func (r *Request) doGetRequestStruct(pointer interface{}, mapping ...map[string]
|
||||
if data == nil {
|
||||
data = map[string]interface{}{}
|
||||
}
|
||||
// Default struct values.
|
||||
if err = r.mergeDefaultStructValue(data, pointer); err != nil {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// `in` Tag Struct values.
|
||||
if err = r.mergeInTagStructValue(data); err != nil {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Default struct values.
|
||||
if err = r.mergeDefaultStructValue(data, pointer); err != nil {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
return data, gconv.Struct(data, pointer, mapping...)
|
||||
}
|
||||
|
||||
@ -194,20 +195,9 @@ func (r *Request) doGetRequestStruct(pointer interface{}, mapping ...map[string]
|
||||
func (r *Request) mergeDefaultStructValue(data map[string]interface{}, pointer interface{}) error {
|
||||
fields := r.serveHandler.Handler.Info.ReqStructFields
|
||||
if len(fields) > 0 {
|
||||
var (
|
||||
foundKey string
|
||||
foundValue interface{}
|
||||
)
|
||||
for _, field := range fields {
|
||||
if tagValue := field.TagDefault(); tagValue != "" {
|
||||
foundKey, foundValue = gutil.MapPossibleItemByKey(data, field.Name())
|
||||
if foundKey == "" {
|
||||
data[field.Name()] = tagValue
|
||||
} else {
|
||||
if empty.IsEmpty(foundValue) {
|
||||
data[foundKey] = tagValue
|
||||
}
|
||||
}
|
||||
mergeTagValueWithFoundKey(data, false, field.Name(), field.Name(), tagValue)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
@ -219,19 +209,8 @@ func (r *Request) mergeDefaultStructValue(data map[string]interface{}, pointer i
|
||||
return err
|
||||
}
|
||||
if len(tagFields) > 0 {
|
||||
var (
|
||||
foundKey string
|
||||
foundValue interface{}
|
||||
)
|
||||
for _, field := range tagFields {
|
||||
foundKey, foundValue = gutil.MapPossibleItemByKey(data, field.Name())
|
||||
if foundKey == "" {
|
||||
data[field.Name()] = field.TagValue
|
||||
} else {
|
||||
if empty.IsEmpty(foundValue) {
|
||||
data[foundKey] = field.TagValue
|
||||
}
|
||||
}
|
||||
mergeTagValueWithFoundKey(data, false, field.Name(), field.Name(), field.TagValue)
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,34 +240,29 @@ func (r *Request) mergeInTagStructValue(data map[string]interface{}) error {
|
||||
|
||||
for _, field := range fields {
|
||||
if tagValue := field.TagIn(); tagValue != "" {
|
||||
findKey := field.TagPriorityName()
|
||||
switch tagValue {
|
||||
case goai.ParameterInHeader:
|
||||
foundHeaderKey, foundHeaderValue := gutil.MapPossibleItemByKey(headerMap, field.TagPriorityName())
|
||||
if foundHeaderKey != "" {
|
||||
foundKey, foundValue = gutil.MapPossibleItemByKey(data, foundHeaderKey)
|
||||
if foundKey == "" {
|
||||
data[field.Name()] = foundHeaderValue
|
||||
} else {
|
||||
if empty.IsEmpty(foundValue) {
|
||||
data[foundKey] = foundHeaderValue
|
||||
}
|
||||
}
|
||||
}
|
||||
foundKey, foundValue = gutil.MapPossibleItemByKey(headerMap, findKey)
|
||||
case goai.ParameterInCookie:
|
||||
foundCookieKey, foundCookieValue := gutil.MapPossibleItemByKey(cookieMap, field.TagPriorityName())
|
||||
if foundCookieKey != "" {
|
||||
foundKey, foundValue = gutil.MapPossibleItemByKey(data, foundCookieKey)
|
||||
if foundKey == "" {
|
||||
data[field.Name()] = foundCookieValue
|
||||
} else {
|
||||
if empty.IsEmpty(foundValue) {
|
||||
data[foundKey] = foundCookieValue
|
||||
}
|
||||
}
|
||||
}
|
||||
foundKey, foundValue = gutil.MapPossibleItemByKey(cookieMap, findKey)
|
||||
}
|
||||
if foundKey != "" {
|
||||
mergeTagValueWithFoundKey(data, true, foundKey, field.Name(), foundValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// mergeTagValueWithFoundKey merges the request parameters when the key does not exist in the map or overwritten is true or the value is nil.
|
||||
func mergeTagValueWithFoundKey(data map[string]interface{}, overwritten bool, findKey string, fieldName string, tagValue interface{}) {
|
||||
if foundKey, foundValue := gutil.MapPossibleItemByKey(data, findKey); foundKey == "" {
|
||||
data[fieldName] = tagValue
|
||||
} else {
|
||||
if overwritten || foundValue == nil {
|
||||
data[foundKey] = tagValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,7 +205,7 @@ type ServerConfig struct {
|
||||
// Logging.
|
||||
// ======================================================================================================
|
||||
|
||||
Logger *glog.Logger `json:"logger"` // Logger specifies the logger for server.
|
||||
Logger *glog.Logger `json:"logger"` // Logger directly specifies the logger for server.
|
||||
LogPath string `json:"logPath"` // LogPath specifies the directory for storing logging files.
|
||||
LogLevel string `json:"logLevel"` // LogLevel specifies the logging level for logger.
|
||||
LogStdout bool `json:"logStdout"` // LogStdout specifies whether printing logging content to stdout.
|
||||
@ -267,6 +267,9 @@ type ServerConfig struct {
|
||||
|
||||
// DumpRouterMap specifies whether automatically dumps router map when server starts.
|
||||
DumpRouterMap bool `json:"dumpRouterMap"`
|
||||
|
||||
// AutoDecodingBody specifies whether automatically decodes request body using common encoding types: json/xml.
|
||||
AutoDecodingBody bool `json:"auto_decoding_body"`
|
||||
}
|
||||
|
||||
// NewConfig creates and returns a ServerConfig object with default configurations.
|
||||
@ -313,6 +316,7 @@ func NewConfig() ServerConfig {
|
||||
Graceful: false,
|
||||
GracefulTimeout: 2, // seconds
|
||||
GracefulShutdownTimeout: 5, // seconds
|
||||
AutoDecodingBody: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -286,6 +286,6 @@ func (item HandlerItem) MarshalJSON() ([]byte, error) {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (item HandlerItemParsed) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(item.Handler)
|
||||
func (h *HandlerItemParsed) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(h.Handler)
|
||||
}
|
||||
|
||||
@ -30,15 +30,15 @@ func Test_ConfigFromMap(t *testing.T) {
|
||||
"readTimeout": "60s",
|
||||
"indexFiles": g.Slice{"index.php", "main.php"},
|
||||
"errorLogEnabled": true,
|
||||
"cookieMaxAge": "1y",
|
||||
"cookieMaxAge": "1d",
|
||||
"cookieSameSite": "lax",
|
||||
"cookieSecure": true,
|
||||
"cookieHttpOnly": true,
|
||||
}
|
||||
config, err := ghttp.ConfigFromMap(m)
|
||||
t.AssertNil(err)
|
||||
d1, _ := time.ParseDuration(gconv.String(m["readTimeout"]))
|
||||
d2, _ := time.ParseDuration(gconv.String(m["cookieMaxAge"]))
|
||||
d1, _ := gtime.ParseDuration(gconv.String(m["readTimeout"]))
|
||||
d2, _ := gtime.ParseDuration(gconv.String(m["cookieMaxAge"]))
|
||||
t.Assert(config.Address, m["address"])
|
||||
t.Assert(config.ReadTimeout, d1)
|
||||
t.Assert(config.CookieMaxAge, d2)
|
||||
|
||||
@ -13,34 +13,34 @@ import (
|
||||
"github.com/gogf/gf/v2/util/guid"
|
||||
)
|
||||
|
||||
type UserReq struct {
|
||||
// UserTagInReq struct tag "in" supports: header, cookie
|
||||
type UserTagInReq struct {
|
||||
g.Meta `path:"/user" tags:"User" method:"post" summary:"user api" title:"api title"`
|
||||
Id int `v:"required" d:"1"`
|
||||
Name string `v:"required" in:"cookie"`
|
||||
Age string `v:"required" in:"header"`
|
||||
// header,query,cookie,form
|
||||
}
|
||||
|
||||
type UserRes struct {
|
||||
type UserTagInRes struct {
|
||||
g.Meta `mime:"text/html" example:"string"`
|
||||
}
|
||||
|
||||
var (
|
||||
User = cUser{}
|
||||
UserTagIn = cUserTagIn{}
|
||||
)
|
||||
|
||||
type cUser struct{}
|
||||
type cUserTagIn struct{}
|
||||
|
||||
func (c *cUser) User(ctx context.Context, req *UserReq) (res *UserRes, err error) {
|
||||
func (c *cUserTagIn) User(ctx context.Context, req *UserTagInReq) (res *UserTagInRes, err error) {
|
||||
g.RequestFromCtx(ctx).Response.WriteJson(req)
|
||||
return
|
||||
}
|
||||
|
||||
func Test_Params_Tag(t *testing.T) {
|
||||
func Test_ParamsTagIn(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(ghttp.MiddlewareHandlerResponse)
|
||||
group.Bind(User)
|
||||
group.Bind(UserTagIn)
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
@ -56,17 +56,101 @@ func Test_Params_Tag(t *testing.T) {
|
||||
client.SetHeader("age", "18")
|
||||
|
||||
t.Assert(client.PostContent(ctx, "/user"), `{"Id":1,"Name":"john","Age":"18"}`)
|
||||
t.Assert(client.PostContent(ctx, "/user", "name=&age=&id="), `{"Id":1,"Name":"john","Age":"18"}`)
|
||||
t.Assert(client.PostContent(ctx, "/user", "name=&age="), `{"Id":1,"Name":"john","Age":"18"}`)
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_ParamTag(b *testing.B) {
|
||||
type UserTagDefaultReq struct {
|
||||
g.Meta `path:"/user-default" method:"post,get" summary:"user default tag api"`
|
||||
Id int `v:"required" d:"1"`
|
||||
Name string `d:"john"`
|
||||
Age int `d:"18"`
|
||||
Score float64 `d:"99.9"`
|
||||
IsVip bool `d:"true"`
|
||||
NickName string `p:"nickname" d:"nickname-default"`
|
||||
EmptyStr string `d:""`
|
||||
Email string
|
||||
Address string
|
||||
}
|
||||
|
||||
type UserTagDefaultRes struct {
|
||||
g.Meta `mime:"application/json" example:"string"`
|
||||
}
|
||||
|
||||
var (
|
||||
UserTagDefault = cUserTagDefault{}
|
||||
)
|
||||
|
||||
type cUserTagDefault struct{}
|
||||
|
||||
func (c *cUserTagDefault) User(ctx context.Context, req *UserTagDefaultReq) (res *UserTagDefaultRes, err error) {
|
||||
g.RequestFromCtx(ctx).Response.WriteJson(req)
|
||||
return
|
||||
}
|
||||
|
||||
func Test_ParamsTagDefault(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(ghttp.MiddlewareHandlerResponse)
|
||||
group.Bind(UserTagDefault)
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())
|
||||
client := g.Client()
|
||||
client.SetPrefix(prefix)
|
||||
|
||||
// Test with no parameters, should use all default values
|
||||
resp := client.GetContent(ctx, "/user-default")
|
||||
t.Assert(resp, `{"Id":1,"Name":"john","Age":18,"Score":99.9,"IsVip":true,"NickName":"nickname-default","EmptyStr":"","Email":"","Address":""}`)
|
||||
|
||||
// Test with partial parameters (query method), should use partial default values
|
||||
resp = client.GetContent(ctx, "/user-default?id=100&name=smith")
|
||||
t.Assert(resp, `{"Id":100,"Name":"smith","Age":18,"Score":99.9,"IsVip":true,"NickName":"nickname-default","EmptyStr":"","Email":"","Address":""}`)
|
||||
|
||||
// Test with partial parameters (query method), should use partial default values
|
||||
resp = client.GetContent(ctx, "/user-default?id=100&name=smith&age")
|
||||
t.Assert(resp, `{"Id":100,"Name":"smith","Age":18,"Score":99.9,"IsVip":true,"NickName":"nickname-default","EmptyStr":"","Email":"","Address":""}`)
|
||||
|
||||
// Test providing partial parameters via POST form
|
||||
resp = client.PostContent(ctx, "/user-default", "id=200&age=30&nickname=jack")
|
||||
t.Assert(resp, `{"Id":200,"Name":"john","Age":30,"Score":99.9,"IsVip":true,"NickName":"jack","EmptyStr":"","Email":"","Address":""}`)
|
||||
|
||||
// Test providing partial parameters via POST JSON
|
||||
resp = client.ContentJson().PostContent(ctx, "/user-default", g.Map{
|
||||
"id": 300,
|
||||
"name": "bob",
|
||||
"score": 88.8,
|
||||
"address": "beijing",
|
||||
})
|
||||
t.Assert(resp, `{"Id":300,"Name":"bob","Age":18,"Score":88.8,"IsVip":true,"NickName":"nickname-default","EmptyStr":"","Email":"","Address":"beijing"}`)
|
||||
|
||||
// Test providing JSON content via GET request
|
||||
resp = client.ContentJson().PostContent(ctx, "/user-default", `{"id":500,"isVip":false}`)
|
||||
t.Assert(resp, `{"Id":500,"Name":"john","Age":18,"Score":99.9,"IsVip":false,"NickName":"nickname-default","EmptyStr":"","Email":"","Address":""}`)
|
||||
|
||||
// Test providing empty values, should use default values
|
||||
resp = client.PostContent(ctx, "/user-default", "id=400&name=&age=")
|
||||
t.Assert(resp, `{"Id":400,"Name":"","Age":0,"Score":99.9,"IsVip":true,"NickName":"nickname-default","EmptyStr":"","Email":"","Address":""}`)
|
||||
|
||||
// Test providing JSON content via GET request
|
||||
resp = client.ContentJson().GetContent(ctx, "/user-default", `{"id":500,"isVip":false}`)
|
||||
t.Assert(resp, `{"Id":500,"Name":"john","Age":18,"Score":99.9,"IsVip":false,"NickName":"nickname-default","EmptyStr":"","Email":"","Address":""}`)
|
||||
})
|
||||
}
|
||||
|
||||
func Benchmark_ParamTagIn(b *testing.B) {
|
||||
b.StopTimer()
|
||||
|
||||
s := g.Server(guid.S())
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(ghttp.MiddlewareHandlerResponse)
|
||||
group.Bind(User)
|
||||
group.Bind(UserTagIn)
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.SetAccessLogEnabled(false)
|
||||
|
||||
@ -8,6 +8,7 @@ package ghttp_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"testing"
|
||||
@ -861,3 +862,44 @@ func Test_Params_GetRequestMapStrVar(t *testing.T) {
|
||||
t.Assert(client.GetContent(ctx, "/GetRequestMapStrVar", "id=1"), 1)
|
||||
})
|
||||
}
|
||||
|
||||
type GetMetaTagReq struct {
|
||||
g.Meta `path:"/test" method:"post" summary:"meta_tag" tags:"meta"`
|
||||
Name string
|
||||
}
|
||||
type GetMetaTagRes struct{}
|
||||
|
||||
type GetMetaTagSt struct{}
|
||||
|
||||
func (r GetMetaTagSt) PostTest(ctx context.Context, req *GetMetaTagReq) (*GetMetaTagRes, error) {
|
||||
return &GetMetaTagRes{}, nil
|
||||
}
|
||||
|
||||
func TestRequest_GetServeHandler_GetMetaTag(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.Use(func(r *ghttp.Request) {
|
||||
r.Response.Writef(
|
||||
"summary:%s,method:%s",
|
||||
r.GetServeHandler().GetMetaTag("summary"),
|
||||
r.GetServeHandler().GetMetaTag("method"),
|
||||
)
|
||||
})
|
||||
s.Group("/", func(grp *ghttp.RouterGroup) {
|
||||
grp.Bind(GetMetaTagSt{})
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(1000 * time.Millisecond)
|
||||
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
t.Assert(client.PostContent(ctx, "/test", "name=john"), "summary:meta_tag,method:post")
|
||||
})
|
||||
}
|
||||
|
||||
@ -523,7 +523,7 @@ func Test_NullString_Issue3465(t *testing.T) {
|
||||
"name": "null",
|
||||
}
|
||||
|
||||
expect1 := `{"code":0,"message":"OK","data":{"name":null}}`
|
||||
expect1 := `{"code":0,"message":"OK","data":{"name":["null"]}}`
|
||||
t.Assert(client.GetContent(ctx, "/test", data1), expect1)
|
||||
|
||||
data2 := map[string]any{
|
||||
|
||||
@ -678,3 +678,51 @@ func Test_Issue4047(t *testing.T) {
|
||||
t.Assert(s.Logger(), nil)
|
||||
})
|
||||
}
|
||||
|
||||
// Issue4093Req
|
||||
type Issue4093Req struct {
|
||||
g.Meta `path:"/test" method:"post"`
|
||||
Page int `json:"page" example:"10" d:"1" v:"min:1#页码最小值不能低于1" dc:"当前页码"`
|
||||
PerPage int `json:"pageSize" example:"1" d:"10" v:"min:1|max:200#每页数量最小值不能低于1|最大值不能大于200" dc:"每页数量"`
|
||||
Pagination bool `json:"pagination" d:"true" dc:"是否需要进行分页"`
|
||||
Name string `json:"name" d:"john"`
|
||||
Number int `json:"number" d:"1"`
|
||||
}
|
||||
|
||||
type Issue4093Res struct {
|
||||
g.Meta `mime:"text/html" example:"string"`
|
||||
}
|
||||
|
||||
var (
|
||||
Issue4093 = cIssue4093{}
|
||||
)
|
||||
|
||||
type cIssue4093 struct{}
|
||||
|
||||
func (c *cIssue4093) User(ctx context.Context, req *Issue4093Req) (res *Issue4093Res, err error) {
|
||||
g.RequestFromCtx(ctx).Response.WriteJson(req)
|
||||
return
|
||||
}
|
||||
|
||||
// https://github.com/gogf/gf/issues/4093
|
||||
func Test_Issue4093(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(ghttp.MiddlewareHandlerResponse)
|
||||
group.Bind(Issue4093)
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
prefix := fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort())
|
||||
client := g.Client().ContentJson()
|
||||
client.SetPrefix(prefix)
|
||||
|
||||
t.Assert(client.PostContent(ctx, "/test", `{"pagination":false,"name":"","number":0}`), `{"page":1,"pageSize":10,"pagination":false,"name":"","number":0}`)
|
||||
t.Assert(client.PostContent(ctx, "/test"), `{"page":1,"pageSize":10,"pagination":true,"name":"john","number":1}`)
|
||||
})
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ type Field struct {
|
||||
type FieldsInput struct {
|
||||
// Pointer should be type of struct/*struct.
|
||||
// TODO this attribute name is not suitable, which would make confuse.
|
||||
Pointer interface{}
|
||||
Pointer any
|
||||
|
||||
// RecursiveOption specifies the way retrieving the fields recursively if the attribute
|
||||
// is an embedded struct. It is RecursiveOptionNone in default.
|
||||
@ -47,7 +47,7 @@ type FieldsInput struct {
|
||||
type FieldMapInput struct {
|
||||
// Pointer should be type of struct/*struct.
|
||||
// TODO this attribute name is not suitable, which would make confuse.
|
||||
Pointer interface{}
|
||||
Pointer any
|
||||
|
||||
// PriorityTagArray specifies the priority tag array for retrieving from high to low.
|
||||
// If it's given `nil`, it returns map[name]Field, of which the `name` is attribute name.
|
||||
@ -123,6 +123,7 @@ func Fields(in FieldsInput) ([]Field, error) {
|
||||
}
|
||||
}
|
||||
continue
|
||||
default:
|
||||
}
|
||||
}
|
||||
continue
|
||||
@ -194,6 +195,7 @@ func FieldMap(in FieldMapInput) (map[string]Field, error) {
|
||||
mapField[k] = tempV
|
||||
}
|
||||
}
|
||||
default:
|
||||
}
|
||||
} else {
|
||||
mapField[field.Name()] = tempField
|
||||
@ -205,7 +207,19 @@ func FieldMap(in FieldMapInput) (map[string]Field, error) {
|
||||
|
||||
// StructType retrieves and returns the struct Type of specified struct/*struct.
|
||||
// The parameter `object` should be either type of struct/*struct/[]struct/[]*struct.
|
||||
func StructType(object interface{}) (*Type, error) {
|
||||
func StructType(object any) (*Type, error) {
|
||||
// if already reflect.Type
|
||||
if reflectType, ok := object.(reflect.Type); ok {
|
||||
for reflectType.Kind() == reflect.Ptr {
|
||||
reflectType = reflectType.Elem()
|
||||
}
|
||||
if reflectType.Kind() == reflect.Struct {
|
||||
return &Type{
|
||||
Type: reflectType,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
reflectValue reflect.Value
|
||||
reflectKind reflect.Kind
|
||||
|
||||
@ -10,36 +10,155 @@
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/converter"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/structcache"
|
||||
)
|
||||
|
||||
var (
|
||||
// Empty strings.
|
||||
emptyStringMap = map[string]struct{}{
|
||||
"": {},
|
||||
"0": {},
|
||||
"no": {},
|
||||
"off": {},
|
||||
"false": {},
|
||||
}
|
||||
// Converter is the manager for type converting.
|
||||
type Converter interface {
|
||||
ConverterForConvert
|
||||
ConverterForRegister
|
||||
ConverterForInt
|
||||
ConverterForUint
|
||||
ConverterForTime
|
||||
ConverterForFloat
|
||||
ConverterForMap
|
||||
ConverterForSlice
|
||||
ConverterForStruct
|
||||
ConverterForBasic
|
||||
}
|
||||
|
||||
// ConverterForBasic is the basic converting interface.
|
||||
type ConverterForBasic interface {
|
||||
Scan(srcValue, dstPointer any, option ScanOption) (err error)
|
||||
String(any any) (string, error)
|
||||
Bool(any any) (bool, error)
|
||||
Rune(any any) (rune, error)
|
||||
}
|
||||
|
||||
// ConverterForTime is the converting interface for time.
|
||||
type ConverterForTime interface {
|
||||
Time(v any, format ...string) (time.Time, error)
|
||||
Duration(v any) (time.Duration, error)
|
||||
GTime(v any, format ...string) (*gtime.Time, error)
|
||||
}
|
||||
|
||||
// ConverterForInt is the converting interface for integer.
|
||||
type ConverterForInt interface {
|
||||
Int(v any) (int, error)
|
||||
Int8(v any) (int8, error)
|
||||
Int16(v any) (int16, error)
|
||||
Int32(v any) (int32, error)
|
||||
Int64(v any) (int64, error)
|
||||
}
|
||||
|
||||
// ConverterForUint is the converting interface for unsigned integer.
|
||||
type ConverterForUint interface {
|
||||
Uint(v any) (uint, error)
|
||||
Uint8(v any) (uint8, error)
|
||||
Uint16(v any) (uint16, error)
|
||||
Uint32(v any) (uint32, error)
|
||||
Uint64(v any) (uint64, error)
|
||||
}
|
||||
|
||||
// ConverterForFloat is the converting interface for float.
|
||||
type ConverterForFloat interface {
|
||||
Float32(v any) (float32, error)
|
||||
Float64(v any) (float64, error)
|
||||
}
|
||||
|
||||
// ConverterForMap is the converting interface for map.
|
||||
type ConverterForMap interface {
|
||||
Map(v any, option MapOption) (map[string]any, error)
|
||||
MapStrStr(v any, option MapOption) (map[string]string, error)
|
||||
}
|
||||
|
||||
// ConverterForSlice is the converting interface for slice.
|
||||
type ConverterForSlice interface {
|
||||
Bytes(v any) ([]byte, error)
|
||||
Runes(v any) ([]rune, error)
|
||||
SliceAny(v any, option SliceOption) ([]any, error)
|
||||
SliceFloat32(v any, option SliceOption) ([]float32, error)
|
||||
SliceFloat64(v any, option SliceOption) ([]float64, error)
|
||||
SliceInt(v any, option SliceOption) ([]int, error)
|
||||
SliceInt32(v any, option SliceOption) ([]int32, error)
|
||||
SliceInt64(v any, option SliceOption) ([]int64, error)
|
||||
SliceUint(v any, option SliceOption) ([]uint, error)
|
||||
SliceUint32(v any, option SliceOption) ([]uint32, error)
|
||||
SliceUint64(v any, option SliceOption) ([]uint64, error)
|
||||
SliceStr(v any, option SliceOption) ([]string, error)
|
||||
SliceMap(v any, sliceOption SliceOption, mapOption MapOption) ([]map[string]any, error)
|
||||
}
|
||||
|
||||
// ConverterForStruct is the converting interface for struct.
|
||||
type ConverterForStruct interface {
|
||||
Struct(params, pointer any, option StructOption) (err error)
|
||||
Structs(params, pointer any, sliceOption SliceOption, structOption StructOption) (err error)
|
||||
}
|
||||
|
||||
// ConverterForConvert is the converting interface for custom converting.
|
||||
type ConverterForConvert interface {
|
||||
ConvertWithRefer(fromValue, referValue any, option ConvertOption) (any, error)
|
||||
ConvertWithTypeName(fromValue any, toTypeName string, option ConvertOption) (any, error)
|
||||
}
|
||||
|
||||
// ConverterForRegister is the converting interface for custom converter registration.
|
||||
type ConverterForRegister interface {
|
||||
RegisterTypeConverterFunc(f any) error
|
||||
RegisterAnyConverterFunc(f AnyConvertFunc, types ...reflect.Type)
|
||||
}
|
||||
|
||||
type (
|
||||
// AnyConvertFunc is the function type for converting any to specified type.
|
||||
AnyConvertFunc = structcache.AnyConvertFunc
|
||||
|
||||
// MapOption specifies the option for map converting.
|
||||
MapOption = converter.MapOption
|
||||
|
||||
// SliceOption is the option for Slice type converting.
|
||||
SliceOption = converter.SliceOption
|
||||
|
||||
// ScanOption is the option for the Scan function.
|
||||
ScanOption = converter.ScanOption
|
||||
|
||||
// StructOption is the option for Struct converting.
|
||||
StructOption = converter.StructOption
|
||||
|
||||
// ConvertOption is the option for converting.
|
||||
ConvertOption = converter.ConvertOption
|
||||
)
|
||||
|
||||
// IUnmarshalValue is the interface for custom defined types customizing value assignment.
|
||||
// Note that only pointer can implement interface IUnmarshalValue.
|
||||
type IUnmarshalValue = localinterface.IUnmarshalValue
|
||||
|
||||
func init() {
|
||||
// register common converters for internal usage.
|
||||
structcache.RegisterCommonConverter(structcache.CommonConverter{
|
||||
Int64: Int64,
|
||||
Uint64: Uint64,
|
||||
String: String,
|
||||
Float32: Float32,
|
||||
Float64: Float64,
|
||||
Time: Time,
|
||||
GTime: GTime,
|
||||
Bytes: Bytes,
|
||||
Bool: Bool,
|
||||
})
|
||||
var (
|
||||
// defaultConverter is the default management object converting.
|
||||
defaultConverter = converter.NewConverter()
|
||||
)
|
||||
|
||||
// RegisterAnyConverterFunc registers custom type converting function for specified type.
|
||||
func RegisterAnyConverterFunc(f AnyConvertFunc, types ...reflect.Type) {
|
||||
defaultConverter.RegisterAnyConverterFunc(f, types...)
|
||||
}
|
||||
|
||||
// NewConverter creates and returns management object for type converting.
|
||||
func NewConverter() Converter {
|
||||
return converter.NewConverter()
|
||||
}
|
||||
|
||||
// RegisterConverter registers custom converter.
|
||||
// Deprecated: use RegisterTypeConverterFunc instead for clear
|
||||
func RegisterConverter(fn any) (err error) {
|
||||
return RegisterTypeConverterFunc(fn)
|
||||
}
|
||||
|
||||
// RegisterTypeConverterFunc registers custom converter.
|
||||
func RegisterTypeConverterFunc(fn any) (err error) {
|
||||
return defaultConverter.RegisterTypeConverterFunc(fn)
|
||||
}
|
||||
|
||||
@ -6,309 +6,40 @@
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gbinary"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/reflection"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
|
||||
)
|
||||
|
||||
// Byte converts `any` to byte.
|
||||
func Byte(any any) byte {
|
||||
v, _ := doByte(any)
|
||||
v, _ := defaultConverter.Uint8(any)
|
||||
return v
|
||||
}
|
||||
|
||||
func doByte(any any) (byte, error) {
|
||||
if v, ok := any.(byte); ok {
|
||||
return v, nil
|
||||
}
|
||||
return doUint8(any)
|
||||
}
|
||||
|
||||
// Bytes converts `any` to []byte.
|
||||
func Bytes(any any) []byte {
|
||||
v, _ := doBytes(any)
|
||||
v, _ := defaultConverter.Bytes(any)
|
||||
return v
|
||||
}
|
||||
|
||||
func doBytes(any any) ([]byte, error) {
|
||||
if empty.IsNil(any) {
|
||||
return nil, nil
|
||||
}
|
||||
switch value := any.(type) {
|
||||
case string:
|
||||
return []byte(value), nil
|
||||
|
||||
case []byte:
|
||||
return value, nil
|
||||
|
||||
default:
|
||||
if f, ok := value.(localinterface.IBytes); ok {
|
||||
return f.Bytes(), nil
|
||||
}
|
||||
originValueAndKind := reflection.OriginValueAndKind(any)
|
||||
switch originValueAndKind.OriginKind {
|
||||
case reflect.Map:
|
||||
bytes, err := json.Marshal(any)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bytes, nil
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
var (
|
||||
ok = true
|
||||
bytes = make([]byte, originValueAndKind.OriginValue.Len())
|
||||
)
|
||||
for i := range bytes {
|
||||
int32Value, err := doInt32(originValueAndKind.OriginValue.Index(i).Interface())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if int32Value < 0 || int32Value > math.MaxUint8 {
|
||||
ok = false
|
||||
break
|
||||
}
|
||||
bytes[i] = byte(int32Value)
|
||||
}
|
||||
if ok {
|
||||
return bytes, nil
|
||||
}
|
||||
default:
|
||||
}
|
||||
return gbinary.Encode(any), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Rune converts `any` to rune.
|
||||
func Rune(any any) rune {
|
||||
v, _ := doRune(any)
|
||||
v, _ := defaultConverter.Rune(any)
|
||||
return v
|
||||
}
|
||||
|
||||
func doRune(any any) (rune, error) {
|
||||
if v, ok := any.(rune); ok {
|
||||
return v, nil
|
||||
}
|
||||
v, err := doInt32(any)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// Runes converts `any` to []rune.
|
||||
func Runes(any any) []rune {
|
||||
v, _ := doRunes(any)
|
||||
v, _ := defaultConverter.Runes(any)
|
||||
return v
|
||||
}
|
||||
|
||||
func doRunes(any any) ([]rune, error) {
|
||||
if v, ok := any.([]rune); ok {
|
||||
return v, nil
|
||||
}
|
||||
s, err := doString(any)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []rune(s), nil
|
||||
}
|
||||
|
||||
// String converts `any` to string.
|
||||
// It's most commonly used converting function.
|
||||
func String(any any) string {
|
||||
v, _ := doString(any)
|
||||
v, _ := defaultConverter.String(any)
|
||||
return v
|
||||
}
|
||||
|
||||
func doString(any any) (string, error) {
|
||||
if empty.IsNil(any) {
|
||||
return "", nil
|
||||
}
|
||||
switch value := any.(type) {
|
||||
case int:
|
||||
return strconv.Itoa(value), nil
|
||||
case int8:
|
||||
return strconv.Itoa(int(value)), nil
|
||||
case int16:
|
||||
return strconv.Itoa(int(value)), nil
|
||||
case int32:
|
||||
return strconv.Itoa(int(value)), nil
|
||||
case int64:
|
||||
return strconv.FormatInt(value, 10), nil
|
||||
case uint:
|
||||
return strconv.FormatUint(uint64(value), 10), nil
|
||||
case uint8:
|
||||
return strconv.FormatUint(uint64(value), 10), nil
|
||||
case uint16:
|
||||
return strconv.FormatUint(uint64(value), 10), nil
|
||||
case uint32:
|
||||
return strconv.FormatUint(uint64(value), 10), nil
|
||||
case uint64:
|
||||
return strconv.FormatUint(value, 10), nil
|
||||
case float32:
|
||||
return strconv.FormatFloat(float64(value), 'f', -1, 32), nil
|
||||
case float64:
|
||||
return strconv.FormatFloat(value, 'f', -1, 64), nil
|
||||
case bool:
|
||||
return strconv.FormatBool(value), nil
|
||||
case string:
|
||||
return value, nil
|
||||
case []byte:
|
||||
return string(value), nil
|
||||
case complex64, complex128:
|
||||
return fmt.Sprintf("%v", value), nil
|
||||
case time.Time:
|
||||
if value.IsZero() {
|
||||
return "", nil
|
||||
}
|
||||
return value.String(), nil
|
||||
case *time.Time:
|
||||
if value == nil {
|
||||
return "", nil
|
||||
}
|
||||
return value.String(), nil
|
||||
case gtime.Time:
|
||||
if value.IsZero() {
|
||||
return "", nil
|
||||
}
|
||||
return value.String(), nil
|
||||
case *gtime.Time:
|
||||
if value == nil {
|
||||
return "", nil
|
||||
}
|
||||
return value.String(), nil
|
||||
default:
|
||||
if f, ok := value.(localinterface.IString); ok {
|
||||
// If the variable implements the String() interface,
|
||||
// then use that interface to perform the conversion
|
||||
return f.String(), nil
|
||||
}
|
||||
if f, ok := value.(localinterface.IError); ok {
|
||||
// If the variable implements the Error() interface,
|
||||
// then use that interface to perform the conversion
|
||||
return f.Error(), nil
|
||||
}
|
||||
// Reflect checks.
|
||||
var (
|
||||
rv = reflect.ValueOf(value)
|
||||
kind = rv.Kind()
|
||||
)
|
||||
switch kind {
|
||||
case
|
||||
reflect.Chan,
|
||||
reflect.Map,
|
||||
reflect.Slice,
|
||||
reflect.Func,
|
||||
reflect.Interface,
|
||||
reflect.UnsafePointer:
|
||||
if rv.IsNil() {
|
||||
return "", nil
|
||||
}
|
||||
case reflect.String:
|
||||
return rv.String(), nil
|
||||
case reflect.Ptr:
|
||||
if rv.IsNil() {
|
||||
return "", nil
|
||||
}
|
||||
return doString(rv.Elem().Interface())
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return strconv.FormatInt(rv.Int(), 10), nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return strconv.FormatUint(rv.Uint(), 10), nil
|
||||
case reflect.Uintptr:
|
||||
return strconv.FormatUint(rv.Uint(), 10), nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return strconv.FormatFloat(rv.Float(), 'f', -1, 64), nil
|
||||
case reflect.Bool:
|
||||
return strconv.FormatBool(rv.Bool()), nil
|
||||
default:
|
||||
}
|
||||
// Finally, we use json.Marshal to convert.
|
||||
jsonContent, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return fmt.Sprint(value), gerror.WrapCodef(
|
||||
gcode.CodeInvalidParameter, err, "error marshaling value to JSON for: %v", value,
|
||||
)
|
||||
}
|
||||
return string(jsonContent), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Bool converts `any` to bool.
|
||||
// It returns false if `any` is: false, "", 0, "false", "off", "no", empty slice/map.
|
||||
func Bool(any any) bool {
|
||||
v, _ := doBool(any)
|
||||
v, _ := defaultConverter.Bool(any)
|
||||
return v
|
||||
}
|
||||
|
||||
func doBool(any any) (bool, error) {
|
||||
if empty.IsNil(any) {
|
||||
return false, nil
|
||||
}
|
||||
switch value := any.(type) {
|
||||
case bool:
|
||||
return value, nil
|
||||
case []byte:
|
||||
if _, ok := emptyStringMap[strings.ToLower(string(value))]; ok {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
case string:
|
||||
if _, ok := emptyStringMap[strings.ToLower(value)]; ok {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
default:
|
||||
if f, ok := value.(localinterface.IBool); ok {
|
||||
return f.Bool(), nil
|
||||
}
|
||||
rv := reflect.ValueOf(any)
|
||||
switch rv.Kind() {
|
||||
case reflect.Ptr:
|
||||
if rv.IsNil() {
|
||||
return false, nil
|
||||
}
|
||||
if rv.Type().Elem().Kind() == reflect.Bool {
|
||||
return rv.Elem().Bool(), nil
|
||||
}
|
||||
return doBool(rv.Elem().Interface())
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return rv.Int() != 0, nil
|
||||
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return rv.Uint() != 0, nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return rv.Float() != 0, nil
|
||||
case reflect.Bool:
|
||||
return rv.Bool(), nil
|
||||
// TODO:(Map,Array,Slice,Struct) It might panic here for these types.
|
||||
case reflect.Map, reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
return rv.Len() != 0, nil
|
||||
case reflect.Struct:
|
||||
return true, nil
|
||||
default:
|
||||
s, err := doString(any)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if _, ok := emptyStringMap[strings.ToLower(s)]; ok {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,353 +6,30 @@
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/intlog"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// Convert converts the variable `fromValue` to the type `toTypeName`, the type `toTypeName` is specified by string.
|
||||
//
|
||||
// The optional parameter `extraParams` is used for additional necessary parameter for this conversion.
|
||||
// It supports common basic types conversion as its conversion based on type name string.
|
||||
func Convert(fromValue interface{}, toTypeName string, extraParams ...interface{}) interface{} {
|
||||
return doConvert(doConvertInput{
|
||||
FromValue: fromValue,
|
||||
ToTypeName: toTypeName,
|
||||
ReferValue: nil,
|
||||
Extra: extraParams,
|
||||
func Convert(fromValue any, toTypeName string, extraParams ...any) any {
|
||||
result, _ := defaultConverter.ConvertWithTypeName(fromValue, toTypeName, ConvertOption{
|
||||
ExtraParams: extraParams,
|
||||
SliceOption: SliceOption{ContinueOnError: true},
|
||||
MapOption: MapOption{ContinueOnError: true},
|
||||
StructOption: StructOption{ContinueOnError: true},
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
// ConvertWithRefer converts the variable `fromValue` to the type referred by value `referValue`.
|
||||
//
|
||||
// The optional parameter `extraParams` is used for additional necessary parameter for this conversion.
|
||||
// It supports common basic types conversion as its conversion based on type name string.
|
||||
func ConvertWithRefer(fromValue interface{}, referValue interface{}, extraParams ...interface{}) interface{} {
|
||||
var referValueRf reflect.Value
|
||||
if v, ok := referValue.(reflect.Value); ok {
|
||||
referValueRf = v
|
||||
} else {
|
||||
referValueRf = reflect.ValueOf(referValue)
|
||||
}
|
||||
return doConvert(doConvertInput{
|
||||
FromValue: fromValue,
|
||||
ToTypeName: referValueRf.Type().String(),
|
||||
ReferValue: referValue,
|
||||
Extra: extraParams,
|
||||
func ConvertWithRefer(fromValue any, referValue any, extraParams ...any) any {
|
||||
result, _ := defaultConverter.ConvertWithRefer(fromValue, referValue, ConvertOption{
|
||||
ExtraParams: extraParams,
|
||||
SliceOption: SliceOption{ContinueOnError: true},
|
||||
MapOption: MapOption{ContinueOnError: true},
|
||||
StructOption: StructOption{ContinueOnError: true},
|
||||
})
|
||||
}
|
||||
|
||||
type doConvertInput struct {
|
||||
FromValue interface{} // Value that is converted from.
|
||||
ToTypeName string // Target value type name in string.
|
||||
ReferValue interface{} // Referred value, a value in type `ToTypeName`. Note that its type might be reflect.Value.
|
||||
Extra []interface{} // Extra values for implementing the converting.
|
||||
// Marks that the value is already converted and set to `ReferValue`. Caller can ignore the returned result.
|
||||
// It is an attribute for internal usage purpose.
|
||||
alreadySetToReferValue bool
|
||||
}
|
||||
|
||||
// doConvert does commonly use types converting.
|
||||
func doConvert(in doConvertInput) (convertedValue interface{}) {
|
||||
switch in.ToTypeName {
|
||||
case "int":
|
||||
return Int(in.FromValue)
|
||||
case "*int":
|
||||
if _, ok := in.FromValue.(*int); ok {
|
||||
return in.FromValue
|
||||
}
|
||||
v := Int(in.FromValue)
|
||||
return &v
|
||||
|
||||
case "int8":
|
||||
return Int8(in.FromValue)
|
||||
case "*int8":
|
||||
if _, ok := in.FromValue.(*int8); ok {
|
||||
return in.FromValue
|
||||
}
|
||||
v := Int8(in.FromValue)
|
||||
return &v
|
||||
|
||||
case "int16":
|
||||
return Int16(in.FromValue)
|
||||
case "*int16":
|
||||
if _, ok := in.FromValue.(*int16); ok {
|
||||
return in.FromValue
|
||||
}
|
||||
v := Int16(in.FromValue)
|
||||
return &v
|
||||
|
||||
case "int32":
|
||||
return Int32(in.FromValue)
|
||||
case "*int32":
|
||||
if _, ok := in.FromValue.(*int32); ok {
|
||||
return in.FromValue
|
||||
}
|
||||
v := Int32(in.FromValue)
|
||||
return &v
|
||||
|
||||
case "int64":
|
||||
return Int64(in.FromValue)
|
||||
case "*int64":
|
||||
if _, ok := in.FromValue.(*int64); ok {
|
||||
return in.FromValue
|
||||
}
|
||||
v := Int64(in.FromValue)
|
||||
return &v
|
||||
|
||||
case "uint":
|
||||
return Uint(in.FromValue)
|
||||
case "*uint":
|
||||
if _, ok := in.FromValue.(*uint); ok {
|
||||
return in.FromValue
|
||||
}
|
||||
v := Uint(in.FromValue)
|
||||
return &v
|
||||
|
||||
case "uint8":
|
||||
return Uint8(in.FromValue)
|
||||
case "*uint8":
|
||||
if _, ok := in.FromValue.(*uint8); ok {
|
||||
return in.FromValue
|
||||
}
|
||||
v := Uint8(in.FromValue)
|
||||
return &v
|
||||
|
||||
case "uint16":
|
||||
return Uint16(in.FromValue)
|
||||
case "*uint16":
|
||||
if _, ok := in.FromValue.(*uint16); ok {
|
||||
return in.FromValue
|
||||
}
|
||||
v := Uint16(in.FromValue)
|
||||
return &v
|
||||
|
||||
case "uint32":
|
||||
return Uint32(in.FromValue)
|
||||
case "*uint32":
|
||||
if _, ok := in.FromValue.(*uint32); ok {
|
||||
return in.FromValue
|
||||
}
|
||||
v := Uint32(in.FromValue)
|
||||
return &v
|
||||
|
||||
case "uint64":
|
||||
return Uint64(in.FromValue)
|
||||
case "*uint64":
|
||||
if _, ok := in.FromValue.(*uint64); ok {
|
||||
return in.FromValue
|
||||
}
|
||||
v := Uint64(in.FromValue)
|
||||
return &v
|
||||
|
||||
case "float32":
|
||||
return Float32(in.FromValue)
|
||||
case "*float32":
|
||||
if _, ok := in.FromValue.(*float32); ok {
|
||||
return in.FromValue
|
||||
}
|
||||
v := Float32(in.FromValue)
|
||||
return &v
|
||||
|
||||
case "float64":
|
||||
return Float64(in.FromValue)
|
||||
case "*float64":
|
||||
if _, ok := in.FromValue.(*float64); ok {
|
||||
return in.FromValue
|
||||
}
|
||||
v := Float64(in.FromValue)
|
||||
return &v
|
||||
|
||||
case "bool":
|
||||
return Bool(in.FromValue)
|
||||
case "*bool":
|
||||
if _, ok := in.FromValue.(*bool); ok {
|
||||
return in.FromValue
|
||||
}
|
||||
v := Bool(in.FromValue)
|
||||
return &v
|
||||
|
||||
case "string":
|
||||
return String(in.FromValue)
|
||||
case "*string":
|
||||
if _, ok := in.FromValue.(*string); ok {
|
||||
return in.FromValue
|
||||
}
|
||||
v := String(in.FromValue)
|
||||
return &v
|
||||
|
||||
case "[]byte":
|
||||
return Bytes(in.FromValue)
|
||||
case "[]int":
|
||||
return Ints(in.FromValue)
|
||||
case "[]int32":
|
||||
return Int32s(in.FromValue)
|
||||
case "[]int64":
|
||||
return Int64s(in.FromValue)
|
||||
case "[]uint":
|
||||
return Uints(in.FromValue)
|
||||
case "[]uint8":
|
||||
return Bytes(in.FromValue)
|
||||
case "[]uint32":
|
||||
return Uint32s(in.FromValue)
|
||||
case "[]uint64":
|
||||
return Uint64s(in.FromValue)
|
||||
case "[]float32":
|
||||
return Float32s(in.FromValue)
|
||||
case "[]float64":
|
||||
return Float64s(in.FromValue)
|
||||
case "[]string":
|
||||
return Strings(in.FromValue)
|
||||
|
||||
case "Time", "time.Time":
|
||||
if len(in.Extra) > 0 {
|
||||
return Time(in.FromValue, String(in.Extra[0]))
|
||||
}
|
||||
return Time(in.FromValue)
|
||||
case "*time.Time":
|
||||
var v time.Time
|
||||
if len(in.Extra) > 0 {
|
||||
v = Time(in.FromValue, String(in.Extra[0]))
|
||||
} else {
|
||||
if _, ok := in.FromValue.(*time.Time); ok {
|
||||
return in.FromValue
|
||||
}
|
||||
v = Time(in.FromValue)
|
||||
}
|
||||
return &v
|
||||
|
||||
case "GTime", "gtime.Time":
|
||||
if len(in.Extra) > 0 {
|
||||
if v := GTime(in.FromValue, String(in.Extra[0])); v != nil {
|
||||
return *v
|
||||
} else {
|
||||
return *gtime.New()
|
||||
}
|
||||
}
|
||||
if v := GTime(in.FromValue); v != nil {
|
||||
return *v
|
||||
} else {
|
||||
return *gtime.New()
|
||||
}
|
||||
case "*gtime.Time":
|
||||
if len(in.Extra) > 0 {
|
||||
if v := GTime(in.FromValue, String(in.Extra[0])); v != nil {
|
||||
return v
|
||||
} else {
|
||||
return gtime.New()
|
||||
}
|
||||
}
|
||||
if v := GTime(in.FromValue); v != nil {
|
||||
return v
|
||||
} else {
|
||||
return gtime.New()
|
||||
}
|
||||
|
||||
case "Duration", "time.Duration":
|
||||
return Duration(in.FromValue)
|
||||
case "*time.Duration":
|
||||
if _, ok := in.FromValue.(*time.Duration); ok {
|
||||
return in.FromValue
|
||||
}
|
||||
v := Duration(in.FromValue)
|
||||
return &v
|
||||
|
||||
case "map[string]string":
|
||||
return MapStrStr(in.FromValue)
|
||||
|
||||
case "map[string]interface {}":
|
||||
return Map(in.FromValue)
|
||||
|
||||
case "[]map[string]interface {}":
|
||||
return Maps(in.FromValue)
|
||||
|
||||
case "RawMessage", "json.RawMessage":
|
||||
// issue 3449
|
||||
bytes, err := json.Marshal(in.FromValue)
|
||||
if err != nil {
|
||||
intlog.Errorf(context.TODO(), `%+v`, err)
|
||||
}
|
||||
return bytes
|
||||
|
||||
default:
|
||||
if in.ReferValue != nil {
|
||||
var referReflectValue reflect.Value
|
||||
if v, ok := in.ReferValue.(reflect.Value); ok {
|
||||
referReflectValue = v
|
||||
} else {
|
||||
referReflectValue = reflect.ValueOf(in.ReferValue)
|
||||
}
|
||||
var fromReflectValue reflect.Value
|
||||
if v, ok := in.FromValue.(reflect.Value); ok {
|
||||
fromReflectValue = v
|
||||
} else {
|
||||
fromReflectValue = reflect.ValueOf(in.FromValue)
|
||||
}
|
||||
|
||||
// custom converter.
|
||||
if dstReflectValue, ok, _ := callCustomConverterWithRefer(fromReflectValue, referReflectValue); ok {
|
||||
return dstReflectValue.Interface()
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if recover() != nil {
|
||||
in.alreadySetToReferValue = false
|
||||
if err := bindVarToReflectValue(referReflectValue, in.FromValue, nil); err == nil {
|
||||
in.alreadySetToReferValue = true
|
||||
convertedValue = referReflectValue.Interface()
|
||||
}
|
||||
}
|
||||
}()
|
||||
switch referReflectValue.Kind() {
|
||||
case reflect.Ptr:
|
||||
// Type converting for custom type pointers.
|
||||
// Eg:
|
||||
// type PayMode int
|
||||
// type Req struct{
|
||||
// Mode *PayMode
|
||||
// }
|
||||
//
|
||||
// Struct(`{"Mode": 1000}`, &req)
|
||||
originType := referReflectValue.Type().Elem()
|
||||
switch originType.Kind() {
|
||||
case reflect.Struct:
|
||||
// Not support some kinds.
|
||||
default:
|
||||
in.ToTypeName = originType.Kind().String()
|
||||
in.ReferValue = nil
|
||||
refElementValue := reflect.ValueOf(doConvert(in))
|
||||
originTypeValue := reflect.New(refElementValue.Type()).Elem()
|
||||
originTypeValue.Set(refElementValue)
|
||||
in.alreadySetToReferValue = true
|
||||
return originTypeValue.Addr().Convert(referReflectValue.Type()).Interface()
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
var targetValue = reflect.New(referReflectValue.Type()).Elem()
|
||||
if err := doMapToMap(in.FromValue, targetValue); err == nil {
|
||||
in.alreadySetToReferValue = true
|
||||
}
|
||||
return targetValue.Interface()
|
||||
}
|
||||
in.ToTypeName = referReflectValue.Kind().String()
|
||||
in.ReferValue = nil
|
||||
in.alreadySetToReferValue = true
|
||||
convertedValue = reflect.ValueOf(doConvert(in)).Convert(referReflectValue.Type()).Interface()
|
||||
return convertedValue
|
||||
}
|
||||
return in.FromValue
|
||||
}
|
||||
}
|
||||
|
||||
func doConvertWithReflectValueSet(reflectValue reflect.Value, in doConvertInput) {
|
||||
convertedValue := doConvert(in)
|
||||
if !in.alreadySetToReferValue {
|
||||
reflectValue.Set(reflect.ValueOf(convertedValue))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
@ -1,187 +0,0 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/structcache"
|
||||
)
|
||||
|
||||
type (
|
||||
converterInType = reflect.Type
|
||||
converterOutType = reflect.Type
|
||||
converterFunc = reflect.Value
|
||||
)
|
||||
|
||||
// customConverters for internal converter storing.
|
||||
var customConverters = make(map[converterInType]map[converterOutType]converterFunc)
|
||||
|
||||
// RegisterConverter to register custom converter.
|
||||
// It must be registered before you use this custom converting feature.
|
||||
// It is suggested to do it in boot procedure of the process.
|
||||
//
|
||||
// Note:
|
||||
// 1. The parameter `fn` must be defined as pattern `func(T1) (T2, error)`.
|
||||
// It will convert type `T1` to type `T2`.
|
||||
// 2. The `T1` should not be type of pointer, but the `T2` should be type of pointer.
|
||||
func RegisterConverter(fn interface{}) (err error) {
|
||||
var (
|
||||
fnReflectType = reflect.TypeOf(fn)
|
||||
errType = reflect.TypeOf((*error)(nil)).Elem()
|
||||
)
|
||||
if fnReflectType.Kind() != reflect.Func ||
|
||||
fnReflectType.NumIn() != 1 || fnReflectType.NumOut() != 2 ||
|
||||
!fnReflectType.Out(1).Implements(errType) {
|
||||
err = gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
"parameter must be type of converter function and defined as pattern `func(T1) (T2, error)`, but defined as `%s`",
|
||||
fnReflectType.String(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// The Key and Value of the converter map should not be pointer.
|
||||
var (
|
||||
inType = fnReflectType.In(0)
|
||||
outType = fnReflectType.Out(0)
|
||||
)
|
||||
if inType.Kind() == reflect.Pointer {
|
||||
err = gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
"invalid converter function `%s`: invalid input parameter type `%s`, should not be type of pointer",
|
||||
fnReflectType.String(), inType.String(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if outType.Kind() != reflect.Pointer {
|
||||
err = gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
"invalid converter function `%s`: invalid output parameter type `%s` should be type of pointer",
|
||||
fnReflectType.String(), outType.String(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
registeredOutTypeMap, ok := customConverters[inType]
|
||||
if !ok {
|
||||
registeredOutTypeMap = make(map[converterOutType]converterFunc)
|
||||
customConverters[inType] = registeredOutTypeMap
|
||||
}
|
||||
if _, ok = registeredOutTypeMap[outType]; ok {
|
||||
err = gerror.NewCodef(
|
||||
gcode.CodeInvalidOperation,
|
||||
"the converter parameter type `%s` to type `%s` has already been registered",
|
||||
inType.String(), outType.String(),
|
||||
)
|
||||
return
|
||||
}
|
||||
registeredOutTypeMap[outType] = reflect.ValueOf(fn)
|
||||
structcache.RegisterCustomConvertType(outType)
|
||||
return
|
||||
}
|
||||
|
||||
func getRegisteredConverterFuncAndSrcType(
|
||||
srcReflectValue, dstReflectValueForRefer reflect.Value,
|
||||
) (f converterFunc, srcType reflect.Type, ok bool) {
|
||||
if len(customConverters) == 0 {
|
||||
return reflect.Value{}, nil, false
|
||||
}
|
||||
srcType = srcReflectValue.Type()
|
||||
for srcType.Kind() == reflect.Pointer {
|
||||
srcType = srcType.Elem()
|
||||
}
|
||||
var registeredOutTypeMap map[converterOutType]converterFunc
|
||||
// firstly, it searches the map by input parameter type.
|
||||
registeredOutTypeMap, ok = customConverters[srcType]
|
||||
if !ok {
|
||||
return reflect.Value{}, nil, false
|
||||
}
|
||||
var dstType = dstReflectValueForRefer.Type()
|
||||
if dstType.Kind() == reflect.Pointer {
|
||||
// Might be **struct, which is support as designed.
|
||||
if dstType.Elem().Kind() == reflect.Pointer {
|
||||
dstType = dstType.Elem()
|
||||
}
|
||||
} else if dstReflectValueForRefer.IsValid() && dstReflectValueForRefer.CanAddr() {
|
||||
dstType = dstReflectValueForRefer.Addr().Type()
|
||||
} else {
|
||||
dstType = reflect.PointerTo(dstType)
|
||||
}
|
||||
// secondly, it searches the input parameter type map
|
||||
// and finds the result converter function by the output parameter type.
|
||||
f, ok = registeredOutTypeMap[dstType]
|
||||
if !ok {
|
||||
return reflect.Value{}, nil, false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func callCustomConverterWithRefer(
|
||||
srcReflectValue, referReflectValue reflect.Value,
|
||||
) (dstReflectValue reflect.Value, converted bool, err error) {
|
||||
registeredConverterFunc, srcType, ok := getRegisteredConverterFuncAndSrcType(srcReflectValue, referReflectValue)
|
||||
if !ok {
|
||||
return reflect.Value{}, false, nil
|
||||
}
|
||||
dstReflectValue = reflect.New(referReflectValue.Type()).Elem()
|
||||
converted, err = doCallCustomConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType)
|
||||
return
|
||||
}
|
||||
|
||||
// callCustomConverter call the custom converter. It will try some possible type.
|
||||
func callCustomConverter(srcReflectValue, dstReflectValue reflect.Value) (converted bool, err error) {
|
||||
registeredConverterFunc, srcType, ok := getRegisteredConverterFuncAndSrcType(srcReflectValue, dstReflectValue)
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
return doCallCustomConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType)
|
||||
}
|
||||
|
||||
func doCallCustomConverter(
|
||||
srcReflectValue reflect.Value,
|
||||
dstReflectValue reflect.Value,
|
||||
registeredConverterFunc converterFunc,
|
||||
srcType reflect.Type,
|
||||
) (converted bool, err error) {
|
||||
// Converter function calling.
|
||||
for srcReflectValue.Type() != srcType {
|
||||
srcReflectValue = srcReflectValue.Elem()
|
||||
}
|
||||
result := registeredConverterFunc.Call([]reflect.Value{srcReflectValue})
|
||||
if !result[1].IsNil() {
|
||||
return false, result[1].Interface().(error)
|
||||
}
|
||||
// The `result[0]` is a pointer.
|
||||
if result[0].IsNil() {
|
||||
return false, nil
|
||||
}
|
||||
var resultValue = result[0]
|
||||
for {
|
||||
if resultValue.Type() == dstReflectValue.Type() && dstReflectValue.CanSet() {
|
||||
dstReflectValue.Set(resultValue)
|
||||
converted = true
|
||||
} else if dstReflectValue.Kind() == reflect.Pointer {
|
||||
if resultValue.Type() == dstReflectValue.Elem().Type() && dstReflectValue.Elem().CanSet() {
|
||||
dstReflectValue.Elem().Set(resultValue)
|
||||
converted = true
|
||||
}
|
||||
}
|
||||
if converted {
|
||||
break
|
||||
}
|
||||
if resultValue.Kind() == reflect.Pointer {
|
||||
resultValue = resultValue.Elem()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return converted, nil
|
||||
}
|
||||
@ -6,144 +6,14 @@
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gbinary"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
|
||||
)
|
||||
|
||||
// Float32 converts `any` to float32.
|
||||
func Float32(any any) float32 {
|
||||
v, _ := doFloat32(any)
|
||||
v, _ := defaultConverter.Float32(any)
|
||||
return v
|
||||
}
|
||||
|
||||
func doFloat32(any any) (float32, error) {
|
||||
if empty.IsNil(any) {
|
||||
return 0, nil
|
||||
}
|
||||
switch value := any.(type) {
|
||||
case float32:
|
||||
return value, nil
|
||||
case float64:
|
||||
return float32(value), nil
|
||||
case []byte:
|
||||
// TODO: It might panic here for these types.
|
||||
return gbinary.DecodeToFloat32(value), nil
|
||||
default:
|
||||
rv := reflect.ValueOf(any)
|
||||
switch rv.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return float32(rv.Int()), nil
|
||||
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return float32(rv.Uint()), nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return float32(rv.Float()), nil
|
||||
case reflect.Bool:
|
||||
if rv.Bool() {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
case reflect.String:
|
||||
f, err := strconv.ParseFloat(rv.String(), 32)
|
||||
if err != nil {
|
||||
return 0, gerror.WrapCodef(
|
||||
gcode.CodeInvalidParameter, err, "converting string to float32 failed for: %v", any,
|
||||
)
|
||||
}
|
||||
return float32(f), nil
|
||||
case reflect.Ptr:
|
||||
if rv.IsNil() {
|
||||
return 0, nil
|
||||
}
|
||||
if f, ok := value.(localinterface.IFloat32); ok {
|
||||
return f.Float32(), nil
|
||||
}
|
||||
return doFloat32(rv.Elem().Interface())
|
||||
default:
|
||||
if f, ok := value.(localinterface.IFloat32); ok {
|
||||
return f.Float32(), nil
|
||||
}
|
||||
v, err := strconv.ParseFloat(String(any), 32)
|
||||
if err != nil {
|
||||
return 0, gerror.WrapCodef(
|
||||
gcode.CodeInvalidParameter, err, "converting string to float32 failed for: %v", any,
|
||||
)
|
||||
}
|
||||
return float32(v), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Float64 converts `any` to float64.
|
||||
func Float64(any any) float64 {
|
||||
v, _ := doFloat64(any)
|
||||
v, _ := defaultConverter.Float64(any)
|
||||
return v
|
||||
}
|
||||
|
||||
func doFloat64(any any) (float64, error) {
|
||||
if empty.IsNil(any) {
|
||||
return 0, nil
|
||||
}
|
||||
switch value := any.(type) {
|
||||
case float32:
|
||||
return float64(value), nil
|
||||
case float64:
|
||||
return value, nil
|
||||
case []byte:
|
||||
// TODO: It might panic here for these types.
|
||||
return gbinary.DecodeToFloat64(value), nil
|
||||
default:
|
||||
rv := reflect.ValueOf(any)
|
||||
switch rv.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return float64(rv.Int()), nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return float64(rv.Uint()), nil
|
||||
case reflect.Uintptr:
|
||||
return float64(rv.Uint()), nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
// Please Note:
|
||||
// When the type is float32 or a custom type defined based on float32,
|
||||
// switching to float64 may result in a few extra decimal places.
|
||||
return rv.Float(), nil
|
||||
case reflect.Bool:
|
||||
if rv.Bool() {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
case reflect.String:
|
||||
f, err := strconv.ParseFloat(rv.String(), 64)
|
||||
if err != nil {
|
||||
return 0, gerror.WrapCodef(
|
||||
gcode.CodeInvalidParameter, err, "converting string to float64 failed for: %v", any,
|
||||
)
|
||||
}
|
||||
return f, nil
|
||||
case reflect.Ptr:
|
||||
if rv.IsNil() {
|
||||
return 0, nil
|
||||
}
|
||||
if f, ok := value.(localinterface.IFloat64); ok {
|
||||
return f.Float64(), nil
|
||||
}
|
||||
return doFloat64(rv.Elem().Interface())
|
||||
default:
|
||||
if f, ok := value.(localinterface.IFloat64); ok {
|
||||
return f.Float64(), nil
|
||||
}
|
||||
v, err := strconv.ParseFloat(String(any), 64)
|
||||
if err != nil {
|
||||
return 0, gerror.WrapCodef(
|
||||
gcode.CodeInvalidParameter, err, "converting string to float64 failed for: %v", any,
|
||||
)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,177 +6,32 @@
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gbinary"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
|
||||
)
|
||||
|
||||
// Int converts `any` to int.
|
||||
func Int(any any) int {
|
||||
v, _ := doInt(any)
|
||||
v, _ := defaultConverter.Int(any)
|
||||
return v
|
||||
}
|
||||
|
||||
func doInt(any any) (int, error) {
|
||||
if v, ok := any.(int); ok {
|
||||
return v, nil
|
||||
}
|
||||
v, err := doInt64(any)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(v), nil
|
||||
}
|
||||
|
||||
// Int8 converts `any` to int8.
|
||||
func Int8(any any) int8 {
|
||||
v, _ := doInt8(any)
|
||||
v, _ := defaultConverter.Int8(any)
|
||||
return v
|
||||
}
|
||||
|
||||
func doInt8(any any) (int8, error) {
|
||||
if v, ok := any.(int8); ok {
|
||||
return v, nil
|
||||
}
|
||||
v, err := doInt64(any)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int8(v), nil
|
||||
}
|
||||
|
||||
// Int16 converts `any` to int16.
|
||||
func Int16(any any) int16 {
|
||||
v, _ := doInt16(any)
|
||||
v, _ := defaultConverter.Int16(any)
|
||||
return v
|
||||
}
|
||||
|
||||
func doInt16(any any) (int16, error) {
|
||||
if v, ok := any.(int16); ok {
|
||||
return v, nil
|
||||
}
|
||||
v, err := doInt64(any)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int16(v), nil
|
||||
}
|
||||
|
||||
// Int32 converts `any` to int32.
|
||||
func Int32(any any) int32 {
|
||||
v, _ := doInt32(any)
|
||||
v, _ := defaultConverter.Int32(any)
|
||||
return v
|
||||
}
|
||||
|
||||
func doInt32(any any) (int32, error) {
|
||||
if v, ok := any.(int32); ok {
|
||||
return v, nil
|
||||
}
|
||||
v, err := doInt64(any)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int32(v), nil
|
||||
}
|
||||
|
||||
// Int64 converts `any` to int64.
|
||||
func Int64(any any) int64 {
|
||||
v, _ := doInt64(any)
|
||||
v, _ := defaultConverter.Int64(any)
|
||||
return v
|
||||
}
|
||||
|
||||
func doInt64(any any) (int64, error) {
|
||||
if empty.IsNil(any) {
|
||||
return 0, nil
|
||||
}
|
||||
if v, ok := any.(int64); ok {
|
||||
return v, nil
|
||||
}
|
||||
rv := reflect.ValueOf(any)
|
||||
switch rv.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return rv.Int(), nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return int64(rv.Uint()), nil
|
||||
case reflect.Uintptr:
|
||||
return int64(rv.Uint()), nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return int64(rv.Float()), nil
|
||||
case reflect.Bool:
|
||||
if rv.Bool() {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
case reflect.Ptr:
|
||||
if rv.IsNil() {
|
||||
return 0, nil
|
||||
}
|
||||
if f, ok := any.(localinterface.IInt64); ok {
|
||||
return f.Int64(), nil
|
||||
}
|
||||
return doInt64(rv.Elem().Interface())
|
||||
case reflect.Slice:
|
||||
// TODO: It might panic here for these types.
|
||||
if rv.Type().Elem().Kind() == reflect.Uint8 {
|
||||
return gbinary.DecodeToInt64(rv.Bytes()), nil
|
||||
}
|
||||
case reflect.String:
|
||||
var (
|
||||
s = rv.String()
|
||||
isMinus = false
|
||||
)
|
||||
if len(s) > 0 {
|
||||
if s[0] == '-' {
|
||||
isMinus = true
|
||||
s = s[1:]
|
||||
} else if s[0] == '+' {
|
||||
s = s[1:]
|
||||
}
|
||||
}
|
||||
// Hexadecimal.
|
||||
if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') {
|
||||
if v, e := strconv.ParseInt(s[2:], 16, 64); e == nil {
|
||||
if isMinus {
|
||||
return -v, nil
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
// Decimal.
|
||||
if v, e := strconv.ParseInt(s, 10, 64); e == nil {
|
||||
if isMinus {
|
||||
return -v, nil
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
// Float64.
|
||||
valueInt64, err := doFloat64(s)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if math.IsNaN(valueInt64) {
|
||||
return 0, nil
|
||||
} else {
|
||||
if isMinus {
|
||||
return -int64(valueInt64), nil
|
||||
}
|
||||
return int64(valueInt64), nil
|
||||
}
|
||||
default:
|
||||
if f, ok := any.(localinterface.IInt64); ok {
|
||||
return f.Int64(), nil
|
||||
}
|
||||
}
|
||||
return 0, gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`unsupport value type for converting to int64: %v`,
|
||||
reflect.TypeOf(any),
|
||||
)
|
||||
}
|
||||
|
||||
@ -6,566 +6,42 @@
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/utils"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
type recursiveType string
|
||||
|
||||
const (
|
||||
recursiveTypeAuto recursiveType = "auto"
|
||||
recursiveTypeTrue recursiveType = "true"
|
||||
)
|
||||
|
||||
// MapOption specifies the option for map converting.
|
||||
type MapOption struct {
|
||||
// Deep marks doing Map function recursively, which means if the attribute of given converting value
|
||||
// is also a struct/*struct, it automatically calls Map function on this attribute converting it to
|
||||
// a map[string]interface{} type variable.
|
||||
Deep bool
|
||||
|
||||
// OmitEmpty ignores the attributes that has json `omitempty` tag.
|
||||
OmitEmpty bool
|
||||
|
||||
// Tags specifies the converted map key name by struct tag name.
|
||||
Tags []string
|
||||
}
|
||||
|
||||
// Map converts any variable `value` to map[string]interface{}. If the parameter `value` is not a
|
||||
// Map converts any variable `value` to map[string]any. If the parameter `value` is not a
|
||||
// map/struct/*struct type, then the conversion will fail and returns nil.
|
||||
//
|
||||
// If `value` is a struct/*struct object, the second parameter `priorityTagAndFieldName` specifies the most priority
|
||||
// priorityTagAndFieldName that will be detected, otherwise it detects the priorityTagAndFieldName in order of:
|
||||
// gconv, json, field name.
|
||||
func Map(value interface{}, option ...MapOption) map[string]interface{} {
|
||||
return doMapConvert(value, recursiveTypeAuto, false, option...)
|
||||
func Map(value any, option ...MapOption) map[string]any {
|
||||
result, _ := defaultConverter.Map(value, getUsedMapOption(option...))
|
||||
return result
|
||||
}
|
||||
|
||||
// MapDeep does Map function recursively, which means if the attribute of `value`
|
||||
// is also a struct/*struct, calls Map function on this attribute converting it to
|
||||
// a map[string]interface{} type variable.
|
||||
// a map[string]any type variable.
|
||||
// Deprecated: used Map instead.
|
||||
func MapDeep(value interface{}, tags ...string) map[string]interface{} {
|
||||
return doMapConvert(value, recursiveTypeTrue, false, MapOption{
|
||||
Deep: true,
|
||||
Tags: tags,
|
||||
func MapDeep(value any, tags ...string) map[string]any {
|
||||
result, _ := defaultConverter.Map(value, MapOption{
|
||||
Deep: true,
|
||||
OmitEmpty: false,
|
||||
Tags: tags,
|
||||
ContinueOnError: true,
|
||||
})
|
||||
}
|
||||
|
||||
// doMapConvert implements the map converting.
|
||||
// It automatically checks and converts json string to map if `value` is string/[]byte.
|
||||
//
|
||||
// TODO completely implement the recursive converting for all types, especially the map.
|
||||
func doMapConvert(value interface{}, recursive recursiveType, mustMapReturn bool, option ...MapOption) map[string]interface{} {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
// It redirects to its underlying value if it has implemented interface iVal.
|
||||
if v, ok := value.(localinterface.IVal); ok {
|
||||
value = v.Val()
|
||||
}
|
||||
var (
|
||||
usedOption = getUsedMapOption(option...)
|
||||
newTags = gtag.StructTagPriority
|
||||
)
|
||||
if usedOption.Deep {
|
||||
recursive = recursiveTypeTrue
|
||||
}
|
||||
switch len(usedOption.Tags) {
|
||||
case 0:
|
||||
// No need handling.
|
||||
case 1:
|
||||
newTags = append(strings.Split(usedOption.Tags[0], ","), gtag.StructTagPriority...)
|
||||
default:
|
||||
newTags = append(usedOption.Tags, gtag.StructTagPriority...)
|
||||
}
|
||||
// Assert the common combination of types, and finally it uses reflection.
|
||||
dataMap := make(map[string]interface{})
|
||||
switch r := value.(type) {
|
||||
case string:
|
||||
// If it is a JSON string, automatically unmarshal it!
|
||||
if len(r) > 0 && r[0] == '{' && r[len(r)-1] == '}' {
|
||||
if err := json.UnmarshalUseNumber([]byte(r), &dataMap); err != nil {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
case []byte:
|
||||
// If it is a JSON string, automatically unmarshal it!
|
||||
if len(r) > 0 && r[0] == '{' && r[len(r)-1] == '}' {
|
||||
if err := json.UnmarshalUseNumber(r, &dataMap); err != nil {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
case map[interface{}]interface{}:
|
||||
recursiveOption := usedOption
|
||||
recursiveOption.Tags = newTags
|
||||
for k, v := range r {
|
||||
dataMap[String(k)] = doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: v,
|
||||
RecursiveType: recursive,
|
||||
RecursiveOption: recursive == recursiveTypeTrue,
|
||||
Option: recursiveOption,
|
||||
},
|
||||
)
|
||||
}
|
||||
case map[interface{}]string:
|
||||
for k, v := range r {
|
||||
dataMap[String(k)] = v
|
||||
}
|
||||
case map[interface{}]int:
|
||||
for k, v := range r {
|
||||
dataMap[String(k)] = v
|
||||
}
|
||||
case map[interface{}]uint:
|
||||
for k, v := range r {
|
||||
dataMap[String(k)] = v
|
||||
}
|
||||
case map[interface{}]float32:
|
||||
for k, v := range r {
|
||||
dataMap[String(k)] = v
|
||||
}
|
||||
case map[interface{}]float64:
|
||||
for k, v := range r {
|
||||
dataMap[String(k)] = v
|
||||
}
|
||||
case map[string]bool:
|
||||
for k, v := range r {
|
||||
dataMap[k] = v
|
||||
}
|
||||
case map[string]int:
|
||||
for k, v := range r {
|
||||
dataMap[k] = v
|
||||
}
|
||||
case map[string]uint:
|
||||
for k, v := range r {
|
||||
dataMap[k] = v
|
||||
}
|
||||
case map[string]float32:
|
||||
for k, v := range r {
|
||||
dataMap[k] = v
|
||||
}
|
||||
case map[string]float64:
|
||||
for k, v := range r {
|
||||
dataMap[k] = v
|
||||
}
|
||||
case map[string]string:
|
||||
for k, v := range r {
|
||||
dataMap[k] = v
|
||||
}
|
||||
case map[string]interface{}:
|
||||
if recursive == recursiveTypeTrue {
|
||||
recursiveOption := usedOption
|
||||
recursiveOption.Tags = newTags
|
||||
// A copy of current map.
|
||||
for k, v := range r {
|
||||
dataMap[k] = doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: v,
|
||||
RecursiveType: recursive,
|
||||
RecursiveOption: recursive == recursiveTypeTrue,
|
||||
Option: recursiveOption,
|
||||
},
|
||||
)
|
||||
}
|
||||
} else {
|
||||
// It returns the map directly without any changing.
|
||||
return r
|
||||
}
|
||||
case map[int]interface{}:
|
||||
recursiveOption := usedOption
|
||||
recursiveOption.Tags = newTags
|
||||
for k, v := range r {
|
||||
dataMap[String(k)] = doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: v,
|
||||
RecursiveType: recursive,
|
||||
RecursiveOption: recursive == recursiveTypeTrue,
|
||||
Option: recursiveOption,
|
||||
},
|
||||
)
|
||||
}
|
||||
case map[int]string:
|
||||
for k, v := range r {
|
||||
dataMap[String(k)] = v
|
||||
}
|
||||
case map[uint]string:
|
||||
for k, v := range r {
|
||||
dataMap[String(k)] = v
|
||||
}
|
||||
|
||||
default:
|
||||
// Not a common type, it then uses reflection for conversion.
|
||||
var reflectValue reflect.Value
|
||||
if v, ok := value.(reflect.Value); ok {
|
||||
reflectValue = v
|
||||
} else {
|
||||
reflectValue = reflect.ValueOf(value)
|
||||
}
|
||||
reflectKind := reflectValue.Kind()
|
||||
// If it is a pointer, we should find its real data type.
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch reflectKind {
|
||||
// If `value` is type of array, it converts the value of even number index as its key and
|
||||
// the value of odd number index as its corresponding value, for example:
|
||||
// []string{"k1","v1","k2","v2"} => map[string]interface{}{"k1":"v1", "k2":"v2"}
|
||||
// []string{"k1","v1","k2"} => map[string]interface{}{"k1":"v1", "k2":nil}
|
||||
case reflect.Slice, reflect.Array:
|
||||
length := reflectValue.Len()
|
||||
for i := 0; i < length; i += 2 {
|
||||
if i+1 < length {
|
||||
dataMap[String(reflectValue.Index(i).Interface())] = reflectValue.Index(i + 1).Interface()
|
||||
} else {
|
||||
dataMap[String(reflectValue.Index(i).Interface())] = nil
|
||||
}
|
||||
}
|
||||
case reflect.Map, reflect.Struct, reflect.Interface:
|
||||
recursiveOption := usedOption
|
||||
recursiveOption.Tags = newTags
|
||||
convertedValue := doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: true,
|
||||
Value: value,
|
||||
RecursiveType: recursive,
|
||||
RecursiveOption: recursive == recursiveTypeTrue,
|
||||
Option: recursiveOption,
|
||||
MustMapReturn: mustMapReturn,
|
||||
},
|
||||
)
|
||||
if m, ok := convertedValue.(map[string]interface{}); ok {
|
||||
return m
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return dataMap
|
||||
}
|
||||
|
||||
func getUsedMapOption(option ...MapOption) MapOption {
|
||||
var usedOption MapOption
|
||||
if len(option) > 0 {
|
||||
usedOption = option[0]
|
||||
}
|
||||
return usedOption
|
||||
}
|
||||
|
||||
type doMapConvertForMapOrStructValueInput struct {
|
||||
IsRoot bool // It returns directly if it is not root and with no recursive converting.
|
||||
Value interface{} // Current operation value.
|
||||
RecursiveType recursiveType // The type from top function entry.
|
||||
RecursiveOption bool // Whether convert recursively for `current` operation.
|
||||
Option MapOption // Map converting option.
|
||||
MustMapReturn bool // Must return map instead of Value when empty.
|
||||
}
|
||||
|
||||
func doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) interface{} {
|
||||
if !in.IsRoot && !in.RecursiveOption {
|
||||
return in.Value
|
||||
}
|
||||
|
||||
var reflectValue reflect.Value
|
||||
if v, ok := in.Value.(reflect.Value); ok {
|
||||
reflectValue = v
|
||||
in.Value = v.Interface()
|
||||
} else {
|
||||
reflectValue = reflect.ValueOf(in.Value)
|
||||
}
|
||||
reflectKind := reflectValue.Kind()
|
||||
// If it is a pointer, we should find its real data type.
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Map:
|
||||
var (
|
||||
mapIter = reflectValue.MapRange()
|
||||
dataMap = make(map[string]interface{})
|
||||
)
|
||||
for mapIter.Next() {
|
||||
var (
|
||||
mapKeyValue = mapIter.Value()
|
||||
mapValue interface{}
|
||||
)
|
||||
switch {
|
||||
case mapKeyValue.IsZero():
|
||||
if utils.CanCallIsNil(mapKeyValue) && mapKeyValue.IsNil() {
|
||||
// quick check for nil value.
|
||||
mapValue = nil
|
||||
} else {
|
||||
// in case of:
|
||||
// exception recovered: reflect: call of reflect.Value.Interface on zero Value
|
||||
mapValue = reflect.New(mapKeyValue.Type()).Elem().Interface()
|
||||
}
|
||||
default:
|
||||
mapValue = mapKeyValue.Interface()
|
||||
}
|
||||
dataMap[String(mapIter.Key().Interface())] = doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: mapValue,
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
|
||||
Option: in.Option,
|
||||
},
|
||||
)
|
||||
}
|
||||
return dataMap
|
||||
|
||||
case reflect.Struct:
|
||||
var dataMap = make(map[string]interface{})
|
||||
// Map converting interface check.
|
||||
if v, ok := in.Value.(localinterface.IMapStrAny); ok {
|
||||
// Value copy, in case of concurrent safety.
|
||||
for mapK, mapV := range v.MapStrAny() {
|
||||
if in.RecursiveOption {
|
||||
dataMap[mapK] = doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: mapV,
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
|
||||
Option: in.Option,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
dataMap[mapK] = mapV
|
||||
}
|
||||
}
|
||||
if len(dataMap) > 0 {
|
||||
return dataMap
|
||||
}
|
||||
}
|
||||
// Using reflect for converting.
|
||||
var (
|
||||
rtField reflect.StructField
|
||||
rvField reflect.Value
|
||||
reflectType = reflectValue.Type() // attribute value type.
|
||||
mapKey = "" // mapKey may be the tag name or the struct attribute name.
|
||||
)
|
||||
for i := 0; i < reflectValue.NumField(); i++ {
|
||||
rtField = reflectType.Field(i)
|
||||
rvField = reflectValue.Field(i)
|
||||
// Only convert the public attributes.
|
||||
fieldName := rtField.Name
|
||||
if !utils.IsLetterUpper(fieldName[0]) {
|
||||
continue
|
||||
}
|
||||
mapKey = ""
|
||||
fieldTag := rtField.Tag
|
||||
for _, tag := range in.Option.Tags {
|
||||
if mapKey = fieldTag.Get(tag); mapKey != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
if mapKey == "" {
|
||||
mapKey = fieldName
|
||||
} else {
|
||||
// Support json tag feature: -, omitempty
|
||||
mapKey = strings.TrimSpace(mapKey)
|
||||
if mapKey == "-" {
|
||||
continue
|
||||
}
|
||||
array := strings.Split(mapKey, ",")
|
||||
if len(array) > 1 {
|
||||
switch strings.TrimSpace(array[1]) {
|
||||
case "omitempty":
|
||||
if in.Option.OmitEmpty && empty.IsEmpty(rvField.Interface()) {
|
||||
continue
|
||||
} else {
|
||||
mapKey = strings.TrimSpace(array[0])
|
||||
}
|
||||
default:
|
||||
mapKey = strings.TrimSpace(array[0])
|
||||
}
|
||||
}
|
||||
if mapKey == "" {
|
||||
mapKey = fieldName
|
||||
}
|
||||
}
|
||||
if in.RecursiveOption || rtField.Anonymous {
|
||||
// Do map converting recursively.
|
||||
var (
|
||||
rvAttrField = rvField
|
||||
rvAttrKind = rvField.Kind()
|
||||
)
|
||||
if rvAttrKind == reflect.Ptr {
|
||||
rvAttrField = rvField.Elem()
|
||||
rvAttrKind = rvAttrField.Kind()
|
||||
}
|
||||
switch rvAttrKind {
|
||||
case reflect.Struct:
|
||||
// Embedded struct and has no fields, just ignores it.
|
||||
// Eg: gmeta.Meta
|
||||
if rvAttrField.Type().NumField() == 0 {
|
||||
continue
|
||||
}
|
||||
var (
|
||||
hasNoTag = mapKey == fieldName
|
||||
// DO NOT use rvAttrField.Interface() here,
|
||||
// as it might be changed from pointer to struct.
|
||||
rvInterface = rvField.Interface()
|
||||
)
|
||||
switch {
|
||||
case hasNoTag && rtField.Anonymous:
|
||||
// It means this attribute field has no tag.
|
||||
// Overwrite the attribute with sub-struct attribute fields.
|
||||
anonymousValue := doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: rvInterface,
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: true,
|
||||
Option: in.Option,
|
||||
})
|
||||
if m, ok := anonymousValue.(map[string]interface{}); ok {
|
||||
for k, v := range m {
|
||||
dataMap[k] = v
|
||||
}
|
||||
} else {
|
||||
dataMap[mapKey] = rvInterface
|
||||
}
|
||||
|
||||
// It means this attribute field has desired tag.
|
||||
case !hasNoTag && rtField.Anonymous:
|
||||
dataMap[mapKey] = doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: rvInterface,
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: true,
|
||||
Option: in.Option,
|
||||
})
|
||||
|
||||
default:
|
||||
dataMap[mapKey] = doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: rvInterface,
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
|
||||
Option: in.Option,
|
||||
})
|
||||
}
|
||||
|
||||
// The struct attribute is type of slice.
|
||||
case reflect.Array, reflect.Slice:
|
||||
length := rvAttrField.Len()
|
||||
if length == 0 {
|
||||
dataMap[mapKey] = rvAttrField.Interface()
|
||||
break
|
||||
}
|
||||
array := make([]interface{}, length)
|
||||
for arrayIndex := 0; arrayIndex < length; arrayIndex++ {
|
||||
array[arrayIndex] = doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: rvAttrField.Index(arrayIndex).Interface(),
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
|
||||
Option: in.Option,
|
||||
},
|
||||
)
|
||||
}
|
||||
dataMap[mapKey] = array
|
||||
case reflect.Map:
|
||||
var (
|
||||
mapIter = rvAttrField.MapRange()
|
||||
nestedMap = make(map[string]interface{})
|
||||
)
|
||||
for mapIter.Next() {
|
||||
nestedMap[String(mapIter.Key().Interface())] = doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: mapIter.Value().Interface(),
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
|
||||
Option: in.Option,
|
||||
},
|
||||
)
|
||||
}
|
||||
dataMap[mapKey] = nestedMap
|
||||
default:
|
||||
if rvField.IsValid() {
|
||||
dataMap[mapKey] = reflectValue.Field(i).Interface()
|
||||
} else {
|
||||
dataMap[mapKey] = nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No recursive map value converting
|
||||
if rvField.IsValid() {
|
||||
dataMap[mapKey] = reflectValue.Field(i).Interface()
|
||||
} else {
|
||||
dataMap[mapKey] = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if !in.MustMapReturn && len(dataMap) == 0 {
|
||||
return in.Value
|
||||
}
|
||||
return dataMap
|
||||
|
||||
// The given value is type of slice.
|
||||
case reflect.Array, reflect.Slice:
|
||||
length := reflectValue.Len()
|
||||
if length == 0 {
|
||||
break
|
||||
}
|
||||
array := make([]interface{}, reflectValue.Len())
|
||||
for i := 0; i < length; i++ {
|
||||
array[i] = doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: reflectValue.Index(i).Interface(),
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: in.RecursiveType == recursiveTypeTrue,
|
||||
Option: in.Option,
|
||||
})
|
||||
}
|
||||
return array
|
||||
|
||||
default:
|
||||
}
|
||||
return in.Value
|
||||
return result
|
||||
}
|
||||
|
||||
// MapStrStr converts `value` to map[string]string.
|
||||
// Note that there might be data copy for this map type converting.
|
||||
func MapStrStr(value interface{}, option ...MapOption) map[string]string {
|
||||
if r, ok := value.(map[string]string); ok {
|
||||
return r
|
||||
}
|
||||
m := Map(value, option...)
|
||||
if len(m) > 0 {
|
||||
vMap := make(map[string]string, len(m))
|
||||
for k, v := range m {
|
||||
vMap[k] = String(v)
|
||||
}
|
||||
return vMap
|
||||
}
|
||||
return nil
|
||||
func MapStrStr(value any, option ...MapOption) map[string]string {
|
||||
result, _ := defaultConverter.MapStrStr(value, getUsedMapOption(option...))
|
||||
return result
|
||||
}
|
||||
|
||||
// MapStrStrDeep converts `value` to map[string]string recursively.
|
||||
// Note that there might be data copy for this map type converting.
|
||||
// Deprecated: used MapStrStr instead.
|
||||
func MapStrStrDeep(value interface{}, tags ...string) map[string]string {
|
||||
func MapStrStrDeep(value any, tags ...string) map[string]string {
|
||||
if r, ok := value.(map[string]string); ok {
|
||||
return r
|
||||
}
|
||||
@ -579,3 +55,13 @@ func MapStrStrDeep(value interface{}, tags ...string) map[string]string {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getUsedMapOption(option ...MapOption) MapOption {
|
||||
var usedOption = MapOption{
|
||||
ContinueOnError: true,
|
||||
}
|
||||
if len(option) > 0 {
|
||||
usedOption = option[0]
|
||||
}
|
||||
return usedOption
|
||||
}
|
||||
|
||||
@ -9,72 +9,42 @@ package gconv
|
||||
import "github.com/gogf/gf/v2/internal/json"
|
||||
|
||||
// SliceMap is alias of Maps.
|
||||
func SliceMap(any interface{}, option ...MapOption) []map[string]interface{} {
|
||||
func SliceMap(any any, option ...MapOption) []map[string]any {
|
||||
return Maps(any, option...)
|
||||
}
|
||||
|
||||
// SliceMapDeep is alias of MapsDeep.
|
||||
// Deprecated: used SliceMap instead.
|
||||
func SliceMapDeep(any interface{}) []map[string]interface{} {
|
||||
func SliceMapDeep(any any) []map[string]any {
|
||||
return MapsDeep(any)
|
||||
}
|
||||
|
||||
// Maps converts `value` to []map[string]interface{}.
|
||||
// Maps converts `value` to []map[string]any.
|
||||
// Note that it automatically checks and converts json string to []map if `value` is string/[]byte.
|
||||
func Maps(value interface{}, option ...MapOption) []map[string]interface{} {
|
||||
if value == nil {
|
||||
return nil
|
||||
func Maps(value any, option ...MapOption) []map[string]any {
|
||||
mapOption := MapOption{
|
||||
ContinueOnError: true,
|
||||
}
|
||||
switch r := value.(type) {
|
||||
case string:
|
||||
list := make([]map[string]interface{}, 0)
|
||||
if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' {
|
||||
if err := json.UnmarshalUseNumber([]byte(r), &list); err != nil {
|
||||
return nil
|
||||
}
|
||||
return list
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
case []byte:
|
||||
list := make([]map[string]interface{}, 0)
|
||||
if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' {
|
||||
if err := json.UnmarshalUseNumber(r, &list); err != nil {
|
||||
return nil
|
||||
}
|
||||
return list
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
case []map[string]interface{}:
|
||||
return r
|
||||
|
||||
default:
|
||||
array := Interfaces(value)
|
||||
if len(array) == 0 {
|
||||
return nil
|
||||
}
|
||||
list := make([]map[string]interface{}, len(array))
|
||||
for k, v := range array {
|
||||
list[k] = Map(v, option...)
|
||||
}
|
||||
return list
|
||||
if len(option) > 0 {
|
||||
mapOption = option[0]
|
||||
}
|
||||
result, _ := defaultConverter.SliceMap(value, SliceOption{
|
||||
ContinueOnError: true,
|
||||
}, mapOption)
|
||||
return result
|
||||
}
|
||||
|
||||
// MapsDeep converts `value` to []map[string]interface{} recursively.
|
||||
// MapsDeep converts `value` to []map[string]any recursively.
|
||||
//
|
||||
// TODO completely implement the recursive converting for all types.
|
||||
// Deprecated: used Maps instead.
|
||||
func MapsDeep(value interface{}, tags ...string) []map[string]interface{} {
|
||||
func MapsDeep(value any, tags ...string) []map[string]any {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
switch r := value.(type) {
|
||||
case string:
|
||||
list := make([]map[string]interface{}, 0)
|
||||
list := make([]map[string]any, 0)
|
||||
if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' {
|
||||
if err := json.UnmarshalUseNumber([]byte(r), &list); err != nil {
|
||||
return nil
|
||||
@ -85,7 +55,7 @@ func MapsDeep(value interface{}, tags ...string) []map[string]interface{} {
|
||||
}
|
||||
|
||||
case []byte:
|
||||
list := make([]map[string]interface{}, 0)
|
||||
list := make([]map[string]any, 0)
|
||||
if len(r) > 0 && r[0] == '[' && r[len(r)-1] == ']' {
|
||||
if err := json.UnmarshalUseNumber(r, &list); err != nil {
|
||||
return nil
|
||||
@ -95,8 +65,8 @@ func MapsDeep(value interface{}, tags ...string) []map[string]interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
case []map[string]interface{}:
|
||||
list := make([]map[string]interface{}, len(r))
|
||||
case []map[string]any:
|
||||
list := make([]map[string]any, len(r))
|
||||
for k, v := range r {
|
||||
list[k] = MapDeep(v, tags...)
|
||||
}
|
||||
@ -107,7 +77,7 @@ func MapsDeep(value interface{}, tags ...string) []map[string]interface{} {
|
||||
if len(array) == 0 {
|
||||
return nil
|
||||
}
|
||||
list := make([]map[string]interface{}, len(array))
|
||||
list := make([]map[string]any, len(array))
|
||||
for k, v := range array {
|
||||
list[k] = MapDeep(v, tags...)
|
||||
}
|
||||
|
||||
@ -6,126 +6,9 @@
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
)
|
||||
|
||||
// MapToMap converts any map type variable `params` to another map type variable `pointer`
|
||||
// using reflect.
|
||||
// See doMapToMap.
|
||||
func MapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) error {
|
||||
func MapToMap(params any, pointer any, mapping ...map[string]string) error {
|
||||
return Scan(params, pointer, mapping...)
|
||||
}
|
||||
|
||||
// doMapToMap converts any map type variable `params` to another map type variable `pointer`.
|
||||
//
|
||||
// The parameter `params` can be any type of map, like:
|
||||
// map[string]string, map[string]struct, map[string]*struct, reflect.Value, etc.
|
||||
//
|
||||
// The parameter `pointer` should be type of *map, like:
|
||||
// map[int]string, map[string]struct, map[string]*struct, reflect.Value, etc.
|
||||
//
|
||||
// The optional parameter `mapping` is used for struct attribute to map key mapping, which makes
|
||||
// sense only if the items of original map `params` is type struct.
|
||||
func doMapToMap(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
var (
|
||||
paramsRv reflect.Value
|
||||
paramsKind reflect.Kind
|
||||
keyToAttributeNameMapping map[string]string
|
||||
)
|
||||
if len(mapping) > 0 {
|
||||
keyToAttributeNameMapping = mapping[0]
|
||||
}
|
||||
if v, ok := params.(reflect.Value); ok {
|
||||
paramsRv = v
|
||||
} else {
|
||||
paramsRv = reflect.ValueOf(params)
|
||||
}
|
||||
paramsKind = paramsRv.Kind()
|
||||
if paramsKind == reflect.Ptr {
|
||||
paramsRv = paramsRv.Elem()
|
||||
paramsKind = paramsRv.Kind()
|
||||
}
|
||||
if paramsKind != reflect.Map {
|
||||
return doMapToMap(Map(params), pointer, mapping...)
|
||||
}
|
||||
// Empty params map, no need continue.
|
||||
if paramsRv.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
var pointerRv reflect.Value
|
||||
if v, ok := pointer.(reflect.Value); ok {
|
||||
pointerRv = v
|
||||
} else {
|
||||
pointerRv = reflect.ValueOf(pointer)
|
||||
}
|
||||
pointerKind := pointerRv.Kind()
|
||||
for pointerKind == reflect.Ptr {
|
||||
pointerRv = pointerRv.Elem()
|
||||
pointerKind = pointerRv.Kind()
|
||||
}
|
||||
if pointerKind != reflect.Map {
|
||||
return gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`destination pointer should be type of *map, but got: %s`,
|
||||
pointerKind,
|
||||
)
|
||||
}
|
||||
defer func() {
|
||||
// Catch the panic, especially the reflection operation panics.
|
||||
if exception := recover(); exception != nil {
|
||||
if v, ok := exception.(error); ok && gerror.HasStack(v) {
|
||||
err = v
|
||||
} else {
|
||||
err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception)
|
||||
}
|
||||
}
|
||||
}()
|
||||
var (
|
||||
paramsKeys = paramsRv.MapKeys()
|
||||
pointerKeyType = pointerRv.Type().Key()
|
||||
pointerValueType = pointerRv.Type().Elem()
|
||||
pointerValueKind = pointerValueType.Kind()
|
||||
dataMap = reflect.MakeMapWithSize(pointerRv.Type(), len(paramsKeys))
|
||||
)
|
||||
// Retrieve the true element type of target map.
|
||||
if pointerValueKind == reflect.Ptr {
|
||||
pointerValueKind = pointerValueType.Elem().Kind()
|
||||
}
|
||||
for _, key := range paramsKeys {
|
||||
mapValue := reflect.New(pointerValueType).Elem()
|
||||
switch pointerValueKind {
|
||||
case reflect.Map, reflect.Struct:
|
||||
if err = doStruct(
|
||||
paramsRv.MapIndex(key).Interface(), mapValue, keyToAttributeNameMapping, "",
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
mapValue.Set(
|
||||
reflect.ValueOf(
|
||||
doConvert(doConvertInput{
|
||||
FromValue: paramsRv.MapIndex(key).Interface(),
|
||||
ToTypeName: pointerValueType.String(),
|
||||
ReferValue: mapValue,
|
||||
Extra: nil,
|
||||
}),
|
||||
),
|
||||
)
|
||||
}
|
||||
var mapKey = reflect.ValueOf(
|
||||
doConvert(doConvertInput{
|
||||
FromValue: key.Interface(),
|
||||
ToTypeName: pointerKeyType.Name(),
|
||||
ReferValue: reflect.New(pointerKeyType).Elem().Interface(),
|
||||
Extra: nil,
|
||||
}),
|
||||
)
|
||||
dataMap.SetMapIndex(mapKey, mapValue)
|
||||
}
|
||||
pointerRv.Set(dataMap)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -6,121 +6,8 @@
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
)
|
||||
|
||||
// MapToMaps converts any slice type variable `params` to another map slice type variable `pointer`.
|
||||
// See doMapToMaps.
|
||||
func MapToMaps(params interface{}, pointer interface{}, mapping ...map[string]string) error {
|
||||
func MapToMaps(params any, pointer any, mapping ...map[string]string) error {
|
||||
return Scan(params, pointer, mapping...)
|
||||
}
|
||||
|
||||
// doMapToMaps converts any map type variable `params` to another map slice variable `pointer`.
|
||||
//
|
||||
// The parameter `params` can be type of []map, []*map, []struct, []*struct.
|
||||
//
|
||||
// The parameter `pointer` should be type of []map, []*map.
|
||||
//
|
||||
// The optional parameter `mapping` is used for struct attribute to map key mapping, which makes
|
||||
// sense only if the item of `params` is type struct.
|
||||
func doMapToMaps(params interface{}, pointer interface{}, paramKeyToAttrMap ...map[string]string) (err error) {
|
||||
// Params and its element type check.
|
||||
var (
|
||||
paramsRv reflect.Value
|
||||
paramsKind reflect.Kind
|
||||
)
|
||||
if v, ok := params.(reflect.Value); ok {
|
||||
paramsRv = v
|
||||
} else {
|
||||
paramsRv = reflect.ValueOf(params)
|
||||
}
|
||||
paramsKind = paramsRv.Kind()
|
||||
if paramsKind == reflect.Ptr {
|
||||
paramsRv = paramsRv.Elem()
|
||||
paramsKind = paramsRv.Kind()
|
||||
}
|
||||
if paramsKind != reflect.Array && paramsKind != reflect.Slice {
|
||||
return gerror.NewCode(
|
||||
gcode.CodeInvalidParameter,
|
||||
"params should be type of slice, example: []map/[]*map/[]struct/[]*struct",
|
||||
)
|
||||
}
|
||||
var (
|
||||
paramsElem = paramsRv.Type().Elem()
|
||||
paramsElemKind = paramsElem.Kind()
|
||||
)
|
||||
if paramsElemKind == reflect.Ptr {
|
||||
paramsElem = paramsElem.Elem()
|
||||
paramsElemKind = paramsElem.Kind()
|
||||
}
|
||||
if paramsElemKind != reflect.Map &&
|
||||
paramsElemKind != reflect.Struct &&
|
||||
paramsElemKind != reflect.Interface {
|
||||
return gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
"params element should be type of map/*map/struct/*struct, but got: %s",
|
||||
paramsElemKind,
|
||||
)
|
||||
}
|
||||
// Empty slice, no need continue.
|
||||
if paramsRv.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
// Pointer and its element type check.
|
||||
var (
|
||||
pointerRv = reflect.ValueOf(pointer)
|
||||
pointerKind = pointerRv.Kind()
|
||||
)
|
||||
for pointerKind == reflect.Ptr {
|
||||
pointerRv = pointerRv.Elem()
|
||||
pointerKind = pointerRv.Kind()
|
||||
}
|
||||
if pointerKind != reflect.Array && pointerKind != reflect.Slice {
|
||||
return gerror.NewCode(gcode.CodeInvalidParameter, "pointer should be type of *[]map/*[]*map")
|
||||
}
|
||||
var (
|
||||
pointerElemType = pointerRv.Type().Elem()
|
||||
pointerElemKind = pointerElemType.Kind()
|
||||
)
|
||||
if pointerElemKind == reflect.Ptr {
|
||||
pointerElemKind = pointerElemType.Elem().Kind()
|
||||
}
|
||||
if pointerElemKind != reflect.Map {
|
||||
return gerror.NewCode(gcode.CodeInvalidParameter, "pointer element should be type of map/*map")
|
||||
}
|
||||
defer func() {
|
||||
// Catch the panic, especially the reflection operation panics.
|
||||
if exception := recover(); exception != nil {
|
||||
if v, ok := exception.(error); ok && gerror.HasStack(v) {
|
||||
err = v
|
||||
} else {
|
||||
err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception)
|
||||
}
|
||||
}
|
||||
}()
|
||||
var (
|
||||
pointerSlice = reflect.MakeSlice(pointerRv.Type(), paramsRv.Len(), paramsRv.Len())
|
||||
)
|
||||
for i := 0; i < paramsRv.Len(); i++ {
|
||||
var item reflect.Value
|
||||
if pointerElemType.Kind() == reflect.Ptr {
|
||||
item = reflect.New(pointerElemType.Elem())
|
||||
if err = MapToMap(paramsRv.Index(i).Interface(), item, paramKeyToAttrMap...); err != nil {
|
||||
return err
|
||||
}
|
||||
pointerSlice.Index(i).Set(item)
|
||||
} else {
|
||||
item = reflect.New(pointerElemType)
|
||||
if err = MapToMap(paramsRv.Index(i).Interface(), item, paramKeyToAttrMap...); err != nil {
|
||||
return err
|
||||
}
|
||||
pointerSlice.Index(i).Set(item.Elem())
|
||||
}
|
||||
}
|
||||
pointerRv.Set(pointerSlice)
|
||||
return
|
||||
}
|
||||
|
||||
@ -6,15 +6,6 @@
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
|
||||
)
|
||||
|
||||
// Scan automatically checks the type of `pointer` and converts `params` to `pointer`.
|
||||
// It supports various types of parameter conversions, including:
|
||||
// 1. Basic types (int, string, float, etc.)
|
||||
@ -26,321 +17,11 @@ import (
|
||||
// The `paramKeyToAttrMap` parameter is used for mapping between attribute names and parameter keys.
|
||||
// TODO: change `paramKeyToAttrMap` to `ScanOption` to be more scalable; add `DeepCopy` option for `ScanOption`.
|
||||
func Scan(srcValue any, dstPointer any, paramKeyToAttrMap ...map[string]string) (err error) {
|
||||
// Check if srcValue is nil, in which case no conversion is needed
|
||||
if srcValue == nil {
|
||||
return nil
|
||||
option := ScanOption{
|
||||
ContinueOnError: true,
|
||||
}
|
||||
// Check if dstPointer is nil, which is an invalid parameter
|
||||
if dstPointer == nil {
|
||||
return gerror.NewCode(
|
||||
gcode.CodeInvalidParameter,
|
||||
`destination pointer should not be nil`,
|
||||
)
|
||||
}
|
||||
|
||||
// Get the reflection type and value of dstPointer
|
||||
var (
|
||||
dstPointerReflectType reflect.Type
|
||||
dstPointerReflectValue reflect.Value
|
||||
)
|
||||
if v, ok := dstPointer.(reflect.Value); ok {
|
||||
dstPointerReflectValue = v
|
||||
dstPointerReflectType = v.Type()
|
||||
} else {
|
||||
dstPointerReflectValue = reflect.ValueOf(dstPointer)
|
||||
// Do not use dstPointerReflectValue.Type() as dstPointerReflectValue might be zero
|
||||
dstPointerReflectType = reflect.TypeOf(dstPointer)
|
||||
}
|
||||
|
||||
// Validate the kind of dstPointer
|
||||
var dstPointerReflectKind = dstPointerReflectType.Kind()
|
||||
if dstPointerReflectKind != reflect.Ptr {
|
||||
// If dstPointer is not a pointer, try to get its address
|
||||
if dstPointerReflectValue.CanAddr() {
|
||||
dstPointerReflectValue = dstPointerReflectValue.Addr()
|
||||
dstPointerReflectType = dstPointerReflectValue.Type()
|
||||
dstPointerReflectKind = dstPointerReflectType.Kind()
|
||||
} else {
|
||||
// If dstPointer is not a pointer and cannot be addressed, return an error
|
||||
return gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`destination pointer should be type of pointer, but got type: %v`,
|
||||
dstPointerReflectType,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Get the reflection value of srcValue
|
||||
var srcValueReflectValue reflect.Value
|
||||
if v, ok := srcValue.(reflect.Value); ok {
|
||||
srcValueReflectValue = v
|
||||
} else {
|
||||
srcValueReflectValue = reflect.ValueOf(srcValue)
|
||||
}
|
||||
|
||||
// Get the element type and kind of dstPointer
|
||||
var (
|
||||
dstPointerReflectValueElem = dstPointerReflectValue.Elem()
|
||||
dstPointerReflectValueElemKind = dstPointerReflectValueElem.Kind()
|
||||
)
|
||||
// Handle multiple level pointers
|
||||
if dstPointerReflectValueElemKind == reflect.Ptr {
|
||||
if dstPointerReflectValueElem.IsNil() {
|
||||
// Create a new value for the pointer dereference
|
||||
nextLevelPtr := reflect.New(dstPointerReflectValueElem.Type().Elem())
|
||||
// Recursively scan into the dereferenced pointer
|
||||
if err = Scan(srcValueReflectValue, nextLevelPtr, paramKeyToAttrMap...); err == nil {
|
||||
dstPointerReflectValueElem.Set(nextLevelPtr)
|
||||
}
|
||||
return
|
||||
}
|
||||
return Scan(srcValueReflectValue, dstPointerReflectValueElem, paramKeyToAttrMap...)
|
||||
}
|
||||
|
||||
// Check if srcValue and dstPointer are the same type, in which case direct assignment can be performed
|
||||
if ok := doConvertWithTypeCheck(srcValueReflectValue, dstPointerReflectValueElem); ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handle different destination types
|
||||
switch dstPointerReflectValueElemKind {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
// Convert to int type
|
||||
dstPointerReflectValueElem.SetInt(Int64(srcValue))
|
||||
return nil
|
||||
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
// Convert to uint type
|
||||
dstPointerReflectValueElem.SetUint(Uint64(srcValue))
|
||||
return nil
|
||||
|
||||
case reflect.Float32, reflect.Float64:
|
||||
// Convert to float type
|
||||
dstPointerReflectValueElem.SetFloat(Float64(srcValue))
|
||||
return nil
|
||||
|
||||
case reflect.String:
|
||||
// Convert to string type
|
||||
dstPointerReflectValueElem.SetString(String(srcValue))
|
||||
return nil
|
||||
|
||||
case reflect.Bool:
|
||||
// Convert to bool type
|
||||
dstPointerReflectValueElem.SetBool(Bool(srcValue))
|
||||
return nil
|
||||
|
||||
case reflect.Slice:
|
||||
// Handle slice type conversion
|
||||
var (
|
||||
dstElemType = dstPointerReflectValueElem.Type().Elem()
|
||||
dstElemKind = dstElemType.Kind()
|
||||
)
|
||||
// The slice element might be a pointer type
|
||||
if dstElemKind == reflect.Ptr {
|
||||
dstElemType = dstElemType.Elem()
|
||||
dstElemKind = dstElemType.Kind()
|
||||
}
|
||||
// Special handling for struct or map slice elements
|
||||
if dstElemKind == reflect.Struct || dstElemKind == reflect.Map {
|
||||
return doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...)
|
||||
}
|
||||
// Handle basic type slice conversions
|
||||
var srcValueReflectValueKind = srcValueReflectValue.Kind()
|
||||
if srcValueReflectValueKind == reflect.Slice || srcValueReflectValueKind == reflect.Array {
|
||||
var (
|
||||
srcLen = srcValueReflectValue.Len()
|
||||
newSlice = reflect.MakeSlice(dstPointerReflectValueElem.Type(), srcLen, srcLen)
|
||||
)
|
||||
for i := 0; i < srcLen; i++ {
|
||||
srcElem := srcValueReflectValue.Index(i).Interface()
|
||||
switch dstElemType.Kind() {
|
||||
case reflect.String:
|
||||
newSlice.Index(i).SetString(String(srcElem))
|
||||
case reflect.Int:
|
||||
newSlice.Index(i).SetInt(Int64(srcElem))
|
||||
case reflect.Int64:
|
||||
newSlice.Index(i).SetInt(Int64(srcElem))
|
||||
case reflect.Float64:
|
||||
newSlice.Index(i).SetFloat(Float64(srcElem))
|
||||
case reflect.Bool:
|
||||
newSlice.Index(i).SetBool(Bool(srcElem))
|
||||
default:
|
||||
return Scan(
|
||||
srcElem, newSlice.Index(i).Addr().Interface(), paramKeyToAttrMap...,
|
||||
)
|
||||
}
|
||||
}
|
||||
dstPointerReflectValueElem.Set(newSlice)
|
||||
return nil
|
||||
}
|
||||
return doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...)
|
||||
|
||||
default:
|
||||
// Handle complex types (structs, maps, etc.)
|
||||
return doScanForComplicatedTypes(srcValue, dstPointer, dstPointerReflectType, paramKeyToAttrMap...)
|
||||
}
|
||||
}
|
||||
|
||||
// doScanForComplicatedTypes handles the scanning of complex data types.
|
||||
// It supports converting between maps, structs, and slices of these types.
|
||||
// The function first attempts JSON conversion, then falls back to specific type handling.
|
||||
//
|
||||
// It supports `pointer` in type of `*map/*[]map/*[]*map/*struct/**struct/*[]struct/*[]*struct` for converting.
|
||||
//
|
||||
// Parameters:
|
||||
// - srcValue: The source value to convert from
|
||||
// - dstPointer: The destination pointer to convert to
|
||||
// - dstPointerReflectType: The reflection type of the destination pointer
|
||||
// - paramKeyToAttrMap: Optional mapping between parameter keys and struct attribute names
|
||||
func doScanForComplicatedTypes(
|
||||
srcValue, dstPointer any,
|
||||
dstPointerReflectType reflect.Type,
|
||||
paramKeyToAttrMap ...map[string]string,
|
||||
) error {
|
||||
// Try JSON conversion first
|
||||
ok, err := doConvertWithJsonCheck(srcValue, dstPointer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handle specific type conversions
|
||||
var (
|
||||
dstPointerReflectTypeElem = dstPointerReflectType.Elem()
|
||||
dstPointerReflectTypeElemKind = dstPointerReflectTypeElem.Kind()
|
||||
keyToAttributeNameMapping map[string]string
|
||||
)
|
||||
if len(paramKeyToAttrMap) > 0 {
|
||||
keyToAttributeNameMapping = paramKeyToAttrMap[0]
|
||||
}
|
||||
|
||||
// Handle different destination types
|
||||
switch dstPointerReflectTypeElemKind {
|
||||
case reflect.Map:
|
||||
// Convert map to map
|
||||
return doMapToMap(srcValue, dstPointer, paramKeyToAttrMap...)
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
var (
|
||||
sliceElem = dstPointerReflectTypeElem.Elem()
|
||||
sliceElemKind = sliceElem.Kind()
|
||||
)
|
||||
// Handle pointer elements
|
||||
for sliceElemKind == reflect.Ptr {
|
||||
sliceElem = sliceElem.Elem()
|
||||
sliceElemKind = sliceElem.Kind()
|
||||
}
|
||||
if sliceElemKind == reflect.Map {
|
||||
// Convert to slice of maps
|
||||
return doMapToMaps(srcValue, dstPointer, paramKeyToAttrMap...)
|
||||
}
|
||||
// Convert to slice of structs
|
||||
return doStructs(srcValue, dstPointer, keyToAttributeNameMapping, "")
|
||||
|
||||
default:
|
||||
// Convert to single struct
|
||||
return doStruct(srcValue, dstPointer, keyToAttributeNameMapping, "")
|
||||
option.ParamKeyToAttrMap = paramKeyToAttrMap[0]
|
||||
}
|
||||
}
|
||||
|
||||
// doConvertWithTypeCheck supports `pointer` in type of `*map/*[]map/*[]*map/*struct/**struct/*[]struct/*[]*struct`
|
||||
// for converting.
|
||||
func doConvertWithTypeCheck(srcValueReflectValue, dstPointerReflectValueElem reflect.Value) (ok bool) {
|
||||
if !dstPointerReflectValueElem.IsValid() || !srcValueReflectValue.IsValid() {
|
||||
return false
|
||||
}
|
||||
switch {
|
||||
// Examples:
|
||||
// UploadFile => UploadFile
|
||||
// []UploadFile => []UploadFile
|
||||
// *UploadFile => *UploadFile
|
||||
// *[]UploadFile => *[]UploadFile
|
||||
// map[int][int] => map[int][int]
|
||||
// []map[int][int] => []map[int][int]
|
||||
// *[]map[int][int] => *[]map[int][int]
|
||||
case dstPointerReflectValueElem.Type() == srcValueReflectValue.Type():
|
||||
dstPointerReflectValueElem.Set(srcValueReflectValue)
|
||||
return true
|
||||
|
||||
// Examples:
|
||||
// UploadFile => *UploadFile
|
||||
// []UploadFile => *[]UploadFile
|
||||
// map[int][int] => *map[int][int]
|
||||
// []map[int][int] => *[]map[int][int]
|
||||
case dstPointerReflectValueElem.Kind() == reflect.Ptr &&
|
||||
dstPointerReflectValueElem.Elem().IsValid() &&
|
||||
dstPointerReflectValueElem.Elem().Type() == srcValueReflectValue.Type():
|
||||
dstPointerReflectValueElem.Elem().Set(srcValueReflectValue)
|
||||
return true
|
||||
|
||||
// Examples:
|
||||
// *UploadFile => UploadFile
|
||||
// *[]UploadFile => []UploadFile
|
||||
// *map[int][int] => map[int][int]
|
||||
// *[]map[int][int] => []map[int][int]
|
||||
case srcValueReflectValue.Kind() == reflect.Ptr &&
|
||||
srcValueReflectValue.Elem().IsValid() &&
|
||||
dstPointerReflectValueElem.Type() == srcValueReflectValue.Elem().Type():
|
||||
dstPointerReflectValueElem.Set(srcValueReflectValue.Elem())
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// doConvertWithJsonCheck attempts to convert the source value to the destination
|
||||
// using JSON marshaling and unmarshaling. This is particularly useful for complex
|
||||
// types that can be represented as JSON.
|
||||
//
|
||||
// Parameters:
|
||||
// - srcValue: The source value to convert from
|
||||
// - dstPointer: The destination pointer to convert to
|
||||
//
|
||||
// Returns:
|
||||
// - bool: true if JSON conversion was successful
|
||||
// - error: any error that occurred during conversion
|
||||
func doConvertWithJsonCheck(srcValue any, dstPointer any) (ok bool, err error) {
|
||||
switch valueResult := srcValue.(type) {
|
||||
case []byte:
|
||||
if json.Valid(valueResult) {
|
||||
if dstPointerReflectType, ok := dstPointer.(reflect.Value); ok {
|
||||
if dstPointerReflectType.Kind() == reflect.Ptr {
|
||||
if dstPointerReflectType.IsNil() {
|
||||
return false, nil
|
||||
}
|
||||
return true, json.UnmarshalUseNumber(valueResult, dstPointerReflectType.Interface())
|
||||
} else if dstPointerReflectType.CanAddr() {
|
||||
return true, json.UnmarshalUseNumber(valueResult, dstPointerReflectType.Addr().Interface())
|
||||
}
|
||||
} else {
|
||||
return true, json.UnmarshalUseNumber(valueResult, dstPointer)
|
||||
}
|
||||
}
|
||||
|
||||
case string:
|
||||
if valueBytes := []byte(valueResult); json.Valid(valueBytes) {
|
||||
if dstPointerReflectType, ok := dstPointer.(reflect.Value); ok {
|
||||
if dstPointerReflectType.Kind() == reflect.Ptr {
|
||||
if dstPointerReflectType.IsNil() {
|
||||
return false, nil
|
||||
}
|
||||
return true, json.UnmarshalUseNumber(valueBytes, dstPointerReflectType.Interface())
|
||||
} else if dstPointerReflectType.CanAddr() {
|
||||
return true, json.UnmarshalUseNumber(valueBytes, dstPointerReflectType.Addr().Interface())
|
||||
}
|
||||
} else {
|
||||
return true, json.UnmarshalUseNumber(valueBytes, dstPointer)
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
// The `params` might be struct that implements interface function Interface, eg: gvar.Var.
|
||||
if v, ok := srcValue.(localinterface.IInterface); ok {
|
||||
return doConvertWithJsonCheck(v.Interface(), dstPointer)
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
return defaultConverter.Scan(srcValue, dstPointer, option)
|
||||
}
|
||||
|
||||
@ -93,7 +93,9 @@ import (
|
||||
// given `relation` parameter.
|
||||
//
|
||||
// See the example or unit testing cases for clear understanding for this function.
|
||||
func ScanList(structSlice interface{}, structSlicePointer interface{}, bindToAttrName string, relationAttrNameAndFields ...string) (err error) {
|
||||
func ScanList(
|
||||
structSlice any, structSlicePointer any, bindToAttrName string, relationAttrNameAndFields ...string,
|
||||
) (err error) {
|
||||
var (
|
||||
relationAttrName string
|
||||
relationFields string
|
||||
@ -111,7 +113,7 @@ func ScanList(structSlice interface{}, structSlicePointer interface{}, bindToAtt
|
||||
// doScanList converts `structSlice` to struct slice which contains other complex struct attributes recursively.
|
||||
// Note that the parameter `structSlicePointer` should be type of *[]struct/*[]*struct.
|
||||
func doScanList(
|
||||
structSlice interface{}, structSlicePointer interface{}, bindToAttrName, relationAttrName, relationFields string,
|
||||
structSlice any, structSlicePointer any, bindToAttrName, relationAttrName, relationFields string,
|
||||
) (err error) {
|
||||
var (
|
||||
maps = Maps(structSlice)
|
||||
@ -169,7 +171,7 @@ func doScanList(
|
||||
|
||||
// Relation variables.
|
||||
var (
|
||||
relationDataMap map[string]interface{}
|
||||
relationDataMap map[string]any
|
||||
relationFromFieldName string // Eg: relationKV: id:uid -> id
|
||||
relationBindToFieldName string // Eg: relationKV: id:uid -> uid
|
||||
)
|
||||
@ -315,7 +317,7 @@ func doScanList(
|
||||
relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToFieldName)
|
||||
if relationFromAttrField.IsValid() {
|
||||
// results := make(Result, 0)
|
||||
results := make([]interface{}, 0)
|
||||
results := make([]any, 0)
|
||||
for _, v := range SliceAny(relationDataMap[String(relationFromAttrField.Interface())]) {
|
||||
item := v
|
||||
results = append(results, item)
|
||||
|
||||
@ -6,17 +6,6 @@
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/reflection"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
|
||||
)
|
||||
|
||||
// SliceAny is alias of Interfaces.
|
||||
func SliceAny(any interface{}) []interface{} {
|
||||
return Interfaces(any)
|
||||
@ -24,123 +13,8 @@ func SliceAny(any interface{}) []interface{} {
|
||||
|
||||
// Interfaces converts `any` to []interface{}.
|
||||
func Interfaces(any interface{}) []interface{} {
|
||||
if empty.IsNil(any) {
|
||||
return nil
|
||||
}
|
||||
var array []interface{}
|
||||
switch value := any.(type) {
|
||||
case []interface{}:
|
||||
array = value
|
||||
case []string:
|
||||
array = make([]interface{}, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = v
|
||||
}
|
||||
case []int:
|
||||
array = make([]interface{}, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = v
|
||||
}
|
||||
case []int8:
|
||||
array = make([]interface{}, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = v
|
||||
}
|
||||
case []int16:
|
||||
array = make([]interface{}, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = v
|
||||
}
|
||||
case []int32:
|
||||
array = make([]interface{}, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = v
|
||||
}
|
||||
case []int64:
|
||||
array = make([]interface{}, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = v
|
||||
}
|
||||
case []uint:
|
||||
array = make([]interface{}, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = v
|
||||
}
|
||||
case []uint8:
|
||||
if json.Valid(value) {
|
||||
if _ = json.UnmarshalUseNumber(value, &array); array != nil {
|
||||
return array
|
||||
}
|
||||
if bytes.EqualFold([]byte("null"), value) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
array = make([]interface{}, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = v
|
||||
}
|
||||
case string:
|
||||
byteValue := []byte(value)
|
||||
if json.Valid(byteValue) {
|
||||
if _ = json.UnmarshalUseNumber(byteValue, &array); array != nil {
|
||||
return array
|
||||
}
|
||||
if strings.EqualFold(value, "null") {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
case []uint16:
|
||||
array = make([]interface{}, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = v
|
||||
}
|
||||
case []uint32:
|
||||
for _, v := range value {
|
||||
array = append(array, v)
|
||||
}
|
||||
case []uint64:
|
||||
array = make([]interface{}, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = v
|
||||
}
|
||||
case []bool:
|
||||
array = make([]interface{}, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = v
|
||||
}
|
||||
case []float32:
|
||||
array = make([]interface{}, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = v
|
||||
}
|
||||
case []float64:
|
||||
array = make([]interface{}, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = v
|
||||
}
|
||||
}
|
||||
if array != nil {
|
||||
return array
|
||||
}
|
||||
if v, ok := any.(localinterface.IInterfaces); ok {
|
||||
return v.Interfaces()
|
||||
}
|
||||
|
||||
// Not a common type, it then uses reflection for conversion.
|
||||
originValueAndKind := reflection.OriginValueAndKind(any)
|
||||
switch originValueAndKind.OriginKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
var (
|
||||
length = originValueAndKind.OriginValue.Len()
|
||||
slice = make([]interface{}, length)
|
||||
)
|
||||
for i := 0; i < length; i++ {
|
||||
slice[i] = originValueAndKind.OriginValue.Index(i).Interface()
|
||||
}
|
||||
return slice
|
||||
|
||||
default:
|
||||
return []interface{}{any}
|
||||
}
|
||||
result, _ := defaultConverter.SliceAny(any, SliceOption{
|
||||
ContinueOnError: true,
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
@ -6,18 +6,6 @@
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/reflection"
|
||||
"github.com/gogf/gf/v2/internal/utils"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
|
||||
)
|
||||
|
||||
// SliceFloat is alias of Floats.
|
||||
func SliceFloat(any interface{}) []float64 {
|
||||
return Floats(any)
|
||||
@ -40,270 +28,16 @@ func Floats(any interface{}) []float64 {
|
||||
|
||||
// Float32s converts `any` to []float32.
|
||||
func Float32s(any interface{}) []float32 {
|
||||
if empty.IsNil(any) {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
array []float32 = nil
|
||||
)
|
||||
switch value := any.(type) {
|
||||
case []string:
|
||||
array = make([]float32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float32(v)
|
||||
}
|
||||
case []int:
|
||||
array = make([]float32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float32(v)
|
||||
}
|
||||
case []int8:
|
||||
array = make([]float32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float32(v)
|
||||
}
|
||||
case []int16:
|
||||
array = make([]float32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float32(v)
|
||||
}
|
||||
case []int32:
|
||||
array = make([]float32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float32(v)
|
||||
}
|
||||
case []int64:
|
||||
array = make([]float32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float32(v)
|
||||
}
|
||||
case []uint:
|
||||
for _, v := range value {
|
||||
array = append(array, Float32(v))
|
||||
}
|
||||
case []uint8:
|
||||
if json.Valid(value) {
|
||||
if _ = json.UnmarshalUseNumber(value, &array); array != nil {
|
||||
return array
|
||||
}
|
||||
if bytes.EqualFold([]byte("null"), value) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
array = make([]float32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float32(v)
|
||||
}
|
||||
case string:
|
||||
byteValue := []byte(value)
|
||||
if json.Valid(byteValue) {
|
||||
if _ = json.UnmarshalUseNumber(byteValue, &array); array != nil {
|
||||
return array
|
||||
}
|
||||
if strings.EqualFold(value, "null") {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if value == "" {
|
||||
return []float32{}
|
||||
}
|
||||
if utils.IsNumeric(value) {
|
||||
return []float32{Float32(value)}
|
||||
}
|
||||
case []uint16:
|
||||
array = make([]float32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float32(v)
|
||||
}
|
||||
case []uint32:
|
||||
array = make([]float32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float32(v)
|
||||
}
|
||||
case []uint64:
|
||||
array = make([]float32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float32(v)
|
||||
}
|
||||
case []bool:
|
||||
array = make([]float32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float32(v)
|
||||
}
|
||||
case []float32:
|
||||
array = value
|
||||
case []float64:
|
||||
array = make([]float32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float32(v)
|
||||
}
|
||||
case []interface{}:
|
||||
array = make([]float32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float32(v)
|
||||
}
|
||||
}
|
||||
if array != nil {
|
||||
return array
|
||||
}
|
||||
if v, ok := any.(localinterface.IFloats); ok {
|
||||
return Float32s(v.Floats())
|
||||
}
|
||||
if v, ok := any.(localinterface.IInterfaces); ok {
|
||||
return Float32s(v.Interfaces())
|
||||
}
|
||||
// Not a common type, it then uses reflection for conversion.
|
||||
originValueAndKind := reflection.OriginValueAndKind(any)
|
||||
switch originValueAndKind.OriginKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
var (
|
||||
length = originValueAndKind.OriginValue.Len()
|
||||
slice = make([]float32, length)
|
||||
)
|
||||
for i := 0; i < length; i++ {
|
||||
slice[i] = Float32(originValueAndKind.OriginValue.Index(i).Interface())
|
||||
}
|
||||
return slice
|
||||
|
||||
default:
|
||||
if originValueAndKind.OriginValue.IsZero() {
|
||||
return []float32{}
|
||||
}
|
||||
return []float32{Float32(any)}
|
||||
}
|
||||
result, _ := defaultConverter.SliceFloat32(any, SliceOption{
|
||||
ContinueOnError: true,
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
// Float64s converts `any` to []float64.
|
||||
func Float64s(any interface{}) []float64 {
|
||||
if empty.IsNil(any) {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
array []float64 = nil
|
||||
)
|
||||
switch value := any.(type) {
|
||||
case []string:
|
||||
array = make([]float64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float64(v)
|
||||
}
|
||||
case []int:
|
||||
array = make([]float64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float64(v)
|
||||
}
|
||||
case []int8:
|
||||
array = make([]float64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float64(v)
|
||||
}
|
||||
case []int16:
|
||||
array = make([]float64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float64(v)
|
||||
}
|
||||
case []int32:
|
||||
array = make([]float64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float64(v)
|
||||
}
|
||||
case []int64:
|
||||
array = make([]float64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float64(v)
|
||||
}
|
||||
case []uint:
|
||||
for _, v := range value {
|
||||
array = append(array, Float64(v))
|
||||
}
|
||||
case []uint8:
|
||||
if json.Valid(value) {
|
||||
if _ = json.UnmarshalUseNumber(value, &array); array != nil {
|
||||
return array
|
||||
}
|
||||
if bytes.EqualFold([]byte("null"), value) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
array = make([]float64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float64(v)
|
||||
}
|
||||
case string:
|
||||
byteValue := []byte(value)
|
||||
if json.Valid(byteValue) {
|
||||
if _ = json.UnmarshalUseNumber(byteValue, &array); array != nil {
|
||||
return array
|
||||
}
|
||||
if strings.EqualFold(value, "null") {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if value == "" {
|
||||
return []float64{}
|
||||
}
|
||||
if utils.IsNumeric(value) {
|
||||
return []float64{Float64(value)}
|
||||
}
|
||||
case []uint16:
|
||||
array = make([]float64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float64(v)
|
||||
}
|
||||
case []uint32:
|
||||
array = make([]float64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float64(v)
|
||||
}
|
||||
case []uint64:
|
||||
array = make([]float64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float64(v)
|
||||
}
|
||||
case []bool:
|
||||
array = make([]float64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float64(v)
|
||||
}
|
||||
case []float32:
|
||||
array = make([]float64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float64(v)
|
||||
}
|
||||
case []float64:
|
||||
array = value
|
||||
case []interface{}:
|
||||
array = make([]float64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Float64(v)
|
||||
}
|
||||
}
|
||||
if array != nil {
|
||||
return array
|
||||
}
|
||||
if v, ok := any.(localinterface.IFloats); ok {
|
||||
return v.Floats()
|
||||
}
|
||||
if v, ok := any.(localinterface.IInterfaces); ok {
|
||||
return Floats(v.Interfaces())
|
||||
}
|
||||
// Not a common type, it then uses reflection for conversion.
|
||||
originValueAndKind := reflection.OriginValueAndKind(any)
|
||||
switch originValueAndKind.OriginKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
var (
|
||||
length = originValueAndKind.OriginValue.Len()
|
||||
slice = make([]float64, length)
|
||||
)
|
||||
for i := 0; i < length; i++ {
|
||||
slice[i] = Float64(originValueAndKind.OriginValue.Index(i).Interface())
|
||||
}
|
||||
return slice
|
||||
|
||||
default:
|
||||
if originValueAndKind.OriginValue.IsZero() {
|
||||
return []float64{}
|
||||
}
|
||||
return []float64{Float64(any)}
|
||||
}
|
||||
result, _ := defaultConverter.SliceFloat64(any, SliceOption{
|
||||
ContinueOnError: true,
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
@ -6,464 +6,41 @@
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/reflection"
|
||||
"github.com/gogf/gf/v2/internal/utils"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
|
||||
)
|
||||
|
||||
// SliceInt is alias of Ints.
|
||||
func SliceInt(any interface{}) []int {
|
||||
func SliceInt(any any) []int {
|
||||
return Ints(any)
|
||||
}
|
||||
|
||||
// SliceInt32 is alias of Int32s.
|
||||
func SliceInt32(any interface{}) []int32 {
|
||||
func SliceInt32(any any) []int32 {
|
||||
return Int32s(any)
|
||||
}
|
||||
|
||||
// SliceInt64 is alias of Int64s.
|
||||
func SliceInt64(any interface{}) []int64 {
|
||||
func SliceInt64(any any) []int64 {
|
||||
return Int64s(any)
|
||||
}
|
||||
|
||||
// Ints converts `any` to []int.
|
||||
func Ints(any interface{}) []int {
|
||||
if empty.IsNil(any) {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
array []int = nil
|
||||
)
|
||||
switch value := any.(type) {
|
||||
case []string:
|
||||
array = make([]int, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Int(v)
|
||||
}
|
||||
case []int:
|
||||
array = value
|
||||
case []int8:
|
||||
array = make([]int, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int(v)
|
||||
}
|
||||
case []int16:
|
||||
array = make([]int, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int(v)
|
||||
}
|
||||
case []int32:
|
||||
array = make([]int, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int(v)
|
||||
}
|
||||
case []int64:
|
||||
array = make([]int, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int(v)
|
||||
}
|
||||
case []uint:
|
||||
array = make([]int, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int(v)
|
||||
}
|
||||
case []uint8:
|
||||
if json.Valid(value) {
|
||||
if _ = json.UnmarshalUseNumber(value, &array); array != nil {
|
||||
return array
|
||||
}
|
||||
if bytes.EqualFold([]byte("null"), value) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
array = make([]int, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int(v)
|
||||
}
|
||||
case string:
|
||||
byteValue := []byte(value)
|
||||
if json.Valid(byteValue) {
|
||||
if _ = json.UnmarshalUseNumber(byteValue, &array); array != nil {
|
||||
return array
|
||||
}
|
||||
if strings.EqualFold(value, "null") {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if value == "" {
|
||||
return []int{}
|
||||
}
|
||||
if utils.IsNumeric(value) {
|
||||
return []int{Int(value)}
|
||||
}
|
||||
case []uint16:
|
||||
array = make([]int, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int(v)
|
||||
}
|
||||
case []uint32:
|
||||
array = make([]int, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int(v)
|
||||
}
|
||||
case []uint64:
|
||||
array = make([]int, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int(v)
|
||||
}
|
||||
case []bool:
|
||||
array = make([]int, len(value))
|
||||
for k, v := range value {
|
||||
if v {
|
||||
array[k] = 1
|
||||
} else {
|
||||
array[k] = 0
|
||||
}
|
||||
}
|
||||
case []float32:
|
||||
array = make([]int, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Int(v)
|
||||
}
|
||||
case []float64:
|
||||
array = make([]int, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Int(v)
|
||||
}
|
||||
case []interface{}:
|
||||
array = make([]int, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Int(v)
|
||||
}
|
||||
case [][]byte:
|
||||
array = make([]int, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Int(v)
|
||||
}
|
||||
}
|
||||
if array != nil {
|
||||
return array
|
||||
}
|
||||
if v, ok := any.(localinterface.IInts); ok {
|
||||
return v.Ints()
|
||||
}
|
||||
if v, ok := any.(localinterface.IInterfaces); ok {
|
||||
return Ints(v.Interfaces())
|
||||
}
|
||||
// Not a common type, it then uses reflection for conversion.
|
||||
originValueAndKind := reflection.OriginValueAndKind(any)
|
||||
switch originValueAndKind.OriginKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
var (
|
||||
length = originValueAndKind.OriginValue.Len()
|
||||
slice = make([]int, length)
|
||||
)
|
||||
for i := 0; i < length; i++ {
|
||||
slice[i] = Int(originValueAndKind.OriginValue.Index(i).Interface())
|
||||
}
|
||||
return slice
|
||||
|
||||
default:
|
||||
if originValueAndKind.OriginValue.IsZero() {
|
||||
return []int{}
|
||||
}
|
||||
return []int{Int(any)}
|
||||
}
|
||||
func Ints(any any) []int {
|
||||
result, _ := defaultConverter.SliceInt(any, SliceOption{
|
||||
ContinueOnError: true,
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
// Int32s converts `any` to []int32.
|
||||
func Int32s(any interface{}) []int32 {
|
||||
if empty.IsNil(any) {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
array []int32 = nil
|
||||
)
|
||||
switch value := any.(type) {
|
||||
case []string:
|
||||
array = make([]int32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Int32(v)
|
||||
}
|
||||
case []int:
|
||||
array = make([]int32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int32(v)
|
||||
}
|
||||
case []int8:
|
||||
array = make([]int32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int32(v)
|
||||
}
|
||||
case []int16:
|
||||
array = make([]int32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int32(v)
|
||||
}
|
||||
case []int32:
|
||||
array = value
|
||||
case []int64:
|
||||
array = make([]int32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int32(v)
|
||||
}
|
||||
case []uint:
|
||||
array = make([]int32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int32(v)
|
||||
}
|
||||
case []uint8:
|
||||
if json.Valid(value) {
|
||||
if _ = json.UnmarshalUseNumber(value, &array); array != nil {
|
||||
return array
|
||||
}
|
||||
if bytes.EqualFold([]byte("null"), value) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
array = make([]int32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int32(v)
|
||||
}
|
||||
case string:
|
||||
byteValue := []byte(value)
|
||||
if json.Valid(byteValue) {
|
||||
if _ = json.UnmarshalUseNumber(byteValue, &array); array != nil {
|
||||
return array
|
||||
}
|
||||
if strings.EqualFold(value, "null") {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if value == "" {
|
||||
return []int32{}
|
||||
}
|
||||
if utils.IsNumeric(value) {
|
||||
return []int32{Int32(value)}
|
||||
}
|
||||
case []uint16:
|
||||
array = make([]int32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int32(v)
|
||||
}
|
||||
case []uint32:
|
||||
array = make([]int32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int32(v)
|
||||
}
|
||||
case []uint64:
|
||||
array = make([]int32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int32(v)
|
||||
}
|
||||
case []bool:
|
||||
array = make([]int32, len(value))
|
||||
for k, v := range value {
|
||||
if v {
|
||||
array[k] = 1
|
||||
} else {
|
||||
array[k] = 0
|
||||
}
|
||||
}
|
||||
case []float32:
|
||||
array = make([]int32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Int32(v)
|
||||
}
|
||||
case []float64:
|
||||
array = make([]int32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Int32(v)
|
||||
}
|
||||
case []interface{}:
|
||||
array = make([]int32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Int32(v)
|
||||
}
|
||||
case [][]byte:
|
||||
array = make([]int32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Int32(v)
|
||||
}
|
||||
}
|
||||
if array != nil {
|
||||
return array
|
||||
}
|
||||
if v, ok := any.(localinterface.IInts); ok {
|
||||
return Int32s(v.Ints())
|
||||
}
|
||||
if v, ok := any.(localinterface.IInterfaces); ok {
|
||||
return Int32s(v.Interfaces())
|
||||
}
|
||||
// Not a common type, it then uses reflection for conversion.
|
||||
originValueAndKind := reflection.OriginValueAndKind(any)
|
||||
switch originValueAndKind.OriginKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
var (
|
||||
length = originValueAndKind.OriginValue.Len()
|
||||
slice = make([]int32, length)
|
||||
)
|
||||
for i := 0; i < length; i++ {
|
||||
slice[i] = Int32(originValueAndKind.OriginValue.Index(i).Interface())
|
||||
}
|
||||
return slice
|
||||
|
||||
default:
|
||||
if originValueAndKind.OriginValue.IsZero() {
|
||||
return []int32{}
|
||||
}
|
||||
return []int32{Int32(any)}
|
||||
}
|
||||
func Int32s(any any) []int32 {
|
||||
result, _ := defaultConverter.SliceInt32(any, SliceOption{
|
||||
ContinueOnError: true,
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
// Int64s converts `any` to []int64.
|
||||
func Int64s(any interface{}) []int64 {
|
||||
if empty.IsNil(any) {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
array []int64 = nil
|
||||
)
|
||||
switch value := any.(type) {
|
||||
case []string:
|
||||
array = make([]int64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Int64(v)
|
||||
}
|
||||
case []int:
|
||||
array = make([]int64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int64(v)
|
||||
}
|
||||
case []int8:
|
||||
array = make([]int64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int64(v)
|
||||
}
|
||||
case []int16:
|
||||
array = make([]int64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int64(v)
|
||||
}
|
||||
case []int32:
|
||||
array = make([]int64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int64(v)
|
||||
}
|
||||
case []int64:
|
||||
array = value
|
||||
case []uint:
|
||||
array = make([]int64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int64(v)
|
||||
}
|
||||
case []uint8:
|
||||
if json.Valid(value) {
|
||||
if _ = json.UnmarshalUseNumber(value, &array); array != nil {
|
||||
return array
|
||||
}
|
||||
if bytes.EqualFold([]byte("null"), value) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
array = make([]int64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int64(v)
|
||||
}
|
||||
case string:
|
||||
byteValue := []byte(value)
|
||||
if json.Valid(byteValue) {
|
||||
if _ = json.UnmarshalUseNumber(byteValue, &array); array != nil {
|
||||
return array
|
||||
}
|
||||
if strings.EqualFold(value, "null") {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if value == "" {
|
||||
return []int64{}
|
||||
}
|
||||
if utils.IsNumeric(value) {
|
||||
return []int64{Int64(value)}
|
||||
}
|
||||
case []uint16:
|
||||
array = make([]int64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int64(v)
|
||||
}
|
||||
case []uint32:
|
||||
array = make([]int64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int64(v)
|
||||
}
|
||||
case []uint64:
|
||||
array = make([]int64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = int64(v)
|
||||
}
|
||||
case []bool:
|
||||
array = make([]int64, len(value))
|
||||
for k, v := range value {
|
||||
if v {
|
||||
array[k] = 1
|
||||
} else {
|
||||
array[k] = 0
|
||||
}
|
||||
}
|
||||
case []float32:
|
||||
array = make([]int64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Int64(v)
|
||||
}
|
||||
case []float64:
|
||||
array = make([]int64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Int64(v)
|
||||
}
|
||||
case []interface{}:
|
||||
array = make([]int64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Int64(v)
|
||||
}
|
||||
case [][]byte:
|
||||
array = make([]int64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Int64(v)
|
||||
}
|
||||
}
|
||||
if array != nil {
|
||||
return array
|
||||
}
|
||||
if v, ok := any.(localinterface.IInts); ok {
|
||||
return Int64s(v.Ints())
|
||||
}
|
||||
if v, ok := any.(localinterface.IInterfaces); ok {
|
||||
return Int64s(v.Interfaces())
|
||||
}
|
||||
// Not a common type, it then uses reflection for conversion.
|
||||
originValueAndKind := reflection.OriginValueAndKind(any)
|
||||
switch originValueAndKind.OriginKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
var (
|
||||
length = originValueAndKind.OriginValue.Len()
|
||||
slice = make([]int64, length)
|
||||
)
|
||||
for i := 0; i < length; i++ {
|
||||
slice[i] = Int64(originValueAndKind.OriginValue.Index(i).Interface())
|
||||
}
|
||||
return slice
|
||||
|
||||
default:
|
||||
if originValueAndKind.OriginValue.IsZero() {
|
||||
return []int64{}
|
||||
}
|
||||
return []int64{Int64(any)}
|
||||
}
|
||||
func Int64s(any any) []int64 {
|
||||
result, _ := defaultConverter.SliceInt64(any, SliceOption{
|
||||
ContinueOnError: true,
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
@ -6,17 +6,6 @@
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/reflection"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
|
||||
)
|
||||
|
||||
// SliceStr is alias of Strings.
|
||||
func SliceStr(any interface{}) []string {
|
||||
return Strings(any)
|
||||
@ -24,140 +13,8 @@ func SliceStr(any interface{}) []string {
|
||||
|
||||
// Strings converts `any` to []string.
|
||||
func Strings(any interface{}) []string {
|
||||
if empty.IsNil(any) {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
array []string = nil
|
||||
)
|
||||
switch value := any.(type) {
|
||||
case []int:
|
||||
array = make([]string, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = String(v)
|
||||
}
|
||||
case []int8:
|
||||
array = make([]string, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = String(v)
|
||||
}
|
||||
case []int16:
|
||||
array = make([]string, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = String(v)
|
||||
}
|
||||
case []int32:
|
||||
array = make([]string, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = String(v)
|
||||
}
|
||||
case []int64:
|
||||
array = make([]string, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = String(v)
|
||||
}
|
||||
case []uint:
|
||||
array = make([]string, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = String(v)
|
||||
}
|
||||
case []uint8:
|
||||
if json.Valid(value) {
|
||||
if _ = json.UnmarshalUseNumber(value, &array); array != nil {
|
||||
return array
|
||||
}
|
||||
if bytes.EqualFold([]byte("null"), value) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
array = make([]string, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = String(v)
|
||||
}
|
||||
return array
|
||||
case string:
|
||||
byteValue := []byte(value)
|
||||
if json.Valid(byteValue) {
|
||||
if _ = json.UnmarshalUseNumber(byteValue, &array); array != nil {
|
||||
return array
|
||||
}
|
||||
if strings.EqualFold(value, "null") {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if value == "" {
|
||||
return []string{}
|
||||
}
|
||||
return []string{value}
|
||||
case []uint16:
|
||||
array = make([]string, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = String(v)
|
||||
}
|
||||
case []uint32:
|
||||
array = make([]string, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = String(v)
|
||||
}
|
||||
case []uint64:
|
||||
array = make([]string, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = String(v)
|
||||
}
|
||||
case []bool:
|
||||
array = make([]string, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = String(v)
|
||||
}
|
||||
case []float32:
|
||||
array = make([]string, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = String(v)
|
||||
}
|
||||
case []float64:
|
||||
array = make([]string, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = String(v)
|
||||
}
|
||||
case []interface{}:
|
||||
array = make([]string, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = String(v)
|
||||
}
|
||||
case []string:
|
||||
array = value
|
||||
case [][]byte:
|
||||
array = make([]string, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = String(v)
|
||||
}
|
||||
}
|
||||
if array != nil {
|
||||
return array
|
||||
}
|
||||
if v, ok := any.(localinterface.IStrings); ok {
|
||||
return v.Strings()
|
||||
}
|
||||
if v, ok := any.(localinterface.IInterfaces); ok {
|
||||
return Strings(v.Interfaces())
|
||||
}
|
||||
// Not a common type, it then uses reflection for conversion.
|
||||
originValueAndKind := reflection.OriginValueAndKind(any)
|
||||
switch originValueAndKind.OriginKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
var (
|
||||
length = originValueAndKind.OriginValue.Len()
|
||||
slice = make([]string, length)
|
||||
)
|
||||
for i := 0; i < length; i++ {
|
||||
slice[i] = String(originValueAndKind.OriginValue.Index(i).Interface())
|
||||
}
|
||||
return slice
|
||||
|
||||
default:
|
||||
if originValueAndKind.OriginValue.IsZero() {
|
||||
return []string{}
|
||||
}
|
||||
return []string{String(any)}
|
||||
}
|
||||
result, _ := defaultConverter.SliceStr(any, SliceOption{
|
||||
ContinueOnError: true,
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
@ -6,18 +6,6 @@
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/reflection"
|
||||
"github.com/gogf/gf/v2/internal/utils"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
|
||||
)
|
||||
|
||||
// SliceUint is alias of Uints.
|
||||
func SliceUint(any interface{}) []uint {
|
||||
return Uints(any)
|
||||
@ -35,426 +23,24 @@ func SliceUint64(any interface{}) []uint64 {
|
||||
|
||||
// Uints converts `any` to []uint.
|
||||
func Uints(any interface{}) []uint {
|
||||
if empty.IsNil(any) {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
array []uint = nil
|
||||
)
|
||||
switch value := any.(type) {
|
||||
case []string:
|
||||
array = make([]uint, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Uint(v)
|
||||
}
|
||||
case []int8:
|
||||
array = make([]uint, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint(v)
|
||||
}
|
||||
case []int16:
|
||||
array = make([]uint, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint(v)
|
||||
}
|
||||
case []int32:
|
||||
array = make([]uint, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint(v)
|
||||
}
|
||||
case []int64:
|
||||
array = make([]uint, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint(v)
|
||||
}
|
||||
case []uint:
|
||||
array = value
|
||||
case []uint8:
|
||||
if json.Valid(value) {
|
||||
if _ = json.UnmarshalUseNumber(value, &array); array != nil {
|
||||
return array
|
||||
}
|
||||
if bytes.EqualFold([]byte("null"), value) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
array = make([]uint, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint(v)
|
||||
}
|
||||
case string:
|
||||
byteValue := []byte(value)
|
||||
if json.Valid(byteValue) {
|
||||
if _ = json.UnmarshalUseNumber(byteValue, &array); array != nil {
|
||||
return array
|
||||
}
|
||||
if strings.EqualFold(value, "null") {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if value == "" {
|
||||
return []uint{}
|
||||
}
|
||||
if utils.IsNumeric(value) {
|
||||
return []uint{Uint(value)}
|
||||
}
|
||||
case []uint16:
|
||||
array = make([]uint, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint(v)
|
||||
}
|
||||
case []uint32:
|
||||
array = make([]uint, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint(v)
|
||||
}
|
||||
case []uint64:
|
||||
array = make([]uint, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint(v)
|
||||
}
|
||||
case []bool:
|
||||
array = make([]uint, len(value))
|
||||
for k, v := range value {
|
||||
if v {
|
||||
array[k] = 1
|
||||
} else {
|
||||
array[k] = 0
|
||||
}
|
||||
}
|
||||
case []float32:
|
||||
array = make([]uint, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Uint(v)
|
||||
}
|
||||
case []float64:
|
||||
array = make([]uint, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Uint(v)
|
||||
}
|
||||
case []interface{}:
|
||||
array = make([]uint, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Uint(v)
|
||||
}
|
||||
case [][]byte:
|
||||
array = make([]uint, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Uint(v)
|
||||
}
|
||||
}
|
||||
|
||||
if array != nil {
|
||||
return array
|
||||
}
|
||||
|
||||
// Default handler.
|
||||
if v, ok := any.(localinterface.IUints); ok {
|
||||
return v.Uints()
|
||||
}
|
||||
if v, ok := any.(localinterface.IInterfaces); ok {
|
||||
return Uints(v.Interfaces())
|
||||
}
|
||||
// Not a common type, it then uses reflection for conversion.
|
||||
originValueAndKind := reflection.OriginValueAndKind(any)
|
||||
switch originValueAndKind.OriginKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
var (
|
||||
length = originValueAndKind.OriginValue.Len()
|
||||
slice = make([]uint, length)
|
||||
)
|
||||
for i := 0; i < length; i++ {
|
||||
slice[i] = Uint(originValueAndKind.OriginValue.Index(i).Interface())
|
||||
}
|
||||
return slice
|
||||
|
||||
default:
|
||||
if originValueAndKind.OriginValue.IsZero() {
|
||||
return []uint{}
|
||||
}
|
||||
return []uint{Uint(any)}
|
||||
}
|
||||
result, _ := defaultConverter.SliceUint(any, SliceOption{
|
||||
ContinueOnError: true,
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
// Uint32s converts `any` to []uint32.
|
||||
func Uint32s(any interface{}) []uint32 {
|
||||
if empty.IsNil(any) {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
array []uint32 = nil
|
||||
)
|
||||
switch value := any.(type) {
|
||||
case []string:
|
||||
array = make([]uint32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Uint32(v)
|
||||
}
|
||||
case []int8:
|
||||
array = make([]uint32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint32(v)
|
||||
}
|
||||
case []int16:
|
||||
array = make([]uint32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint32(v)
|
||||
}
|
||||
case []int32:
|
||||
array = make([]uint32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint32(v)
|
||||
}
|
||||
case []int64:
|
||||
array = make([]uint32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint32(v)
|
||||
}
|
||||
case []uint:
|
||||
array = make([]uint32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint32(v)
|
||||
}
|
||||
case []uint8:
|
||||
if json.Valid(value) {
|
||||
if _ = json.UnmarshalUseNumber(value, &array); array != nil {
|
||||
return array
|
||||
}
|
||||
if bytes.EqualFold([]byte("null"), value) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
array = make([]uint32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint32(v)
|
||||
}
|
||||
case string:
|
||||
byteValue := []byte(value)
|
||||
if json.Valid(byteValue) {
|
||||
if _ = json.UnmarshalUseNumber(byteValue, &array); array != nil {
|
||||
return array
|
||||
}
|
||||
if strings.EqualFold(value, "null") {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if value == "" {
|
||||
return []uint32{}
|
||||
}
|
||||
if utils.IsNumeric(value) {
|
||||
return []uint32{Uint32(value)}
|
||||
}
|
||||
case []uint16:
|
||||
array = make([]uint32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint32(v)
|
||||
}
|
||||
case []uint32:
|
||||
array = value
|
||||
case []uint64:
|
||||
array = make([]uint32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint32(v)
|
||||
}
|
||||
case []bool:
|
||||
array = make([]uint32, len(value))
|
||||
for k, v := range value {
|
||||
if v {
|
||||
array[k] = 1
|
||||
} else {
|
||||
array[k] = 0
|
||||
}
|
||||
}
|
||||
case []float32:
|
||||
array = make([]uint32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Uint32(v)
|
||||
}
|
||||
case []float64:
|
||||
array = make([]uint32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Uint32(v)
|
||||
}
|
||||
case []interface{}:
|
||||
array = make([]uint32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Uint32(v)
|
||||
}
|
||||
case [][]byte:
|
||||
array = make([]uint32, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Uint32(v)
|
||||
}
|
||||
}
|
||||
if array != nil {
|
||||
return array
|
||||
}
|
||||
|
||||
// Default handler.
|
||||
if v, ok := any.(localinterface.IUints); ok {
|
||||
return Uint32s(v.Uints())
|
||||
}
|
||||
if v, ok := any.(localinterface.IInterfaces); ok {
|
||||
return Uint32s(v.Interfaces())
|
||||
}
|
||||
// Not a common type, it then uses reflection for conversion.
|
||||
originValueAndKind := reflection.OriginValueAndKind(any)
|
||||
switch originValueAndKind.OriginKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
var (
|
||||
length = originValueAndKind.OriginValue.Len()
|
||||
slice = make([]uint32, length)
|
||||
)
|
||||
for i := 0; i < length; i++ {
|
||||
slice[i] = Uint32(originValueAndKind.OriginValue.Index(i).Interface())
|
||||
}
|
||||
return slice
|
||||
|
||||
default:
|
||||
if originValueAndKind.OriginValue.IsZero() {
|
||||
return []uint32{}
|
||||
}
|
||||
return []uint32{Uint32(any)}
|
||||
}
|
||||
result, _ := defaultConverter.SliceUint32(any, SliceOption{
|
||||
ContinueOnError: true,
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
// Uint64s converts `any` to []uint64.
|
||||
func Uint64s(any interface{}) []uint64 {
|
||||
if empty.IsNil(any) {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
array []uint64 = nil
|
||||
)
|
||||
switch value := any.(type) {
|
||||
case []string:
|
||||
array = make([]uint64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Uint64(v)
|
||||
}
|
||||
case []int8:
|
||||
array = make([]uint64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint64(v)
|
||||
}
|
||||
case []int16:
|
||||
array = make([]uint64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint64(v)
|
||||
}
|
||||
case []int32:
|
||||
array = make([]uint64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint64(v)
|
||||
}
|
||||
case []int64:
|
||||
array = make([]uint64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint64(v)
|
||||
}
|
||||
case []uint:
|
||||
array = make([]uint64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint64(v)
|
||||
}
|
||||
case []uint8:
|
||||
if json.Valid(value) {
|
||||
if _ = json.UnmarshalUseNumber(value, &array); array != nil {
|
||||
return array
|
||||
}
|
||||
if bytes.EqualFold([]byte("null"), value) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
array = make([]uint64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint64(v)
|
||||
}
|
||||
case string:
|
||||
byteValue := []byte(value)
|
||||
if json.Valid(byteValue) {
|
||||
if _ = json.UnmarshalUseNumber(byteValue, &array); array != nil {
|
||||
return array
|
||||
}
|
||||
if strings.EqualFold(value, "null") {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if value == "" {
|
||||
return []uint64{}
|
||||
}
|
||||
if utils.IsNumeric(value) {
|
||||
return []uint64{Uint64(value)}
|
||||
}
|
||||
case []uint16:
|
||||
array = make([]uint64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint64(v)
|
||||
}
|
||||
case []uint32:
|
||||
array = make([]uint64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = uint64(v)
|
||||
}
|
||||
case []uint64:
|
||||
array = value
|
||||
case []bool:
|
||||
array = make([]uint64, len(value))
|
||||
for k, v := range value {
|
||||
if v {
|
||||
array[k] = 1
|
||||
} else {
|
||||
array[k] = 0
|
||||
}
|
||||
}
|
||||
case []float32:
|
||||
array = make([]uint64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Uint64(v)
|
||||
}
|
||||
case []float64:
|
||||
array = make([]uint64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Uint64(v)
|
||||
}
|
||||
case []interface{}:
|
||||
array = make([]uint64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Uint64(v)
|
||||
}
|
||||
case [][]byte:
|
||||
array = make([]uint64, len(value))
|
||||
for k, v := range value {
|
||||
array[k] = Uint64(v)
|
||||
}
|
||||
}
|
||||
if array != nil {
|
||||
return array
|
||||
}
|
||||
// Default handler.
|
||||
if v, ok := any.(localinterface.IUints); ok {
|
||||
return Uint64s(v.Uints())
|
||||
}
|
||||
if v, ok := any.(localinterface.IInterfaces); ok {
|
||||
return Uint64s(v.Interfaces())
|
||||
}
|
||||
// Not a common type, it then uses reflection for conversion.
|
||||
originValueAndKind := reflection.OriginValueAndKind(any)
|
||||
switch originValueAndKind.OriginKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
var (
|
||||
length = originValueAndKind.OriginValue.Len()
|
||||
slice = make([]uint64, length)
|
||||
)
|
||||
for i := 0; i < length; i++ {
|
||||
slice[i] = Uint64(originValueAndKind.OriginValue.Index(i).Interface())
|
||||
}
|
||||
return slice
|
||||
|
||||
default:
|
||||
if originValueAndKind.OriginValue.IsZero() {
|
||||
return []uint64{}
|
||||
}
|
||||
return []uint64{Uint64(any)}
|
||||
}
|
||||
result, _ := defaultConverter.SliceUint64(any, SliceOption{
|
||||
ContinueOnError: true,
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
@ -6,19 +6,6 @@
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/utils"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/structcache"
|
||||
)
|
||||
|
||||
// Struct maps the params key-value pairs to the corresponding struct object's attributes.
|
||||
// The third parameter `mapping` is unnecessary, indicating the mapping rules between the
|
||||
// custom key name and the attribute name(case-sensitive).
|
||||
@ -32,609 +19,17 @@ import (
|
||||
// It will automatically convert the first letter of the key to uppercase
|
||||
// in mapping procedure to do the matching.
|
||||
// It ignores the map key, if it does not match.
|
||||
func Struct(params interface{}, pointer interface{}, paramKeyToAttrMap ...map[string]string) (err error) {
|
||||
func Struct(params any, pointer any, paramKeyToAttrMap ...map[string]string) (err error) {
|
||||
return Scan(params, pointer, paramKeyToAttrMap...)
|
||||
}
|
||||
|
||||
// StructTag acts as Struct but also with support for priority tag feature, which retrieves the
|
||||
// specified priorityTagAndFieldName for `params` key-value items to struct attribute names mapping.
|
||||
// The parameter `priorityTag` supports multiple priorityTagAndFieldName that can be joined with char ','.
|
||||
func StructTag(params interface{}, pointer interface{}, priorityTag string) (err error) {
|
||||
return doStruct(params, pointer, nil, priorityTag)
|
||||
}
|
||||
|
||||
// doStruct is the core internal converting function for any data to struct.
|
||||
func doStruct(
|
||||
params interface{}, pointer interface{}, paramKeyToAttrMap map[string]string, priorityTag string,
|
||||
) (err error) {
|
||||
if params == nil {
|
||||
// If `params` is nil, no conversion.
|
||||
return nil
|
||||
}
|
||||
if pointer == nil {
|
||||
return gerror.NewCode(gcode.CodeInvalidParameter, "object pointer cannot be nil")
|
||||
}
|
||||
|
||||
// JSON content converting.
|
||||
ok, err := doConvertWithJsonCheck(params, pointer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// Catch the panic, especially the reflection operation panics.
|
||||
if exception := recover(); exception != nil {
|
||||
if v, ok := exception.(error); ok && gerror.HasStack(v) {
|
||||
err = v
|
||||
} else {
|
||||
err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
var (
|
||||
paramsReflectValue reflect.Value
|
||||
paramsInterface interface{} // DO NOT use `params` directly as it might be type `reflect.Value`
|
||||
pointerReflectValue reflect.Value
|
||||
pointerReflectKind reflect.Kind
|
||||
pointerElemReflectValue reflect.Value // The reflection value to struct element.
|
||||
)
|
||||
if v, ok := params.(reflect.Value); ok {
|
||||
paramsReflectValue = v
|
||||
} else {
|
||||
paramsReflectValue = reflect.ValueOf(params)
|
||||
}
|
||||
paramsInterface = paramsReflectValue.Interface()
|
||||
if v, ok := pointer.(reflect.Value); ok {
|
||||
pointerReflectValue = v
|
||||
pointerElemReflectValue = v
|
||||
} else {
|
||||
pointerReflectValue = reflect.ValueOf(pointer)
|
||||
pointerReflectKind = pointerReflectValue.Kind()
|
||||
if pointerReflectKind != reflect.Ptr {
|
||||
return gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
"destination pointer should be type of '*struct', but got '%v'",
|
||||
pointerReflectKind,
|
||||
)
|
||||
}
|
||||
// Using IsNil on reflect.Ptr variable is OK.
|
||||
if !pointerReflectValue.IsValid() || pointerReflectValue.IsNil() {
|
||||
return gerror.NewCode(
|
||||
gcode.CodeInvalidParameter,
|
||||
"destination pointer cannot be nil",
|
||||
)
|
||||
}
|
||||
pointerElemReflectValue = pointerReflectValue.Elem()
|
||||
}
|
||||
|
||||
// If `params` and `pointer` are the same type, the do directly assignment.
|
||||
// For performance enhancement purpose.
|
||||
if ok = doConvertWithTypeCheck(paramsReflectValue, pointerElemReflectValue); ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// custom convert.
|
||||
if ok, err = callCustomConverter(paramsReflectValue, pointerReflectValue); ok {
|
||||
return err
|
||||
}
|
||||
|
||||
// Normal unmarshalling interfaces checks.
|
||||
if ok, err = bindVarToReflectValueWithInterfaceCheck(pointerReflectValue, paramsInterface); ok {
|
||||
return err
|
||||
}
|
||||
|
||||
// It automatically creates struct object if necessary.
|
||||
// For example, if `pointer` is **User, then `elem` is *User, which is a pointer to User.
|
||||
if pointerElemReflectValue.Kind() == reflect.Ptr {
|
||||
if !pointerElemReflectValue.IsValid() || pointerElemReflectValue.IsNil() {
|
||||
e := reflect.New(pointerElemReflectValue.Type().Elem())
|
||||
pointerElemReflectValue.Set(e)
|
||||
defer func() {
|
||||
if err != nil {
|
||||
// If it is converted failed, it reset the `pointer` to nil.
|
||||
pointerReflectValue.Elem().Set(reflect.Zero(pointerReflectValue.Type().Elem()))
|
||||
}
|
||||
}()
|
||||
}
|
||||
// if v, ok := pointerElemReflectValue.Interface().(localinterface.IUnmarshalValue); ok {
|
||||
// return v.UnmarshalValue(params)
|
||||
// }
|
||||
// Note that it's `pointerElemReflectValue` here not `pointerReflectValue`.
|
||||
if ok, err = bindVarToReflectValueWithInterfaceCheck(pointerElemReflectValue, paramsInterface); ok {
|
||||
return err
|
||||
}
|
||||
// Retrieve its element, may be struct at last.
|
||||
pointerElemReflectValue = pointerElemReflectValue.Elem()
|
||||
}
|
||||
paramsMap, ok := paramsInterface.(map[string]interface{})
|
||||
if !ok {
|
||||
// paramsMap is the map[string]interface{} type variable for params.
|
||||
// DO NOT use MapDeep here.
|
||||
paramsMap = doMapConvert(paramsInterface, recursiveTypeAuto, true)
|
||||
if paramsMap == nil {
|
||||
return gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`convert params from "%#v" to "map[string]interface{}" failed`,
|
||||
params,
|
||||
)
|
||||
}
|
||||
}
|
||||
// Nothing to be done as the parameters are empty.
|
||||
if len(paramsMap) == 0 {
|
||||
return nil
|
||||
}
|
||||
// Get struct info from cache or parse struct and cache the struct info.
|
||||
cachedStructInfo := structcache.GetCachedStructInfo(
|
||||
pointerElemReflectValue.Type(), priorityTag,
|
||||
)
|
||||
// Nothing to be converted.
|
||||
if cachedStructInfo == nil {
|
||||
return nil
|
||||
}
|
||||
// For the structure types of 0 tagOrFiledNameToFieldInfoMap,
|
||||
// they also need to be cached to prevent invalid logic
|
||||
if cachedStructInfo.HasNoFields() {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
// Indicates that those values have been used and cannot be reused.
|
||||
usedParamsKeyOrTagNameMap = structcache.GetUsedParamsKeyOrTagNameMapFromPool()
|
||||
cachedFieldInfo *structcache.CachedFieldInfo
|
||||
paramsValue interface{}
|
||||
)
|
||||
defer structcache.PutUsedParamsKeyOrTagNameMapToPool(usedParamsKeyOrTagNameMap)
|
||||
|
||||
// Firstly, search according to custom mapping rules.
|
||||
// If a possible direct assignment is found, reduce the number of subsequent map searches.
|
||||
for paramKey, fieldName := range paramKeyToAttrMap {
|
||||
paramsValue, ok = paramsMap[paramKey]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
cachedFieldInfo = cachedStructInfo.GetFieldInfo(fieldName)
|
||||
if cachedFieldInfo != nil {
|
||||
fieldValue := cachedFieldInfo.GetFieldReflectValueFrom(pointerElemReflectValue)
|
||||
if err = bindVarToStructField(
|
||||
fieldValue,
|
||||
paramsValue,
|
||||
cachedFieldInfo,
|
||||
paramKeyToAttrMap,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(cachedFieldInfo.OtherSameNameField) > 0 {
|
||||
if err = setOtherSameNameField(
|
||||
cachedFieldInfo, paramsValue, pointerReflectValue, paramKeyToAttrMap,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
usedParamsKeyOrTagNameMap[paramKey] = struct{}{}
|
||||
}
|
||||
}
|
||||
// Already done converting for given `paramsMap`.
|
||||
if len(usedParamsKeyOrTagNameMap) == len(paramsMap) {
|
||||
return nil
|
||||
}
|
||||
return bindStructWithLoopFieldInfos(
|
||||
paramsMap, pointerElemReflectValue, paramKeyToAttrMap, usedParamsKeyOrTagNameMap, cachedStructInfo,
|
||||
)
|
||||
}
|
||||
|
||||
func setOtherSameNameField(
|
||||
cachedFieldInfo *structcache.CachedFieldInfo,
|
||||
srcValue any,
|
||||
structValue reflect.Value,
|
||||
paramKeyToAttrMap map[string]string,
|
||||
) (err error) {
|
||||
// loop the same field name of all sub attributes.
|
||||
for _, otherFieldInfo := range cachedFieldInfo.OtherSameNameField {
|
||||
fieldValue := cachedFieldInfo.GetOtherFieldReflectValueFrom(structValue, otherFieldInfo.FieldIndexes)
|
||||
if err = bindVarToStructField(fieldValue, srcValue, otherFieldInfo, paramKeyToAttrMap); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func bindStructWithLoopFieldInfos(
|
||||
paramsMap map[string]any,
|
||||
structValue reflect.Value,
|
||||
paramKeyToAttrMap map[string]string,
|
||||
usedParamsKeyOrTagNameMap map[string]struct{},
|
||||
cachedStructInfo *structcache.CachedStructInfo,
|
||||
) (err error) {
|
||||
var (
|
||||
cachedFieldInfo *structcache.CachedFieldInfo
|
||||
fuzzLastKey string
|
||||
fieldValue reflect.Value
|
||||
paramKey string
|
||||
paramValue any
|
||||
matched bool
|
||||
ok bool
|
||||
)
|
||||
for _, cachedFieldInfo = range cachedStructInfo.FieldConvertInfos {
|
||||
for _, fieldTag := range cachedFieldInfo.PriorityTagAndFieldName {
|
||||
if paramValue, ok = paramsMap[fieldTag]; !ok {
|
||||
continue
|
||||
}
|
||||
fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue)
|
||||
if err = bindVarToStructField(
|
||||
fieldValue, paramValue, cachedFieldInfo, paramKeyToAttrMap,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
// handle same field name in nested struct.
|
||||
if len(cachedFieldInfo.OtherSameNameField) > 0 {
|
||||
if err = setOtherSameNameField(
|
||||
cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
usedParamsKeyOrTagNameMap[fieldTag] = struct{}{}
|
||||
matched = true
|
||||
break
|
||||
}
|
||||
if matched {
|
||||
matched = false
|
||||
continue
|
||||
}
|
||||
|
||||
fuzzLastKey = cachedFieldInfo.LastFuzzyKey.Load().(string)
|
||||
if paramValue, ok = paramsMap[fuzzLastKey]; !ok {
|
||||
paramKey, paramValue = fuzzyMatchingFieldName(
|
||||
cachedFieldInfo.RemoveSymbolsFieldName, paramsMap, usedParamsKeyOrTagNameMap,
|
||||
)
|
||||
ok = paramKey != ""
|
||||
cachedFieldInfo.LastFuzzyKey.Store(paramKey)
|
||||
}
|
||||
if ok {
|
||||
fieldValue = cachedFieldInfo.GetFieldReflectValueFrom(structValue)
|
||||
if paramValue != nil {
|
||||
if err = bindVarToStructField(
|
||||
fieldValue, paramValue, cachedFieldInfo, paramKeyToAttrMap,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
// handle same field name in nested struct.
|
||||
if len(cachedFieldInfo.OtherSameNameField) > 0 {
|
||||
if err = setOtherSameNameField(
|
||||
cachedFieldInfo, paramValue, structValue, paramKeyToAttrMap,
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
usedParamsKeyOrTagNameMap[paramKey] = struct{}{}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// fuzzy matching rule:
|
||||
// to match field name and param key in case-insensitive and without symbols.
|
||||
func fuzzyMatchingFieldName(
|
||||
fieldName string,
|
||||
paramsMap map[string]any,
|
||||
usedParamsKeyMap map[string]struct{},
|
||||
) (string, any) {
|
||||
for paramKey, paramValue := range paramsMap {
|
||||
if _, ok := usedParamsKeyMap[paramKey]; ok {
|
||||
continue
|
||||
}
|
||||
removeParamKeyUnderline := utils.RemoveSymbols(paramKey)
|
||||
if strings.EqualFold(fieldName, removeParamKeyUnderline) {
|
||||
return paramKey, paramValue
|
||||
}
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// bindVarToStructField sets value to struct object attribute by name.
|
||||
func bindVarToStructField(
|
||||
fieldValue reflect.Value,
|
||||
srcValue interface{},
|
||||
cachedFieldInfo *structcache.CachedFieldInfo,
|
||||
paramKeyToAttrMap map[string]string,
|
||||
) (err error) {
|
||||
if !fieldValue.IsValid() {
|
||||
return nil
|
||||
}
|
||||
// CanSet checks whether attribute is public accessible.
|
||||
if !fieldValue.CanSet() {
|
||||
return nil
|
||||
}
|
||||
defer func() {
|
||||
if exception := recover(); exception != nil {
|
||||
if err = bindVarToReflectValue(fieldValue, srcValue, paramKeyToAttrMap); err != nil {
|
||||
err = gerror.Wrapf(err, `error binding srcValue to attribute "%s"`, cachedFieldInfo.FieldName())
|
||||
}
|
||||
}
|
||||
}()
|
||||
// Directly converting.
|
||||
if empty.IsNil(srcValue) {
|
||||
fieldValue.Set(reflect.Zero(fieldValue.Type()))
|
||||
return nil
|
||||
}
|
||||
// Try to call custom converter.
|
||||
// Issue: https://github.com/gogf/gf/issues/3099
|
||||
var (
|
||||
customConverterInput reflect.Value
|
||||
ok bool
|
||||
)
|
||||
if cachedFieldInfo.IsCustomConvert {
|
||||
if customConverterInput, ok = srcValue.(reflect.Value); !ok {
|
||||
customConverterInput = reflect.ValueOf(srcValue)
|
||||
}
|
||||
if ok, err = callCustomConverter(customConverterInput, fieldValue); ok || err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if cachedFieldInfo.IsCommonInterface {
|
||||
if ok, err = bindVarToReflectValueWithInterfaceCheck(fieldValue, srcValue); ok || err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Common types use fast assignment logic
|
||||
if cachedFieldInfo.ConvertFunc != nil {
|
||||
cachedFieldInfo.ConvertFunc(srcValue, fieldValue)
|
||||
return nil
|
||||
}
|
||||
doConvertWithReflectValueSet(fieldValue, doConvertInput{
|
||||
FromValue: srcValue,
|
||||
ToTypeName: cachedFieldInfo.StructField.Type.String(),
|
||||
ReferValue: fieldValue,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindVarToReflectValueWithInterfaceCheck does bind using common interfaces checks.
|
||||
func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value interface{}) (bool, error) {
|
||||
var pointer interface{}
|
||||
if reflectValue.Kind() != reflect.Ptr && reflectValue.CanAddr() {
|
||||
reflectValueAddr := reflectValue.Addr()
|
||||
if reflectValueAddr.IsNil() || !reflectValueAddr.IsValid() {
|
||||
return false, nil
|
||||
}
|
||||
// Not a pointer, but can token address, that makes it can be unmarshalled.
|
||||
pointer = reflectValue.Addr().Interface()
|
||||
} else {
|
||||
if reflectValue.IsNil() || !reflectValue.IsValid() {
|
||||
return false, nil
|
||||
}
|
||||
pointer = reflectValue.Interface()
|
||||
}
|
||||
// UnmarshalValue.
|
||||
if v, ok := pointer.(localinterface.IUnmarshalValue); ok {
|
||||
return ok, v.UnmarshalValue(value)
|
||||
}
|
||||
// UnmarshalText.
|
||||
if v, ok := pointer.(localinterface.IUnmarshalText); ok {
|
||||
var valueBytes []byte
|
||||
if b, ok := value.([]byte); ok {
|
||||
valueBytes = b
|
||||
} else if s, ok := value.(string); ok {
|
||||
valueBytes = []byte(s)
|
||||
} else if f, ok := value.(localinterface.IString); ok {
|
||||
valueBytes = []byte(f.String())
|
||||
}
|
||||
if len(valueBytes) > 0 {
|
||||
return ok, v.UnmarshalText(valueBytes)
|
||||
}
|
||||
}
|
||||
// UnmarshalJSON.
|
||||
if v, ok := pointer.(localinterface.IUnmarshalJSON); ok {
|
||||
var valueBytes []byte
|
||||
if b, ok := value.([]byte); ok {
|
||||
valueBytes = b
|
||||
} else if s, ok := value.(string); ok {
|
||||
valueBytes = []byte(s)
|
||||
} else if f, ok := value.(localinterface.IString); ok {
|
||||
valueBytes = []byte(f.String())
|
||||
}
|
||||
|
||||
if len(valueBytes) > 0 {
|
||||
// If it is not a valid JSON string, it then adds char `"` on its both sides to make it is.
|
||||
if !json.Valid(valueBytes) {
|
||||
newValueBytes := make([]byte, len(valueBytes)+2)
|
||||
newValueBytes[0] = '"'
|
||||
newValueBytes[len(newValueBytes)-1] = '"'
|
||||
copy(newValueBytes[1:], valueBytes)
|
||||
valueBytes = newValueBytes
|
||||
}
|
||||
return ok, v.UnmarshalJSON(valueBytes)
|
||||
}
|
||||
}
|
||||
if v, ok := pointer.(localinterface.ISet); ok {
|
||||
v.Set(value)
|
||||
return ok, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// bindVarToReflectValue sets `value` to reflect value object `structFieldValue`.
|
||||
func bindVarToReflectValue(
|
||||
structFieldValue reflect.Value, value interface{}, paramKeyToAttrMap map[string]string,
|
||||
) (err error) {
|
||||
// JSON content converting.
|
||||
ok, err := doConvertWithJsonCheck(value, structFieldValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
kind := structFieldValue.Kind()
|
||||
// Converting using `Set` interface implements, for some types.
|
||||
switch kind {
|
||||
case reflect.Slice, reflect.Array, reflect.Ptr, reflect.Interface:
|
||||
if !structFieldValue.IsNil() {
|
||||
if v, ok := structFieldValue.Interface().(localinterface.ISet); ok {
|
||||
v.Set(value)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Converting using reflection by kind.
|
||||
switch kind {
|
||||
case reflect.Map:
|
||||
return doMapToMap(value, structFieldValue, paramKeyToAttrMap)
|
||||
|
||||
case reflect.Struct:
|
||||
// Recursively converting for struct attribute.
|
||||
if err = doStruct(value, structFieldValue, nil, ""); err != nil {
|
||||
// Note there's reflect conversion mechanism here.
|
||||
structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
|
||||
}
|
||||
|
||||
// Note that the slice element might be type of struct,
|
||||
// so it uses Struct function doing the converting internally.
|
||||
case reflect.Slice, reflect.Array:
|
||||
var (
|
||||
reflectArray reflect.Value
|
||||
reflectValue = reflect.ValueOf(value)
|
||||
)
|
||||
if reflectValue.Kind() == reflect.Slice || reflectValue.Kind() == reflect.Array {
|
||||
reflectArray = reflect.MakeSlice(structFieldValue.Type(), reflectValue.Len(), reflectValue.Len())
|
||||
if reflectValue.Len() > 0 {
|
||||
var (
|
||||
elemType = reflectArray.Index(0).Type()
|
||||
elemTypeName string
|
||||
converted bool
|
||||
)
|
||||
for i := 0; i < reflectValue.Len(); i++ {
|
||||
converted = false
|
||||
elemTypeName = elemType.Name()
|
||||
if elemTypeName == "" {
|
||||
elemTypeName = elemType.String()
|
||||
}
|
||||
var elem reflect.Value
|
||||
if elemType.Kind() == reflect.Ptr {
|
||||
elem = reflect.New(elemType.Elem()).Elem()
|
||||
} else {
|
||||
elem = reflect.New(elemType).Elem()
|
||||
}
|
||||
if elem.Kind() == reflect.Struct {
|
||||
if err = doStruct(reflectValue.Index(i).Interface(), elem, nil, ""); err == nil {
|
||||
converted = true
|
||||
}
|
||||
}
|
||||
if !converted {
|
||||
doConvertWithReflectValueSet(elem, doConvertInput{
|
||||
FromValue: reflectValue.Index(i).Interface(),
|
||||
ToTypeName: elemTypeName,
|
||||
ReferValue: elem,
|
||||
})
|
||||
}
|
||||
if elemType.Kind() == reflect.Ptr {
|
||||
// Before it sets the `elem` to array, do pointer converting if necessary.
|
||||
elem = elem.Addr()
|
||||
}
|
||||
reflectArray.Index(i).Set(elem)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var (
|
||||
elem reflect.Value
|
||||
elemType = structFieldValue.Type().Elem()
|
||||
elemTypeName = elemType.Name()
|
||||
converted bool
|
||||
)
|
||||
switch reflectValue.Kind() {
|
||||
case reflect.String:
|
||||
// Value is empty string.
|
||||
if reflectValue.IsZero() {
|
||||
var elemKind = elemType.Kind()
|
||||
// Try to find the original type kind of the slice element.
|
||||
if elemKind == reflect.Ptr {
|
||||
elemKind = elemType.Elem().Kind()
|
||||
}
|
||||
switch elemKind {
|
||||
case reflect.String:
|
||||
// Empty string cannot be assigned to string slice.
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if elemTypeName == "" {
|
||||
elemTypeName = elemType.String()
|
||||
}
|
||||
if elemType.Kind() == reflect.Ptr {
|
||||
elem = reflect.New(elemType.Elem()).Elem()
|
||||
} else {
|
||||
elem = reflect.New(elemType).Elem()
|
||||
}
|
||||
if elem.Kind() == reflect.Struct {
|
||||
if err = doStruct(value, elem, nil, ""); err == nil {
|
||||
converted = true
|
||||
}
|
||||
}
|
||||
if !converted {
|
||||
doConvertWithReflectValueSet(elem, doConvertInput{
|
||||
FromValue: value,
|
||||
ToTypeName: elemTypeName,
|
||||
ReferValue: elem,
|
||||
})
|
||||
}
|
||||
if elemType.Kind() == reflect.Ptr {
|
||||
// Before it sets the `elem` to array, do pointer converting if necessary.
|
||||
elem = elem.Addr()
|
||||
}
|
||||
reflectArray = reflect.MakeSlice(structFieldValue.Type(), 1, 1)
|
||||
reflectArray.Index(0).Set(elem)
|
||||
}
|
||||
structFieldValue.Set(reflectArray)
|
||||
|
||||
case reflect.Ptr:
|
||||
if structFieldValue.IsNil() || structFieldValue.IsZero() {
|
||||
// Nil or empty pointer, it creates a new one.
|
||||
item := reflect.New(structFieldValue.Type().Elem())
|
||||
if ok, err = bindVarToReflectValueWithInterfaceCheck(item, value); ok {
|
||||
structFieldValue.Set(item)
|
||||
return err
|
||||
}
|
||||
elem := item.Elem()
|
||||
if err = bindVarToReflectValue(elem, value, paramKeyToAttrMap); err == nil {
|
||||
structFieldValue.Set(elem.Addr())
|
||||
}
|
||||
} else {
|
||||
// Not empty pointer, it assigns values to it.
|
||||
return bindVarToReflectValue(structFieldValue.Elem(), value, paramKeyToAttrMap)
|
||||
}
|
||||
|
||||
// It mainly and specially handles the interface of nil value.
|
||||
case reflect.Interface:
|
||||
if value == nil {
|
||||
// Specially.
|
||||
structFieldValue.Set(reflect.ValueOf((*interface{})(nil)))
|
||||
} else {
|
||||
// Note there's reflect conversion mechanism here.
|
||||
structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
|
||||
}
|
||||
|
||||
default:
|
||||
defer func() {
|
||||
if exception := recover(); exception != nil {
|
||||
err = gerror.NewCodef(
|
||||
gcode.CodeInternalPanic,
|
||||
`cannot convert value "%+v" to type "%s":%+v`,
|
||||
value,
|
||||
structFieldValue.Type().String(),
|
||||
exception,
|
||||
)
|
||||
}
|
||||
}()
|
||||
// It here uses reflect converting `value` to type of the attribute and assigns
|
||||
// the result value to the attribute. It might fail and panic if the usual Go
|
||||
// conversion rules do not allow conversion.
|
||||
structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
|
||||
}
|
||||
return nil
|
||||
func StructTag(params any, pointer any, priorityTag string) (err error) {
|
||||
option := StructOption{
|
||||
PriorityTag: priorityTag,
|
||||
ContinueOnError: true,
|
||||
}
|
||||
return defaultConverter.Struct(params, pointer, option)
|
||||
}
|
||||
|
||||
@ -6,128 +6,25 @@
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
)
|
||||
|
||||
// Structs converts any slice to given struct slice.
|
||||
// Also see Scan, Struct.
|
||||
func Structs(params interface{}, pointer interface{}, paramKeyToAttrMap ...map[string]string) (err error) {
|
||||
func Structs(params any, pointer any, paramKeyToAttrMap ...map[string]string) (err error) {
|
||||
return Scan(params, pointer, paramKeyToAttrMap...)
|
||||
}
|
||||
|
||||
// SliceStruct is alias of Structs.
|
||||
func SliceStruct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
func SliceStruct(params any, pointer any, mapping ...map[string]string) (err error) {
|
||||
return Structs(params, pointer, mapping...)
|
||||
}
|
||||
|
||||
// StructsTag acts as Structs but also with support for priority tag feature, which retrieves the
|
||||
// specified priorityTagAndFieldName for `params` key-value items to struct attribute names mapping.
|
||||
// The parameter `priorityTag` supports multiple priorityTagAndFieldName that can be joined with char ','.
|
||||
func StructsTag(params interface{}, pointer interface{}, priorityTag string) (err error) {
|
||||
return doStructs(params, pointer, nil, priorityTag)
|
||||
}
|
||||
|
||||
// doStructs converts any slice to given struct slice.
|
||||
//
|
||||
// It automatically checks and converts json string to []map if `params` is string/[]byte.
|
||||
//
|
||||
// The parameter `pointer` should be type of pointer to slice of struct.
|
||||
// Note that if `pointer` is a pointer to another pointer of type of slice of struct,
|
||||
// it will create the struct/pointer internally.
|
||||
func doStructs(
|
||||
params interface{}, pointer interface{}, paramKeyToAttrMap map[string]string, priorityTag string,
|
||||
) (err error) {
|
||||
defer func() {
|
||||
// Catch the panic, especially the reflection operation panics.
|
||||
if exception := recover(); exception != nil {
|
||||
if v, ok := exception.(error); ok && gerror.HasStack(v) {
|
||||
err = v
|
||||
} else {
|
||||
err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Pointer type check.
|
||||
pointerRv, ok := pointer.(reflect.Value)
|
||||
if !ok {
|
||||
pointerRv = reflect.ValueOf(pointer)
|
||||
if kind := pointerRv.Kind(); kind != reflect.Ptr {
|
||||
return gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
"pointer should be type of pointer, but got: %v", kind,
|
||||
)
|
||||
}
|
||||
}
|
||||
// Converting `params` to map slice.
|
||||
var (
|
||||
paramsList []interface{}
|
||||
paramsRv = reflect.ValueOf(params)
|
||||
paramsKind = paramsRv.Kind()
|
||||
)
|
||||
for paramsKind == reflect.Ptr {
|
||||
paramsRv = paramsRv.Elem()
|
||||
paramsKind = paramsRv.Kind()
|
||||
}
|
||||
switch paramsKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
paramsList = make([]interface{}, paramsRv.Len())
|
||||
for i := 0; i < paramsRv.Len(); i++ {
|
||||
paramsList[i] = paramsRv.Index(i).Interface()
|
||||
}
|
||||
default:
|
||||
var paramsMaps = Maps(params)
|
||||
paramsList = make([]interface{}, len(paramsMaps))
|
||||
for i := 0; i < len(paramsMaps); i++ {
|
||||
paramsList[i] = paramsMaps[i]
|
||||
}
|
||||
}
|
||||
// If `params` is an empty slice, no conversion.
|
||||
if len(paramsList) == 0 {
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
reflectElemArray = reflect.MakeSlice(pointerRv.Type().Elem(), len(paramsList), len(paramsList))
|
||||
itemType = reflectElemArray.Index(0).Type()
|
||||
itemTypeKind = itemType.Kind()
|
||||
pointerRvElem = pointerRv.Elem()
|
||||
pointerRvLength = pointerRvElem.Len()
|
||||
)
|
||||
if itemTypeKind == reflect.Ptr {
|
||||
// Pointer element.
|
||||
for i := 0; i < len(paramsList); i++ {
|
||||
var tempReflectValue reflect.Value
|
||||
if i < pointerRvLength {
|
||||
// Might be nil.
|
||||
tempReflectValue = pointerRvElem.Index(i).Elem()
|
||||
}
|
||||
if !tempReflectValue.IsValid() {
|
||||
tempReflectValue = reflect.New(itemType.Elem()).Elem()
|
||||
}
|
||||
if err = doStruct(paramsList[i], tempReflectValue, paramKeyToAttrMap, priorityTag); err != nil {
|
||||
return err
|
||||
}
|
||||
reflectElemArray.Index(i).Set(tempReflectValue.Addr())
|
||||
}
|
||||
} else {
|
||||
// Struct element.
|
||||
for i := 0; i < len(paramsList); i++ {
|
||||
var tempReflectValue reflect.Value
|
||||
if i < pointerRvLength {
|
||||
tempReflectValue = pointerRvElem.Index(i)
|
||||
} else {
|
||||
tempReflectValue = reflect.New(itemType).Elem()
|
||||
}
|
||||
if err = doStruct(paramsList[i], tempReflectValue, paramKeyToAttrMap, priorityTag); err != nil {
|
||||
return err
|
||||
}
|
||||
reflectElemArray.Index(i).Set(tempReflectValue)
|
||||
}
|
||||
}
|
||||
pointerRv.Elem().Set(reflectElemArray)
|
||||
return nil
|
||||
func StructsTag(params any, pointer any, priorityTag string) (err error) {
|
||||
return defaultConverter.Structs(params, pointer, SliceOption{
|
||||
ContinueOnError: true,
|
||||
}, StructOption{
|
||||
PriorityTag: priorityTag,
|
||||
ContinueOnError: true,
|
||||
})
|
||||
}
|
||||
|
||||
@ -9,40 +9,21 @@ package gconv
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/utils"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
|
||||
)
|
||||
|
||||
// Time converts `any` to time.Time.
|
||||
func Time(any interface{}, format ...string) time.Time {
|
||||
// It's already this type.
|
||||
if len(format) == 0 {
|
||||
if v, ok := any.(time.Time); ok {
|
||||
return v
|
||||
}
|
||||
}
|
||||
if t := GTime(any, format...); t != nil {
|
||||
return t.Time
|
||||
}
|
||||
return time.Time{}
|
||||
func Time(any any, format ...string) time.Time {
|
||||
t, _ := defaultConverter.Time(any, format...)
|
||||
return t
|
||||
}
|
||||
|
||||
// Duration converts `any` to time.Duration.
|
||||
// If `any` is string, then it uses time.ParseDuration to convert it.
|
||||
// If `any` is numeric, then it converts `any` as nanoseconds.
|
||||
func Duration(any interface{}) time.Duration {
|
||||
// It's already this type.
|
||||
if v, ok := any.(time.Duration); ok {
|
||||
return v
|
||||
}
|
||||
s := String(any)
|
||||
if !utils.IsNumeric(s) {
|
||||
d, _ := gtime.ParseDuration(s)
|
||||
return d
|
||||
}
|
||||
return time.Duration(Int64(any))
|
||||
func Duration(any any) time.Duration {
|
||||
d, _ := defaultConverter.Duration(any)
|
||||
return d
|
||||
}
|
||||
|
||||
// GTime converts `any` to *gtime.Time.
|
||||
@ -50,43 +31,7 @@ func Duration(any interface{}) time.Duration {
|
||||
// It returns the converted value that matched the first format of the formats slice.
|
||||
// If no `format` given, it converts `any` using gtime.NewFromTimeStamp if `any` is numeric,
|
||||
// or using gtime.StrToTime if `any` is string.
|
||||
func GTime(any interface{}, format ...string) *gtime.Time {
|
||||
if empty.IsNil(any) {
|
||||
return nil
|
||||
}
|
||||
if v, ok := any.(localinterface.IGTime); ok {
|
||||
return v.GTime(format...)
|
||||
}
|
||||
// It's already this type.
|
||||
if len(format) == 0 {
|
||||
if v, ok := any.(*gtime.Time); ok {
|
||||
return v
|
||||
}
|
||||
if t, ok := any.(time.Time); ok {
|
||||
return gtime.New(t)
|
||||
}
|
||||
if t, ok := any.(*time.Time); ok {
|
||||
return gtime.New(t)
|
||||
}
|
||||
}
|
||||
s := String(any)
|
||||
if len(s) == 0 {
|
||||
return gtime.New()
|
||||
}
|
||||
// Priority conversion using given format.
|
||||
if len(format) > 0 {
|
||||
for _, item := range format {
|
||||
t, err := gtime.StrToTimeFormat(s, item)
|
||||
if t != nil && err == nil {
|
||||
return t
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if utils.IsNumeric(s) {
|
||||
return gtime.NewFromTimeStamp(Int64(s))
|
||||
} else {
|
||||
t, _ := gtime.StrToTime(s)
|
||||
return t
|
||||
}
|
||||
func GTime(any any, format ...string) *gtime.Time {
|
||||
t, _ := defaultConverter.GTime(any, format...)
|
||||
return t
|
||||
}
|
||||
|
||||
@ -6,181 +6,32 @@
|
||||
|
||||
package gconv
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gbinary"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
|
||||
)
|
||||
|
||||
// Uint converts `any` to uint.
|
||||
func Uint(any any) uint {
|
||||
v, _ := doUint(any)
|
||||
v, _ := defaultConverter.Uint(any)
|
||||
return v
|
||||
}
|
||||
|
||||
func doUint(any any) (uint, error) {
|
||||
if empty.IsNil(any) {
|
||||
return 0, nil
|
||||
}
|
||||
if v, ok := any.(uint); ok {
|
||||
return v, nil
|
||||
}
|
||||
v, err := doUint64(any)
|
||||
return uint(v), err
|
||||
}
|
||||
|
||||
// Uint8 converts `any` to uint8.
|
||||
func Uint8(any any) uint8 {
|
||||
v, _ := doUint8(any)
|
||||
v, _ := defaultConverter.Uint8(any)
|
||||
return v
|
||||
}
|
||||
|
||||
func doUint8(any any) (uint8, error) {
|
||||
if empty.IsNil(any) {
|
||||
return 0, nil
|
||||
}
|
||||
if v, ok := any.(uint8); ok {
|
||||
return v, nil
|
||||
}
|
||||
v, err := doUint64(any)
|
||||
return uint8(v), err
|
||||
}
|
||||
|
||||
// Uint16 converts `any` to uint16.
|
||||
func Uint16(any any) uint16 {
|
||||
v, _ := doUint16(any)
|
||||
v, _ := defaultConverter.Uint16(any)
|
||||
return v
|
||||
}
|
||||
|
||||
func doUint16(any any) (uint16, error) {
|
||||
if empty.IsNil(any) {
|
||||
return 0, nil
|
||||
}
|
||||
if v, ok := any.(uint16); ok {
|
||||
return v, nil
|
||||
}
|
||||
v, err := doUint64(any)
|
||||
return uint16(v), err
|
||||
}
|
||||
|
||||
// Uint32 converts `any` to uint32.
|
||||
func Uint32(any any) uint32 {
|
||||
v, _ := doUint32(any)
|
||||
v, _ := defaultConverter.Uint32(any)
|
||||
return v
|
||||
}
|
||||
|
||||
func doUint32(any any) (uint32, error) {
|
||||
if empty.IsNil(any) {
|
||||
return 0, nil
|
||||
}
|
||||
if v, ok := any.(uint32); ok {
|
||||
return v, nil
|
||||
}
|
||||
v, err := doUint64(any)
|
||||
return uint32(v), err
|
||||
}
|
||||
|
||||
// Uint64 converts `any` to uint64.
|
||||
func Uint64(any any) uint64 {
|
||||
v, _ := doUint64(any)
|
||||
v, _ := defaultConverter.Uint64(any)
|
||||
return v
|
||||
}
|
||||
|
||||
func doUint64(any any) (uint64, error) {
|
||||
if empty.IsNil(any) {
|
||||
return 0, nil
|
||||
}
|
||||
if v, ok := any.(uint64); ok {
|
||||
return v, nil
|
||||
}
|
||||
rv := reflect.ValueOf(any)
|
||||
switch rv.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
val := rv.Int()
|
||||
if val < 0 {
|
||||
return uint64(val), gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`cannot convert negative value "%d" to uint64`,
|
||||
val,
|
||||
)
|
||||
}
|
||||
return uint64(val), nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return rv.Uint(), nil
|
||||
case reflect.Uintptr:
|
||||
return rv.Uint(), nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
val := rv.Float()
|
||||
if val < 0 {
|
||||
return uint64(val), gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`cannot convert negative value "%f" to uint64`,
|
||||
val,
|
||||
)
|
||||
}
|
||||
return uint64(val), nil
|
||||
case reflect.Bool:
|
||||
if rv.Bool() {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
case reflect.Ptr:
|
||||
if rv.IsNil() {
|
||||
return 0, nil
|
||||
}
|
||||
if f, ok := any.(localinterface.IUint64); ok {
|
||||
return f.Uint64(), nil
|
||||
}
|
||||
return doUint64(rv.Elem().Interface())
|
||||
case reflect.Slice:
|
||||
if rv.Type().Elem().Kind() == reflect.Uint8 {
|
||||
return gbinary.DecodeToUint64(rv.Bytes()), nil
|
||||
}
|
||||
return 0, gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`unsupport slice type "%s" for converting to uint64`,
|
||||
rv.Type().String(),
|
||||
)
|
||||
case reflect.String:
|
||||
var s = rv.String()
|
||||
// Hexadecimal
|
||||
if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') {
|
||||
v, err := strconv.ParseUint(s[2:], 16, 64)
|
||||
if err == nil {
|
||||
return v, nil
|
||||
}
|
||||
return 0, gerror.WrapCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
err,
|
||||
`cannot convert hexadecimal string "%s" to uint64`,
|
||||
s,
|
||||
)
|
||||
}
|
||||
// Decimal
|
||||
if v, err := strconv.ParseUint(s, 10, 64); err == nil {
|
||||
return v, nil
|
||||
}
|
||||
// Float64
|
||||
if v, err := doFloat64(any); err == nil {
|
||||
if math.IsNaN(v) {
|
||||
return 0, nil
|
||||
}
|
||||
return uint64(v), nil
|
||||
}
|
||||
default:
|
||||
if f, ok := any.(localinterface.IUint64); ok {
|
||||
return f.Uint64(), nil
|
||||
}
|
||||
}
|
||||
return 0, gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`unsupport value type "%s" for converting to uint64`,
|
||||
reflect.TypeOf(any).String(),
|
||||
)
|
||||
}
|
||||
|
||||
@ -92,7 +92,7 @@ func Benchmark_Struct_Basic(b *testing.B) {
|
||||
|
||||
func Benchmark_doStruct_Fields8_Basic_MapToStruct(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
doStruct(structMapFields8, structPointer8, map[string]string{}, "")
|
||||
defaultConverter.Struct(structMapFields8, structPointer8, StructOption{})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,6 +7,9 @@
|
||||
package gconv_test
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
@ -84,3 +87,87 @@ func TestConvertWithRefer(t *testing.T) {
|
||||
t.AssertNE(gconv.ConvertWithRefer("1.01", false), false)
|
||||
})
|
||||
}
|
||||
|
||||
func testAnyToMyInt(from any, to reflect.Value) error {
|
||||
switch x := from.(type) {
|
||||
case int:
|
||||
to.SetInt(123456)
|
||||
default:
|
||||
return fmt.Errorf("unsupported type %T(%v)", x, x)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func testAnyToSqlNullType(_ any, to reflect.Value) error {
|
||||
if to.Kind() != reflect.Ptr {
|
||||
to = to.Addr()
|
||||
}
|
||||
return to.Interface().(sql.Scanner).Scan(123456)
|
||||
}
|
||||
|
||||
func TestNewConverter(t *testing.T) {
|
||||
type Dst[T any] struct {
|
||||
A T
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
conv := gconv.NewConverter()
|
||||
conv.RegisterAnyConverterFunc(testAnyToMyInt, reflect.TypeOf((*myInt)(nil)))
|
||||
var dst Dst[myInt]
|
||||
err := conv.Struct(map[string]any{
|
||||
"a": 1200,
|
||||
}, &dst, gconv.StructOption{})
|
||||
t.AssertNil(err)
|
||||
t.Assert(dst, Dst[myInt]{
|
||||
A: 123456,
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
conv := gconv.NewConverter()
|
||||
conv.RegisterAnyConverterFunc(testAnyToMyInt, reflect.TypeOf((myInt)(0)))
|
||||
var dst Dst[*myInt]
|
||||
err := conv.Struct(map[string]any{
|
||||
"a": 1200,
|
||||
}, &dst, gconv.StructOption{})
|
||||
t.AssertNil(err)
|
||||
t.Assert(*dst.A, 123456)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
conv := gconv.NewConverter()
|
||||
conv.RegisterAnyConverterFunc(testAnyToSqlNullType, reflect.TypeOf((*sql.Scanner)(nil)))
|
||||
type sqlNullDst struct {
|
||||
A sql.Null[int]
|
||||
B sql.Null[float32]
|
||||
C sql.NullInt64
|
||||
D sql.NullString
|
||||
|
||||
E *sql.Null[int]
|
||||
F *sql.Null[float32]
|
||||
G *sql.NullInt64
|
||||
H *sql.NullString
|
||||
}
|
||||
var dst sqlNullDst
|
||||
err := conv.Struct(map[string]any{
|
||||
"a": 12,
|
||||
"b": 34,
|
||||
"c": 56,
|
||||
"d": "sqlNullString",
|
||||
"e": 12,
|
||||
"f": 34,
|
||||
"g": 56,
|
||||
"h": "sqlNullString",
|
||||
}, &dst, gconv.StructOption{})
|
||||
t.AssertNil(err)
|
||||
t.Assert(dst, sqlNullDst{
|
||||
A: sql.Null[int]{V: 123456, Valid: true},
|
||||
B: sql.Null[float32]{V: 123456, Valid: true},
|
||||
C: sql.NullInt64{Int64: 123456, Valid: true},
|
||||
D: sql.NullString{String: "123456", Valid: true},
|
||||
|
||||
E: &sql.Null[int]{V: 123456, Valid: true},
|
||||
F: &sql.Null[float32]{V: 123456, Valid: true},
|
||||
G: &sql.NullInt64{Int64: 123456, Valid: true},
|
||||
H: &sql.NullString{String: "123456", Valid: true},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
180
util/gconv/internal/converter/converter.go
Normal file
180
util/gconv/internal/converter/converter.go
Normal file
@ -0,0 +1,180 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package converter provides converting utilities for any types of variables.
|
||||
package converter
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/structcache"
|
||||
)
|
||||
|
||||
// AnyConvertFunc is the type for any type converting function.
|
||||
type AnyConvertFunc = structcache.AnyConvertFunc
|
||||
|
||||
// RecursiveType is the type for converting recursively.
|
||||
type RecursiveType string
|
||||
|
||||
const (
|
||||
RecursiveTypeAuto RecursiveType = "auto"
|
||||
RecursiveTypeTrue RecursiveType = "true"
|
||||
)
|
||||
|
||||
type (
|
||||
converterInType = reflect.Type
|
||||
converterOutType = reflect.Type
|
||||
converterFunc = reflect.Value
|
||||
)
|
||||
|
||||
// Converter implements the interface Converter.
|
||||
type Converter struct {
|
||||
internalConverter *structcache.Converter
|
||||
typeConverterFuncMap map[converterInType]map[converterOutType]converterFunc
|
||||
}
|
||||
|
||||
var (
|
||||
// Empty strings.
|
||||
emptyStringMap = map[string]struct{}{
|
||||
"": {},
|
||||
"0": {},
|
||||
"no": {},
|
||||
"off": {},
|
||||
"false": {},
|
||||
}
|
||||
)
|
||||
|
||||
// NewConverter creates and returns management object for type converting.
|
||||
func NewConverter() *Converter {
|
||||
cf := &Converter{
|
||||
internalConverter: structcache.NewConverter(),
|
||||
typeConverterFuncMap: make(map[converterInType]map[converterOutType]converterFunc),
|
||||
}
|
||||
cf.registerBuiltInAnyConvertFunc()
|
||||
return cf
|
||||
}
|
||||
|
||||
// RegisterTypeConverterFunc registers custom converter.
|
||||
// It must be registered before you use this custom converting feature.
|
||||
// It is suggested to do it in boot procedure of the process.
|
||||
//
|
||||
// Note:
|
||||
// 1. The parameter `fn` must be defined as pattern `func(T1) (T2, error)`.
|
||||
// It will convert type `T1` to type `T2`.
|
||||
// 2. The `T1` should not be type of pointer, but the `T2` should be type of pointer.
|
||||
func (c *Converter) RegisterTypeConverterFunc(f any) (err error) {
|
||||
var (
|
||||
fReflectType = reflect.TypeOf(f)
|
||||
errType = reflect.TypeOf((*error)(nil)).Elem()
|
||||
)
|
||||
if fReflectType.Kind() != reflect.Func ||
|
||||
fReflectType.NumIn() != 1 || fReflectType.NumOut() != 2 ||
|
||||
!fReflectType.Out(1).Implements(errType) {
|
||||
err = gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
"parameter must be type of converter function and defined as pattern `func(T1) (T2, error)`, "+
|
||||
"but defined as `%s`",
|
||||
fReflectType.String(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// The Key and Value of the converter map should not be pointer.
|
||||
var (
|
||||
inType = fReflectType.In(0)
|
||||
outType = fReflectType.Out(0)
|
||||
)
|
||||
if inType.Kind() == reflect.Pointer {
|
||||
err = gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
"invalid converter function `%s`: invalid input parameter type `%s`, should not be type of pointer",
|
||||
fReflectType.String(), inType.String(),
|
||||
)
|
||||
return
|
||||
}
|
||||
if outType.Kind() != reflect.Pointer {
|
||||
err = gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
"invalid converter function `%s`: invalid output parameter type `%s` should be type of pointer",
|
||||
fReflectType.String(), outType.String(),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
registeredOutTypeMap, ok := c.typeConverterFuncMap[inType]
|
||||
if !ok {
|
||||
registeredOutTypeMap = make(map[converterOutType]converterFunc)
|
||||
c.typeConverterFuncMap[inType] = registeredOutTypeMap
|
||||
}
|
||||
if _, ok = registeredOutTypeMap[outType]; ok {
|
||||
err = gerror.NewCodef(
|
||||
gcode.CodeInvalidOperation,
|
||||
"the converter parameter type `%s` to type `%s` has already been registered",
|
||||
inType.String(), outType.String(),
|
||||
)
|
||||
return
|
||||
}
|
||||
registeredOutTypeMap[outType] = reflect.ValueOf(f)
|
||||
c.internalConverter.MarkTypeConvertFunc(outType)
|
||||
return
|
||||
}
|
||||
|
||||
// RegisterAnyConverterFunc registers custom type converting function for specified types.
|
||||
func (c *Converter) RegisterAnyConverterFunc(convertFunc AnyConvertFunc, types ...reflect.Type) {
|
||||
for _, t := range types {
|
||||
c.internalConverter.RegisterAnyConvertFunc(t, convertFunc)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Converter) registerBuiltInAnyConvertFunc() {
|
||||
var (
|
||||
intType = reflect.TypeOf(0)
|
||||
int8Type = reflect.TypeOf(int8(0))
|
||||
int16Type = reflect.TypeOf(int16(0))
|
||||
int32Type = reflect.TypeOf(int32(0))
|
||||
int64Type = reflect.TypeOf(int64(0))
|
||||
uintType = reflect.TypeOf(uint(0))
|
||||
uint8Type = reflect.TypeOf(uint8(0))
|
||||
uint16Type = reflect.TypeOf(uint16(0))
|
||||
uint32Type = reflect.TypeOf(uint32(0))
|
||||
uint64Type = reflect.TypeOf(uint64(0))
|
||||
float32Type = reflect.TypeOf(float32(0))
|
||||
float64Type = reflect.TypeOf(float64(0))
|
||||
stringType = reflect.TypeOf("")
|
||||
bytesType = reflect.TypeOf([]byte{})
|
||||
boolType = reflect.TypeOf(false)
|
||||
timeType = reflect.TypeOf((*time.Time)(nil)).Elem()
|
||||
gtimeType = reflect.TypeOf((*gtime.Time)(nil)).Elem()
|
||||
)
|
||||
c.RegisterAnyConverterFunc(
|
||||
c.builtInAnyConvertFuncForInt64, intType, int8Type, int16Type, int32Type, int64Type,
|
||||
)
|
||||
c.RegisterAnyConverterFunc(
|
||||
c.builtInAnyConvertFuncForUint64, uintType, uint8Type, uint16Type, uint32Type, uint64Type,
|
||||
)
|
||||
c.RegisterAnyConverterFunc(
|
||||
c.builtInAnyConvertFuncForString, stringType,
|
||||
)
|
||||
c.RegisterAnyConverterFunc(
|
||||
c.builtInAnyConvertFuncForFloat64, float32Type, float64Type,
|
||||
)
|
||||
c.RegisterAnyConverterFunc(
|
||||
c.builtInAnyConvertFuncForBool, boolType,
|
||||
)
|
||||
c.RegisterAnyConverterFunc(
|
||||
c.builtInAnyConvertFuncForBytes, bytesType,
|
||||
)
|
||||
c.RegisterAnyConverterFunc(
|
||||
c.builtInAnyConvertFuncForTime, timeType,
|
||||
)
|
||||
c.RegisterAnyConverterFunc(
|
||||
c.builtInAnyConvertFuncForGTime, gtimeType,
|
||||
)
|
||||
}
|
||||
75
util/gconv/internal/converter/converter_bool.go
Normal file
75
util/gconv/internal/converter/converter_bool.go
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package converter
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
|
||||
)
|
||||
|
||||
// Bool converts `any` to bool.
|
||||
func (c *Converter) Bool(any any) (bool, error) {
|
||||
if empty.IsNil(any) {
|
||||
return false, nil
|
||||
}
|
||||
switch value := any.(type) {
|
||||
case bool:
|
||||
return value, nil
|
||||
case []byte:
|
||||
if _, ok := emptyStringMap[strings.ToLower(string(value))]; ok {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
case string:
|
||||
if _, ok := emptyStringMap[strings.ToLower(value)]; ok {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
default:
|
||||
if f, ok := value.(localinterface.IBool); ok {
|
||||
return f.Bool(), nil
|
||||
}
|
||||
rv := reflect.ValueOf(any)
|
||||
switch rv.Kind() {
|
||||
case reflect.Ptr:
|
||||
if rv.IsNil() {
|
||||
return false, nil
|
||||
}
|
||||
if rv.Type().Elem().Kind() == reflect.Bool {
|
||||
return rv.Elem().Bool(), nil
|
||||
}
|
||||
return c.Bool(rv.Elem().Interface())
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return rv.Int() != 0, nil
|
||||
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return rv.Uint() != 0, nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return rv.Float() != 0, nil
|
||||
case reflect.Bool:
|
||||
return rv.Bool(), nil
|
||||
// TODO:(Map,Array,Slice,Struct) It might panic here for these types.
|
||||
case reflect.Map, reflect.Array:
|
||||
fallthrough
|
||||
case reflect.Slice:
|
||||
return rv.Len() != 0, nil
|
||||
case reflect.Struct:
|
||||
return true, nil
|
||||
default:
|
||||
s, err := c.String(any)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if _, ok := emptyStringMap[strings.ToLower(s)]; ok {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
89
util/gconv/internal/converter/converter_builtin.go
Normal file
89
util/gconv/internal/converter/converter_builtin.go
Normal file
@ -0,0 +1,89 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package converter
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
func (c *Converter) builtInAnyConvertFuncForInt64(from any, to reflect.Value) error {
|
||||
v, err := c.Int64(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
to.SetInt(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Converter) builtInAnyConvertFuncForUint64(from any, to reflect.Value) error {
|
||||
v, err := c.Uint64(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
to.SetUint(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Converter) builtInAnyConvertFuncForString(from any, to reflect.Value) error {
|
||||
v, err := c.String(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
to.SetString(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Converter) builtInAnyConvertFuncForFloat64(from any, to reflect.Value) error {
|
||||
v, err := c.Float64(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
to.SetFloat(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Converter) builtInAnyConvertFuncForBool(from any, to reflect.Value) error {
|
||||
v, err := c.Bool(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
to.SetBool(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Converter) builtInAnyConvertFuncForBytes(from any, to reflect.Value) error {
|
||||
v, err := c.Bytes(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
to.SetBytes(v)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Converter) builtInAnyConvertFuncForTime(from any, to reflect.Value) error {
|
||||
t, err := c.Time(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*to.Addr().Interface().(*time.Time) = t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Converter) builtInAnyConvertFuncForGTime(from any, to reflect.Value) error {
|
||||
v, err := c.GTime(from)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if v == nil {
|
||||
v = gtime.New()
|
||||
}
|
||||
*to.Addr().Interface().(*gtime.Time) = *v
|
||||
return nil
|
||||
}
|
||||
68
util/gconv/internal/converter/converter_bytes.go
Normal file
68
util/gconv/internal/converter/converter_bytes.go
Normal file
@ -0,0 +1,68 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package converter
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gbinary"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/reflection"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
|
||||
)
|
||||
|
||||
// Bytes converts `any` to []byte.
|
||||
func (c *Converter) Bytes(any any) ([]byte, error) {
|
||||
if empty.IsNil(any) {
|
||||
return nil, nil
|
||||
}
|
||||
switch value := any.(type) {
|
||||
case string:
|
||||
return []byte(value), nil
|
||||
|
||||
case []byte:
|
||||
return value, nil
|
||||
|
||||
default:
|
||||
if f, ok := value.(localinterface.IBytes); ok {
|
||||
return f.Bytes(), nil
|
||||
}
|
||||
originValueAndKind := reflection.OriginValueAndKind(any)
|
||||
switch originValueAndKind.OriginKind {
|
||||
case reflect.Map:
|
||||
bytes, err := json.Marshal(any)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bytes, nil
|
||||
|
||||
case reflect.Array, reflect.Slice:
|
||||
var (
|
||||
ok = true
|
||||
bytes = make([]byte, originValueAndKind.OriginValue.Len())
|
||||
)
|
||||
for i := range bytes {
|
||||
int32Value, err := c.Int32(originValueAndKind.OriginValue.Index(i).Interface())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if int32Value < 0 || int32Value > math.MaxUint8 {
|
||||
ok = false
|
||||
break
|
||||
}
|
||||
bytes[i] = byte(int32Value)
|
||||
}
|
||||
if ok {
|
||||
return bytes, nil
|
||||
}
|
||||
default:
|
||||
}
|
||||
return gbinary.Encode(any), nil
|
||||
}
|
||||
}
|
||||
563
util/gconv/internal/converter/converter_convert.go
Normal file
563
util/gconv/internal/converter/converter_convert.go
Normal file
@ -0,0 +1,563 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package converter
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
)
|
||||
|
||||
// ConvertOption is the option for converting.
|
||||
type ConvertOption struct {
|
||||
// ExtraParams are extra values for implementing the converting.
|
||||
ExtraParams []any
|
||||
SliceOption SliceOption
|
||||
MapOption MapOption
|
||||
StructOption StructOption
|
||||
}
|
||||
|
||||
// ConvertWithTypeName converts the variable `fromValue` to the type `toTypeName`, the type `toTypeName` is specified by string.
|
||||
func (c *Converter) ConvertWithTypeName(fromValue any, toTypeName string, option ConvertOption) (any, error) {
|
||||
return c.doConvert(
|
||||
doConvertInput{
|
||||
FromValue: fromValue,
|
||||
ToTypeName: toTypeName,
|
||||
ReferValue: nil,
|
||||
},
|
||||
option,
|
||||
)
|
||||
}
|
||||
|
||||
// ConvertWithRefer converts the variable `fromValue` to the type referred by value `referValue`.
|
||||
func (c *Converter) ConvertWithRefer(fromValue, referValue any, option ConvertOption) (any, error) {
|
||||
var referValueRf reflect.Value
|
||||
if v, ok := referValue.(reflect.Value); ok {
|
||||
referValueRf = v
|
||||
} else {
|
||||
referValueRf = reflect.ValueOf(referValue)
|
||||
}
|
||||
return c.doConvert(
|
||||
doConvertInput{
|
||||
FromValue: fromValue,
|
||||
ToTypeName: referValueRf.Type().String(),
|
||||
ReferValue: referValue,
|
||||
},
|
||||
option,
|
||||
)
|
||||
}
|
||||
|
||||
type doConvertInput struct {
|
||||
FromValue any // Value that is converted from.
|
||||
ToTypeName string // Target value type name in string.
|
||||
ReferValue any // Referred value, a value in type `ToTypeName`. Note that its type might be reflect.Value.
|
||||
|
||||
// Marks that the value is already converted and set to `ReferValue`. Caller can ignore the returned result.
|
||||
// It is an attribute for internal usage purpose.
|
||||
alreadySetToReferValue bool
|
||||
}
|
||||
|
||||
// doConvert does commonly use types converting.
|
||||
func (c *Converter) doConvert(in doConvertInput, option ConvertOption) (convertedValue any, err error) {
|
||||
switch in.ToTypeName {
|
||||
case "int":
|
||||
return c.Int(in.FromValue)
|
||||
case "*int":
|
||||
if _, ok := in.FromValue.(*int); ok {
|
||||
return in.FromValue, nil
|
||||
}
|
||||
v, err := c.Int(in.FromValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v, nil
|
||||
|
||||
case "int8":
|
||||
return c.Int8(in.FromValue)
|
||||
case "*int8":
|
||||
if _, ok := in.FromValue.(*int8); ok {
|
||||
return in.FromValue, nil
|
||||
}
|
||||
v, err := c.Int8(in.FromValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v, nil
|
||||
|
||||
case "int16":
|
||||
return c.Int16(in.FromValue)
|
||||
case "*int16":
|
||||
if _, ok := in.FromValue.(*int16); ok {
|
||||
return in.FromValue, nil
|
||||
}
|
||||
v, err := c.Int16(in.FromValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v, nil
|
||||
|
||||
case "int32":
|
||||
return c.Int32(in.FromValue)
|
||||
case "*int32":
|
||||
if _, ok := in.FromValue.(*int32); ok {
|
||||
return in.FromValue, nil
|
||||
}
|
||||
v, err := c.Int32(in.FromValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v, nil
|
||||
|
||||
case "int64":
|
||||
return c.Int64(in.FromValue)
|
||||
case "*int64":
|
||||
if _, ok := in.FromValue.(*int64); ok {
|
||||
return in.FromValue, nil
|
||||
}
|
||||
v, err := c.Int64(in.FromValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v, nil
|
||||
|
||||
case "uint":
|
||||
return c.Uint(in.FromValue)
|
||||
case "*uint":
|
||||
if _, ok := in.FromValue.(*uint); ok {
|
||||
return in.FromValue, nil
|
||||
}
|
||||
v, err := c.Uint(in.FromValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v, nil
|
||||
|
||||
case "uint8":
|
||||
return c.Uint8(in.FromValue)
|
||||
case "*uint8":
|
||||
if _, ok := in.FromValue.(*uint8); ok {
|
||||
return in.FromValue, nil
|
||||
}
|
||||
v, err := c.Uint8(in.FromValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v, nil
|
||||
|
||||
case "uint16":
|
||||
return c.Uint16(in.FromValue)
|
||||
case "*uint16":
|
||||
if _, ok := in.FromValue.(*uint16); ok {
|
||||
return in.FromValue, nil
|
||||
}
|
||||
v, err := c.Uint16(in.FromValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v, nil
|
||||
|
||||
case "uint32":
|
||||
return c.Uint32(in.FromValue)
|
||||
case "*uint32":
|
||||
if _, ok := in.FromValue.(*uint32); ok {
|
||||
return in.FromValue, nil
|
||||
}
|
||||
v, err := c.Uint32(in.FromValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v, nil
|
||||
|
||||
case "uint64":
|
||||
return c.Uint64(in.FromValue)
|
||||
case "*uint64":
|
||||
if _, ok := in.FromValue.(*uint64); ok {
|
||||
return in.FromValue, nil
|
||||
}
|
||||
v, err := c.Uint64(in.FromValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v, nil
|
||||
|
||||
case "float32":
|
||||
return c.Float32(in.FromValue)
|
||||
case "*float32":
|
||||
if _, ok := in.FromValue.(*float32); ok {
|
||||
return in.FromValue, nil
|
||||
}
|
||||
v, err := c.Float32(in.FromValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v, nil
|
||||
|
||||
case "float64":
|
||||
return c.Float64(in.FromValue)
|
||||
case "*float64":
|
||||
if _, ok := in.FromValue.(*float64); ok {
|
||||
return in.FromValue, nil
|
||||
}
|
||||
v, err := c.Float64(in.FromValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v, nil
|
||||
|
||||
case "bool":
|
||||
return c.Bool(in.FromValue)
|
||||
case "*bool":
|
||||
if _, ok := in.FromValue.(*bool); ok {
|
||||
return in.FromValue, nil
|
||||
}
|
||||
v, err := c.Bool(in.FromValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v, nil
|
||||
|
||||
case "string":
|
||||
return c.String(in.FromValue)
|
||||
case "*string":
|
||||
if _, ok := in.FromValue.(*string); ok {
|
||||
return in.FromValue, nil
|
||||
}
|
||||
v, err := c.String(in.FromValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v, nil
|
||||
|
||||
case "[]byte":
|
||||
return c.Bytes(in.FromValue)
|
||||
case "[]int":
|
||||
return c.SliceInt(in.FromValue, option.SliceOption)
|
||||
case "[]int32":
|
||||
return c.SliceInt32(in.FromValue, option.SliceOption)
|
||||
case "[]int64":
|
||||
return c.SliceInt64(in.FromValue, option.SliceOption)
|
||||
case "[]uint":
|
||||
return c.SliceUint(in.FromValue, option.SliceOption)
|
||||
case "[]uint8":
|
||||
return c.Bytes(in.FromValue)
|
||||
case "[]uint32":
|
||||
return c.SliceUint32(in.FromValue, option.SliceOption)
|
||||
case "[]uint64":
|
||||
return c.SliceUint64(in.FromValue, option.SliceOption)
|
||||
case "[]float32":
|
||||
return c.SliceFloat32(in.FromValue, option.SliceOption)
|
||||
case "[]float64":
|
||||
return c.SliceFloat64(in.FromValue, option.SliceOption)
|
||||
case "[]string":
|
||||
return c.SliceStr(in.FromValue, option.SliceOption)
|
||||
|
||||
case "Time", "time.Time":
|
||||
if len(option.ExtraParams) > 0 {
|
||||
s, err := c.String(option.ExtraParams[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.Time(in.FromValue, s)
|
||||
}
|
||||
return c.Time(in.FromValue)
|
||||
case "*time.Time":
|
||||
var v time.Time
|
||||
if len(option.ExtraParams) > 0 {
|
||||
s, err := c.String(option.ExtraParams[0])
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
v, err = c.Time(in.FromValue, s)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
} else {
|
||||
if _, ok := in.FromValue.(*time.Time); ok {
|
||||
return in.FromValue, nil
|
||||
}
|
||||
v, err = c.Time(in.FromValue)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
}
|
||||
return &v, nil
|
||||
|
||||
case "GTime", "gtime.Time":
|
||||
if len(option.ExtraParams) > 0 {
|
||||
s, err := c.String(option.ExtraParams[0])
|
||||
if err != nil {
|
||||
return *gtime.New(), err
|
||||
}
|
||||
v, err := c.GTime(in.FromValue, s)
|
||||
if err != nil {
|
||||
return *gtime.New(), err
|
||||
}
|
||||
if v != nil {
|
||||
return *v, nil
|
||||
}
|
||||
return *gtime.New(), nil
|
||||
}
|
||||
v, err := c.GTime(in.FromValue)
|
||||
if err != nil {
|
||||
return *gtime.New(), err
|
||||
}
|
||||
if v != nil {
|
||||
return *v, nil
|
||||
}
|
||||
return *gtime.New(), nil
|
||||
case "*gtime.Time":
|
||||
if len(option.ExtraParams) > 0 {
|
||||
s, err := c.String(option.ExtraParams[0])
|
||||
if err != nil {
|
||||
return gtime.New(), err
|
||||
}
|
||||
v, err := c.GTime(in.FromValue, s)
|
||||
if err != nil {
|
||||
return gtime.New(), err
|
||||
}
|
||||
if v != nil {
|
||||
return v, nil
|
||||
}
|
||||
return gtime.New(), nil
|
||||
}
|
||||
v, err := c.GTime(in.FromValue)
|
||||
if err != nil {
|
||||
return gtime.New(), err
|
||||
}
|
||||
if v != nil {
|
||||
return v, nil
|
||||
}
|
||||
return gtime.New(), nil
|
||||
|
||||
case "Duration", "time.Duration":
|
||||
return c.Duration(in.FromValue)
|
||||
case "*time.Duration":
|
||||
if _, ok := in.FromValue.(*time.Duration); ok {
|
||||
return in.FromValue, nil
|
||||
}
|
||||
v, err := c.Duration(in.FromValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &v, nil
|
||||
|
||||
case "map[string]string":
|
||||
return c.MapStrStr(in.FromValue, option.MapOption)
|
||||
|
||||
case "map[string]interface {}":
|
||||
return c.Map(in.FromValue, option.MapOption)
|
||||
|
||||
case "[]map[string]interface {}":
|
||||
return c.SliceMap(in.FromValue, option.SliceOption, option.MapOption)
|
||||
|
||||
case "RawMessage", "json.RawMessage":
|
||||
// issue 3449
|
||||
bytes, err := json.Marshal(in.FromValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return bytes, nil
|
||||
|
||||
default:
|
||||
return c.doConvertForDefault(in, option)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Converter) doConvertForDefault(in doConvertInput, option ConvertOption) (convertedValue any, err error) {
|
||||
if in.ReferValue != nil {
|
||||
var referReflectValue reflect.Value
|
||||
if v, ok := in.ReferValue.(reflect.Value); ok {
|
||||
referReflectValue = v
|
||||
} else {
|
||||
referReflectValue = reflect.ValueOf(in.ReferValue)
|
||||
}
|
||||
var fromReflectValue reflect.Value
|
||||
if v, ok := in.FromValue.(reflect.Value); ok {
|
||||
fromReflectValue = v
|
||||
} else {
|
||||
fromReflectValue = reflect.ValueOf(in.FromValue)
|
||||
}
|
||||
|
||||
// custom converter.
|
||||
dstReflectValue, ok, err := c.callCustomConverterWithRefer(fromReflectValue, referReflectValue)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ok {
|
||||
return dstReflectValue.Interface(), nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if recover() != nil {
|
||||
in.alreadySetToReferValue = false
|
||||
if err = c.bindVarToReflectValue(referReflectValue, in.FromValue, option.StructOption); err == nil {
|
||||
in.alreadySetToReferValue = true
|
||||
convertedValue = referReflectValue.Interface()
|
||||
}
|
||||
}
|
||||
}()
|
||||
switch referReflectValue.Kind() {
|
||||
case reflect.Ptr:
|
||||
// Type converting for custom type pointers.
|
||||
// Eg:
|
||||
// type PayMode int
|
||||
// type Req struct{
|
||||
// Mode *PayMode
|
||||
// }
|
||||
//
|
||||
// Struct(`{"Mode": 1000}`, &req)
|
||||
originType := referReflectValue.Type().Elem()
|
||||
switch originType.Kind() {
|
||||
case reflect.Struct:
|
||||
// Not support some kinds.
|
||||
default:
|
||||
in.ToTypeName = originType.Kind().String()
|
||||
in.ReferValue = nil
|
||||
result, err := c.doConvert(in, option)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
refElementValue := reflect.ValueOf(result)
|
||||
originTypeValue := reflect.New(refElementValue.Type()).Elem()
|
||||
originTypeValue.Set(refElementValue)
|
||||
in.alreadySetToReferValue = true
|
||||
return originTypeValue.Addr().Convert(referReflectValue.Type()).Interface(), nil
|
||||
}
|
||||
|
||||
case reflect.Map:
|
||||
var targetValue = reflect.New(referReflectValue.Type()).Elem()
|
||||
if err = c.MapToMap(in.FromValue, targetValue, nil, option.MapOption); err == nil {
|
||||
in.alreadySetToReferValue = true
|
||||
}
|
||||
return targetValue.Interface(), nil
|
||||
|
||||
default:
|
||||
}
|
||||
in.ToTypeName = referReflectValue.Kind().String()
|
||||
in.ReferValue = nil
|
||||
in.alreadySetToReferValue = true
|
||||
result, err := c.doConvert(in, option)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
convertedValue = reflect.ValueOf(result).Convert(referReflectValue.Type()).Interface()
|
||||
return convertedValue, nil
|
||||
}
|
||||
return in.FromValue, nil
|
||||
}
|
||||
|
||||
func (c *Converter) doConvertWithReflectValueSet(reflectValue reflect.Value, in doConvertInput, option ConvertOption) error {
|
||||
convertedValue, err := c.doConvert(in, option)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !in.alreadySetToReferValue {
|
||||
reflectValue.Set(reflect.ValueOf(convertedValue))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Converter) getRegisteredConverterFuncAndSrcType(
|
||||
srcReflectValue, dstReflectValueForRefer reflect.Value,
|
||||
) (f converterFunc, srcType reflect.Type, ok bool) {
|
||||
if len(c.typeConverterFuncMap) == 0 {
|
||||
return reflect.Value{}, nil, false
|
||||
}
|
||||
srcType = srcReflectValue.Type()
|
||||
for srcType.Kind() == reflect.Pointer {
|
||||
srcType = srcType.Elem()
|
||||
}
|
||||
var registeredOutTypeMap map[converterOutType]converterFunc
|
||||
// firstly, it searches the map by input parameter type.
|
||||
registeredOutTypeMap, ok = c.typeConverterFuncMap[srcType]
|
||||
if !ok {
|
||||
return reflect.Value{}, nil, false
|
||||
}
|
||||
var dstType = dstReflectValueForRefer.Type()
|
||||
if dstType.Kind() == reflect.Pointer {
|
||||
// Might be **struct, which is support as designed.
|
||||
if dstType.Elem().Kind() == reflect.Pointer {
|
||||
dstType = dstType.Elem()
|
||||
}
|
||||
} else if dstReflectValueForRefer.IsValid() && dstReflectValueForRefer.CanAddr() {
|
||||
dstType = dstReflectValueForRefer.Addr().Type()
|
||||
} else {
|
||||
dstType = reflect.PointerTo(dstType)
|
||||
}
|
||||
// secondly, it searches the input parameter type map
|
||||
// and finds the result converter function by the output parameter type.
|
||||
f, ok = registeredOutTypeMap[dstType]
|
||||
if !ok {
|
||||
return reflect.Value{}, nil, false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Converter) callCustomConverterWithRefer(
|
||||
srcReflectValue, referReflectValue reflect.Value,
|
||||
) (dstReflectValue reflect.Value, converted bool, err error) {
|
||||
registeredConverterFunc, srcType, ok := c.getRegisteredConverterFuncAndSrcType(srcReflectValue, referReflectValue)
|
||||
if !ok {
|
||||
return reflect.Value{}, false, nil
|
||||
}
|
||||
dstReflectValue = reflect.New(referReflectValue.Type()).Elem()
|
||||
converted, err = c.doCallCustomConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType)
|
||||
return
|
||||
}
|
||||
|
||||
// callCustomConverter call the custom converter. It will try some possible type.
|
||||
func (c *Converter) callCustomConverter(srcReflectValue, dstReflectValue reflect.Value) (converted bool, err error) {
|
||||
registeredConverterFunc, srcType, ok := c.getRegisteredConverterFuncAndSrcType(srcReflectValue, dstReflectValue)
|
||||
if !ok {
|
||||
return false, nil
|
||||
}
|
||||
return c.doCallCustomConverter(srcReflectValue, dstReflectValue, registeredConverterFunc, srcType)
|
||||
}
|
||||
|
||||
func (c *Converter) doCallCustomConverter(
|
||||
srcReflectValue reflect.Value,
|
||||
dstReflectValue reflect.Value,
|
||||
registeredConverterFunc converterFunc,
|
||||
srcType reflect.Type,
|
||||
) (converted bool, err error) {
|
||||
// Converter function calling.
|
||||
for srcReflectValue.Type() != srcType {
|
||||
srcReflectValue = srcReflectValue.Elem()
|
||||
}
|
||||
result := registeredConverterFunc.Call([]reflect.Value{srcReflectValue})
|
||||
if !result[1].IsNil() {
|
||||
return false, result[1].Interface().(error)
|
||||
}
|
||||
// The `result[0]` is a pointer.
|
||||
if result[0].IsNil() {
|
||||
return false, nil
|
||||
}
|
||||
var resultValue = result[0]
|
||||
for {
|
||||
if resultValue.Type() == dstReflectValue.Type() && dstReflectValue.CanSet() {
|
||||
dstReflectValue.Set(resultValue)
|
||||
converted = true
|
||||
} else if dstReflectValue.Kind() == reflect.Pointer {
|
||||
if resultValue.Type() == dstReflectValue.Elem().Type() && dstReflectValue.Elem().CanSet() {
|
||||
dstReflectValue.Elem().Set(resultValue)
|
||||
converted = true
|
||||
}
|
||||
}
|
||||
if converted {
|
||||
break
|
||||
}
|
||||
if resultValue.Kind() == reflect.Pointer {
|
||||
resultValue = resultValue.Elem()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return converted, nil
|
||||
}
|
||||
147
util/gconv/internal/converter/converter_float.go
Normal file
147
util/gconv/internal/converter/converter_float.go
Normal file
@ -0,0 +1,147 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package converter
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gbinary"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
|
||||
)
|
||||
|
||||
// Float32 converts `any` to float32.
|
||||
func (c *Converter) Float32(any any) (float32, error) {
|
||||
if empty.IsNil(any) {
|
||||
return 0, nil
|
||||
}
|
||||
switch value := any.(type) {
|
||||
case float32:
|
||||
return value, nil
|
||||
case float64:
|
||||
return float32(value), nil
|
||||
case []byte:
|
||||
// TODO: It might panic here for these types.
|
||||
return gbinary.DecodeToFloat32(value), nil
|
||||
default:
|
||||
rv := reflect.ValueOf(any)
|
||||
switch rv.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return float32(rv.Int()), nil
|
||||
case reflect.Uintptr, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return float32(rv.Uint()), nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return float32(rv.Float()), nil
|
||||
case reflect.Bool:
|
||||
if rv.Bool() {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
case reflect.String:
|
||||
f, err := strconv.ParseFloat(rv.String(), 32)
|
||||
if err != nil {
|
||||
return 0, gerror.WrapCodef(
|
||||
gcode.CodeInvalidParameter, err, "converting string to float32 failed for: %v", any,
|
||||
)
|
||||
}
|
||||
return float32(f), nil
|
||||
case reflect.Ptr:
|
||||
if rv.IsNil() {
|
||||
return 0, nil
|
||||
}
|
||||
if f, ok := value.(localinterface.IFloat32); ok {
|
||||
return f.Float32(), nil
|
||||
}
|
||||
return c.Float32(rv.Elem().Interface())
|
||||
default:
|
||||
if f, ok := value.(localinterface.IFloat32); ok {
|
||||
return f.Float32(), nil
|
||||
}
|
||||
s, err := c.String(any)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
v, err := strconv.ParseFloat(s, 32)
|
||||
if err != nil {
|
||||
return 0, gerror.WrapCodef(
|
||||
gcode.CodeInvalidParameter, err, "converting string to float32 failed for: %v", any,
|
||||
)
|
||||
}
|
||||
return float32(v), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Float64 converts `any` to float64.
|
||||
func (c *Converter) Float64(any any) (float64, error) {
|
||||
if empty.IsNil(any) {
|
||||
return 0, nil
|
||||
}
|
||||
switch value := any.(type) {
|
||||
case float32:
|
||||
return float64(value), nil
|
||||
case float64:
|
||||
return value, nil
|
||||
case []byte:
|
||||
// TODO: It might panic here for these types.
|
||||
return gbinary.DecodeToFloat64(value), nil
|
||||
default:
|
||||
rv := reflect.ValueOf(any)
|
||||
switch rv.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return float64(rv.Int()), nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return float64(rv.Uint()), nil
|
||||
case reflect.Uintptr:
|
||||
return float64(rv.Uint()), nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
// Please Note:
|
||||
// When the type is float32 or a custom type defined based on float32,
|
||||
// switching to float64 may result in a few extra decimal places.
|
||||
return rv.Float(), nil
|
||||
case reflect.Bool:
|
||||
if rv.Bool() {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
case reflect.String:
|
||||
f, err := strconv.ParseFloat(rv.String(), 64)
|
||||
if err != nil {
|
||||
return 0, gerror.WrapCodef(
|
||||
gcode.CodeInvalidParameter, err, "converting string to float64 failed for: %v", any,
|
||||
)
|
||||
}
|
||||
return f, nil
|
||||
case reflect.Ptr:
|
||||
if rv.IsNil() {
|
||||
return 0, nil
|
||||
}
|
||||
if f, ok := value.(localinterface.IFloat64); ok {
|
||||
return f.Float64(), nil
|
||||
}
|
||||
return c.Float64(rv.Elem().Interface())
|
||||
default:
|
||||
if f, ok := value.(localinterface.IFloat64); ok {
|
||||
return f.Float64(), nil
|
||||
}
|
||||
s, err := c.String(any)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
v, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
return 0, gerror.WrapCodef(
|
||||
gcode.CodeInvalidParameter, err, "converting string to float64 failed for: %v", any,
|
||||
)
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
157
util/gconv/internal/converter/converter_int.go
Normal file
157
util/gconv/internal/converter/converter_int.go
Normal file
@ -0,0 +1,157 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package converter
|
||||
|
||||
import (
|
||||
"math"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"github.com/gogf/gf/v2/encoding/gbinary"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
|
||||
)
|
||||
|
||||
// Int converts `any` to int.
|
||||
func (c *Converter) Int(any any) (int, error) {
|
||||
if v, ok := any.(int); ok {
|
||||
return v, nil
|
||||
}
|
||||
v, err := c.Int64(any)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(v), nil
|
||||
}
|
||||
|
||||
// Int8 converts `any` to int8.
|
||||
func (c *Converter) Int8(any any) (int8, error) {
|
||||
if v, ok := any.(int8); ok {
|
||||
return v, nil
|
||||
}
|
||||
v, err := c.Int64(any)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int8(v), nil
|
||||
}
|
||||
|
||||
// Int16 converts `any` to int16.
|
||||
func (c *Converter) Int16(any any) (int16, error) {
|
||||
if v, ok := any.(int16); ok {
|
||||
return v, nil
|
||||
}
|
||||
v, err := c.Int64(any)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int16(v), nil
|
||||
}
|
||||
|
||||
// Int32 converts `any` to int32.
|
||||
func (c *Converter) Int32(any any) (int32, error) {
|
||||
if v, ok := any.(int32); ok {
|
||||
return v, nil
|
||||
}
|
||||
v, err := c.Int64(any)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int32(v), nil
|
||||
}
|
||||
|
||||
// Int64 converts `any` to int64.
|
||||
func (c *Converter) Int64(any any) (int64, error) {
|
||||
if empty.IsNil(any) {
|
||||
return 0, nil
|
||||
}
|
||||
if v, ok := any.(int64); ok {
|
||||
return v, nil
|
||||
}
|
||||
rv := reflect.ValueOf(any)
|
||||
switch rv.Kind() {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return rv.Int(), nil
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return int64(rv.Uint()), nil
|
||||
case reflect.Uintptr:
|
||||
return int64(rv.Uint()), nil
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return int64(rv.Float()), nil
|
||||
case reflect.Bool:
|
||||
if rv.Bool() {
|
||||
return 1, nil
|
||||
}
|
||||
return 0, nil
|
||||
case reflect.Ptr:
|
||||
if rv.IsNil() {
|
||||
return 0, nil
|
||||
}
|
||||
if f, ok := any.(localinterface.IInt64); ok {
|
||||
return f.Int64(), nil
|
||||
}
|
||||
return c.Int64(rv.Elem().Interface())
|
||||
case reflect.Slice:
|
||||
// TODO: It might panic here for these types.
|
||||
if rv.Type().Elem().Kind() == reflect.Uint8 {
|
||||
return gbinary.DecodeToInt64(rv.Bytes()), nil
|
||||
}
|
||||
case reflect.String:
|
||||
var (
|
||||
s = rv.String()
|
||||
isMinus = false
|
||||
)
|
||||
if len(s) > 0 {
|
||||
if s[0] == '-' {
|
||||
isMinus = true
|
||||
s = s[1:]
|
||||
} else if s[0] == '+' {
|
||||
s = s[1:]
|
||||
}
|
||||
}
|
||||
// Hexadecimal.
|
||||
if len(s) > 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X') {
|
||||
if v, e := strconv.ParseInt(s[2:], 16, 64); e == nil {
|
||||
if isMinus {
|
||||
return -v, nil
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
}
|
||||
// Decimal.
|
||||
if v, e := strconv.ParseInt(s, 10, 64); e == nil {
|
||||
if isMinus {
|
||||
return -v, nil
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
// Float64.
|
||||
valueInt64, err := c.Float64(s)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if math.IsNaN(valueInt64) {
|
||||
return 0, nil
|
||||
} else {
|
||||
if isMinus {
|
||||
return -int64(valueInt64), nil
|
||||
}
|
||||
return int64(valueInt64), nil
|
||||
}
|
||||
default:
|
||||
if f, ok := any.(localinterface.IInt64); ok {
|
||||
return f.Int64(), nil
|
||||
}
|
||||
}
|
||||
return 0, gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`unsupport value type for converting to int64: %v`,
|
||||
reflect.TypeOf(any),
|
||||
)
|
||||
}
|
||||
646
util/gconv/internal/converter/converter_map.go
Normal file
646
util/gconv/internal/converter/converter_map.go
Normal file
@ -0,0 +1,646 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package converter
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/utils"
|
||||
"github.com/gogf/gf/v2/util/gconv/internal/localinterface"
|
||||
"github.com/gogf/gf/v2/util/gtag"
|
||||
)
|
||||
|
||||
// MapOption specifies the option for map converting.
|
||||
type MapOption struct {
|
||||
// Deep marks doing Map function recursively, which means if the attribute of given converting value
|
||||
// is also a struct/*struct, it automatically calls Map function on this attribute converting it to
|
||||
// a map[string]any type variable.
|
||||
Deep bool
|
||||
|
||||
// OmitEmpty ignores the attributes that has json `omitempty` tag.
|
||||
OmitEmpty bool
|
||||
|
||||
// Tags specifies the converted map key name by struct tag name.
|
||||
Tags []string
|
||||
|
||||
// ContinueOnError specifies whether to continue converting the next element
|
||||
// if one element converting fails.
|
||||
ContinueOnError bool
|
||||
}
|
||||
|
||||
// Map converts any variable `value` to map[string]any. If the parameter `value` is not a
|
||||
// map/struct/*struct type, then the conversion will fail and returns nil.
|
||||
//
|
||||
// If `value` is a struct/*struct object, the second parameter `priorityTagAndFieldName` specifies the most priority
|
||||
// priorityTagAndFieldName that will be detected, otherwise it detects the priorityTagAndFieldName in order of:
|
||||
// gconv, json, field name.
|
||||
func (c *Converter) Map(value any, option MapOption) (map[string]any, error) {
|
||||
return c.doMapConvert(value, RecursiveTypeAuto, false, option)
|
||||
}
|
||||
|
||||
// MapStrStr converts `value` to map[string]string.
|
||||
// Note that there might be data copy for this map type converting.
|
||||
func (c *Converter) MapStrStr(value any, option MapOption) (map[string]string, error) {
|
||||
if r, ok := value.(map[string]string); ok {
|
||||
return r, nil
|
||||
}
|
||||
m, err := c.Map(value, option)
|
||||
if err != nil && !option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
if len(m) > 0 {
|
||||
var (
|
||||
s string
|
||||
vMap = make(map[string]string, len(m))
|
||||
)
|
||||
for k, v := range m {
|
||||
s, err = c.String(v)
|
||||
if err != nil && !option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
vMap[k] = s
|
||||
}
|
||||
return vMap, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// MapConvert implements the map converting.
|
||||
// It automatically checks and converts json string to map if `value` is string/[]byte.
|
||||
//
|
||||
// TODO completely implement the recursive converting for all types, especially the map.
|
||||
func (c *Converter) doMapConvert(
|
||||
value any, recursive RecursiveType, mustMapReturn bool, option MapOption,
|
||||
) (map[string]any, error) {
|
||||
if value == nil {
|
||||
return nil, nil
|
||||
}
|
||||
// It redirects to its underlying value if it has implemented interface iVal.
|
||||
if v, ok := value.(localinterface.IVal); ok {
|
||||
value = v.Val()
|
||||
}
|
||||
var (
|
||||
err error
|
||||
newTags = gtag.StructTagPriority
|
||||
)
|
||||
if option.Deep {
|
||||
recursive = RecursiveTypeTrue
|
||||
}
|
||||
switch len(option.Tags) {
|
||||
case 0:
|
||||
// No need handling.
|
||||
case 1:
|
||||
newTags = append(strings.Split(option.Tags[0], ","), gtag.StructTagPriority...)
|
||||
default:
|
||||
newTags = append(option.Tags, gtag.StructTagPriority...)
|
||||
}
|
||||
// Assert the common combination of types, and finally it uses reflection.
|
||||
dataMap := make(map[string]interface{})
|
||||
switch r := value.(type) {
|
||||
case string:
|
||||
// If it is a JSON string, automatically unmarshal it!
|
||||
if len(r) > 0 && r[0] == '{' && r[len(r)-1] == '}' {
|
||||
if err = json.UnmarshalUseNumber([]byte(r), &dataMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
case []byte:
|
||||
// If it is a JSON string, automatically unmarshal it!
|
||||
if len(r) > 0 && r[0] == '{' && r[len(r)-1] == '}' {
|
||||
if err = json.UnmarshalUseNumber(r, &dataMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, nil
|
||||
}
|
||||
case map[interface{}]interface{}:
|
||||
recursiveOption := option
|
||||
recursiveOption.Tags = newTags
|
||||
for k, v := range r {
|
||||
s, err := c.String(k)
|
||||
if err != nil && !option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
dataMap[s], err = c.doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: v,
|
||||
RecursiveType: recursive,
|
||||
RecursiveOption: recursive == RecursiveTypeTrue,
|
||||
Option: recursiveOption,
|
||||
},
|
||||
)
|
||||
if err != nil && !option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
case map[interface{}]string:
|
||||
for k, v := range r {
|
||||
s, err := c.String(k)
|
||||
if err != nil && !option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
dataMap[s] = v
|
||||
}
|
||||
case map[interface{}]int:
|
||||
for k, v := range r {
|
||||
s, err := c.String(k)
|
||||
if err != nil && !option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
dataMap[s] = v
|
||||
}
|
||||
case map[interface{}]uint:
|
||||
for k, v := range r {
|
||||
s, err := c.String(k)
|
||||
if err != nil && !option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
dataMap[s] = v
|
||||
}
|
||||
case map[interface{}]float32:
|
||||
for k, v := range r {
|
||||
s, err := c.String(k)
|
||||
if err != nil && !option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
dataMap[s] = v
|
||||
}
|
||||
case map[interface{}]float64:
|
||||
for k, v := range r {
|
||||
s, err := c.String(k)
|
||||
if err != nil && !option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
dataMap[s] = v
|
||||
}
|
||||
case map[string]bool:
|
||||
for k, v := range r {
|
||||
dataMap[k] = v
|
||||
}
|
||||
case map[string]int:
|
||||
for k, v := range r {
|
||||
dataMap[k] = v
|
||||
}
|
||||
case map[string]uint:
|
||||
for k, v := range r {
|
||||
dataMap[k] = v
|
||||
}
|
||||
case map[string]float32:
|
||||
for k, v := range r {
|
||||
dataMap[k] = v
|
||||
}
|
||||
case map[string]float64:
|
||||
for k, v := range r {
|
||||
dataMap[k] = v
|
||||
}
|
||||
case map[string]string:
|
||||
for k, v := range r {
|
||||
dataMap[k] = v
|
||||
}
|
||||
case map[string]interface{}:
|
||||
if recursive == RecursiveTypeTrue {
|
||||
recursiveOption := option
|
||||
recursiveOption.Tags = newTags
|
||||
// A copy of current map.
|
||||
for k, v := range r {
|
||||
dataMap[k], err = c.doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: v,
|
||||
RecursiveType: recursive,
|
||||
RecursiveOption: recursive == RecursiveTypeTrue,
|
||||
Option: recursiveOption,
|
||||
},
|
||||
)
|
||||
if err != nil && !option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// It returns the map directly without any changing.
|
||||
return r, nil
|
||||
}
|
||||
case map[int]interface{}:
|
||||
recursiveOption := option
|
||||
recursiveOption.Tags = newTags
|
||||
for k, v := range r {
|
||||
s, err := c.String(k)
|
||||
if err != nil && !option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
dataMap[s], err = c.doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: v,
|
||||
RecursiveType: recursive,
|
||||
RecursiveOption: recursive == RecursiveTypeTrue,
|
||||
Option: recursiveOption,
|
||||
},
|
||||
)
|
||||
if err != nil && !option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
case map[int]string:
|
||||
for k, v := range r {
|
||||
s, err := c.String(k)
|
||||
if err != nil && !option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
dataMap[s] = v
|
||||
}
|
||||
case map[uint]string:
|
||||
for k, v := range r {
|
||||
s, err := c.String(k)
|
||||
if err != nil && !option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
dataMap[s] = v
|
||||
}
|
||||
|
||||
default:
|
||||
// Not a common type, it then uses reflection for conversion.
|
||||
var reflectValue reflect.Value
|
||||
if v, ok := value.(reflect.Value); ok {
|
||||
reflectValue = v
|
||||
} else {
|
||||
reflectValue = reflect.ValueOf(value)
|
||||
}
|
||||
reflectKind := reflectValue.Kind()
|
||||
// If it is a pointer, we should find its real data type.
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch reflectKind {
|
||||
// If `value` is type of array, it converts the value of even number index as its key and
|
||||
// the value of odd number index as its corresponding value, for example:
|
||||
// []string{"k1","v1","k2","v2"} => map[string]interface{}{"k1":"v1", "k2":"v2"}
|
||||
// []string{"k1","v1","k2"} => map[string]interface{}{"k1":"v1", "k2":nil}
|
||||
case reflect.Slice, reflect.Array:
|
||||
length := reflectValue.Len()
|
||||
for i := 0; i < length; i += 2 {
|
||||
s, err := c.String(reflectValue.Index(i).Interface())
|
||||
if err != nil && !option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
if i+1 < length {
|
||||
dataMap[s] = reflectValue.Index(i + 1).Interface()
|
||||
} else {
|
||||
dataMap[s] = nil
|
||||
}
|
||||
}
|
||||
case reflect.Map, reflect.Struct, reflect.Interface:
|
||||
recursiveOption := option
|
||||
recursiveOption.Tags = newTags
|
||||
convertedValue, err := c.doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: true,
|
||||
Value: value,
|
||||
RecursiveType: recursive,
|
||||
RecursiveOption: recursive == RecursiveTypeTrue,
|
||||
Option: recursiveOption,
|
||||
MustMapReturn: mustMapReturn,
|
||||
},
|
||||
)
|
||||
if err != nil && !option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
if m, ok := convertedValue.(map[string]interface{}); ok {
|
||||
return m, nil
|
||||
}
|
||||
return nil, nil
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
return dataMap, nil
|
||||
}
|
||||
|
||||
type doMapConvertForMapOrStructValueInput struct {
|
||||
IsRoot bool // It returns directly if it is not root and with no recursive converting.
|
||||
Value interface{} // Current operation value.
|
||||
RecursiveType RecursiveType // The type from top function entry.
|
||||
RecursiveOption bool // Whether convert recursively for `current` operation.
|
||||
Option MapOption // Map converting option.
|
||||
MustMapReturn bool // Must return map instead of Value when empty.
|
||||
}
|
||||
|
||||
func (c *Converter) doMapConvertForMapOrStructValue(in doMapConvertForMapOrStructValueInput) (any, error) {
|
||||
if !in.IsRoot && !in.RecursiveOption {
|
||||
return in.Value, nil
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
reflectValue reflect.Value
|
||||
)
|
||||
if v, ok := in.Value.(reflect.Value); ok {
|
||||
reflectValue = v
|
||||
in.Value = v.Interface()
|
||||
} else {
|
||||
reflectValue = reflect.ValueOf(in.Value)
|
||||
}
|
||||
reflectKind := reflectValue.Kind()
|
||||
// If it is a pointer, we should find its real data type.
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Map:
|
||||
var (
|
||||
mapIter = reflectValue.MapRange()
|
||||
dataMap = make(map[string]interface{})
|
||||
)
|
||||
for mapIter.Next() {
|
||||
var (
|
||||
mapKeyValue = mapIter.Value()
|
||||
mapValue interface{}
|
||||
)
|
||||
switch {
|
||||
case mapKeyValue.IsZero():
|
||||
if utils.CanCallIsNil(mapKeyValue) && mapKeyValue.IsNil() {
|
||||
// quick check for nil value.
|
||||
mapValue = nil
|
||||
} else {
|
||||
// in case of:
|
||||
// exception recovered: reflect: call of reflect.Value.Interface on zero Value
|
||||
mapValue = reflect.New(mapKeyValue.Type()).Elem().Interface()
|
||||
}
|
||||
default:
|
||||
mapValue = mapKeyValue.Interface()
|
||||
}
|
||||
s, err := c.String(mapIter.Key().Interface())
|
||||
if err != nil && !in.Option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
dataMap[s], err = c.doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: mapValue,
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: in.RecursiveType == RecursiveTypeTrue,
|
||||
Option: in.Option,
|
||||
},
|
||||
)
|
||||
if err != nil && !in.Option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return dataMap, nil
|
||||
|
||||
case reflect.Struct:
|
||||
var dataMap = make(map[string]interface{})
|
||||
// Map converting interface check.
|
||||
if v, ok := in.Value.(localinterface.IMapStrAny); ok {
|
||||
// Value copy, in case of concurrent safety.
|
||||
for mapK, mapV := range v.MapStrAny() {
|
||||
if in.RecursiveOption {
|
||||
dataMap[mapK], err = c.doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: mapV,
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: in.RecursiveType == RecursiveTypeTrue,
|
||||
Option: in.Option,
|
||||
},
|
||||
)
|
||||
if err != nil && !in.Option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
dataMap[mapK] = mapV
|
||||
}
|
||||
}
|
||||
if len(dataMap) > 0 {
|
||||
return dataMap, nil
|
||||
}
|
||||
}
|
||||
// Using reflect for converting.
|
||||
var (
|
||||
rtField reflect.StructField
|
||||
rvField reflect.Value
|
||||
reflectType = reflectValue.Type() // attribute value type.
|
||||
mapKey = "" // mapKey may be the tag name or the struct attribute name.
|
||||
)
|
||||
for i := 0; i < reflectValue.NumField(); i++ {
|
||||
rtField = reflectType.Field(i)
|
||||
rvField = reflectValue.Field(i)
|
||||
// Only convert the public attributes.
|
||||
fieldName := rtField.Name
|
||||
if !utils.IsLetterUpper(fieldName[0]) {
|
||||
continue
|
||||
}
|
||||
mapKey = ""
|
||||
fieldTag := rtField.Tag
|
||||
for _, tag := range in.Option.Tags {
|
||||
if mapKey = fieldTag.Get(tag); mapKey != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
if mapKey == "" {
|
||||
mapKey = fieldName
|
||||
} else {
|
||||
// Support json tag feature: -, omitempty
|
||||
mapKey = strings.TrimSpace(mapKey)
|
||||
if mapKey == "-" {
|
||||
continue
|
||||
}
|
||||
array := strings.Split(mapKey, ",")
|
||||
if len(array) > 1 {
|
||||
switch strings.TrimSpace(array[1]) {
|
||||
case "omitempty":
|
||||
if in.Option.OmitEmpty && empty.IsEmpty(rvField.Interface()) {
|
||||
continue
|
||||
} else {
|
||||
mapKey = strings.TrimSpace(array[0])
|
||||
}
|
||||
default:
|
||||
mapKey = strings.TrimSpace(array[0])
|
||||
}
|
||||
}
|
||||
if mapKey == "" {
|
||||
mapKey = fieldName
|
||||
}
|
||||
}
|
||||
if in.RecursiveOption || rtField.Anonymous {
|
||||
// Do map converting recursively.
|
||||
var (
|
||||
rvAttrField = rvField
|
||||
rvAttrKind = rvField.Kind()
|
||||
)
|
||||
if rvAttrKind == reflect.Ptr {
|
||||
rvAttrField = rvField.Elem()
|
||||
rvAttrKind = rvAttrField.Kind()
|
||||
}
|
||||
switch rvAttrKind {
|
||||
case reflect.Struct:
|
||||
// Embedded struct and has no fields, just ignores it.
|
||||
// Eg: gmeta.Meta
|
||||
if rvAttrField.Type().NumField() == 0 {
|
||||
continue
|
||||
}
|
||||
var (
|
||||
hasNoTag = mapKey == fieldName
|
||||
// DO NOT use rvAttrField.Interface() here,
|
||||
// as it might be changed from pointer to struct.
|
||||
rvInterface = rvField.Interface()
|
||||
)
|
||||
switch {
|
||||
case hasNoTag && rtField.Anonymous:
|
||||
// It means this attribute field has no tag.
|
||||
// Overwrite the attribute with sub-struct attribute fields.
|
||||
anonymousValue, err := c.doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: rvInterface,
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: true,
|
||||
Option: in.Option,
|
||||
},
|
||||
)
|
||||
if err != nil && !in.Option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
if m, ok := anonymousValue.(map[string]interface{}); ok {
|
||||
for k, v := range m {
|
||||
dataMap[k] = v
|
||||
}
|
||||
} else {
|
||||
dataMap[mapKey] = rvInterface
|
||||
}
|
||||
|
||||
// It means this attribute field has desired tag.
|
||||
case !hasNoTag && rtField.Anonymous:
|
||||
dataMap[mapKey], err = c.doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: rvInterface,
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: true,
|
||||
Option: in.Option,
|
||||
},
|
||||
)
|
||||
if err != nil && !in.Option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
default:
|
||||
dataMap[mapKey], err = c.doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: rvInterface,
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: in.RecursiveType == RecursiveTypeTrue,
|
||||
Option: in.Option,
|
||||
},
|
||||
)
|
||||
if err != nil && !in.Option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// The struct attribute is type of slice.
|
||||
case reflect.Array, reflect.Slice:
|
||||
length := rvAttrField.Len()
|
||||
if length == 0 {
|
||||
dataMap[mapKey] = rvAttrField.Interface()
|
||||
break
|
||||
}
|
||||
array := make([]interface{}, length)
|
||||
for arrayIndex := 0; arrayIndex < length; arrayIndex++ {
|
||||
array[arrayIndex], err = c.doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: rvAttrField.Index(arrayIndex).Interface(),
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: in.RecursiveType == RecursiveTypeTrue,
|
||||
Option: in.Option,
|
||||
},
|
||||
)
|
||||
if err != nil && !in.Option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
dataMap[mapKey] = array
|
||||
case reflect.Map:
|
||||
var (
|
||||
mapIter = rvAttrField.MapRange()
|
||||
nestedMap = make(map[string]interface{})
|
||||
)
|
||||
for mapIter.Next() {
|
||||
s, err := c.String(mapIter.Key().Interface())
|
||||
if err != nil && !in.Option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
nestedMap[s], err = c.doMapConvertForMapOrStructValue(
|
||||
doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: mapIter.Value().Interface(),
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: in.RecursiveType == RecursiveTypeTrue,
|
||||
Option: in.Option,
|
||||
},
|
||||
)
|
||||
if err != nil && !in.Option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
dataMap[mapKey] = nestedMap
|
||||
default:
|
||||
if rvField.IsValid() {
|
||||
dataMap[mapKey] = reflectValue.Field(i).Interface()
|
||||
} else {
|
||||
dataMap[mapKey] = nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No recursive map value converting
|
||||
if rvField.IsValid() {
|
||||
dataMap[mapKey] = reflectValue.Field(i).Interface()
|
||||
} else {
|
||||
dataMap[mapKey] = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if !in.MustMapReturn && len(dataMap) == 0 {
|
||||
return in.Value, nil
|
||||
}
|
||||
return dataMap, nil
|
||||
|
||||
// The given value is type of slice.
|
||||
case reflect.Array, reflect.Slice:
|
||||
length := reflectValue.Len()
|
||||
if length == 0 {
|
||||
break
|
||||
}
|
||||
array := make([]interface{}, reflectValue.Len())
|
||||
for i := 0; i < length; i++ {
|
||||
array[i], err = c.doMapConvertForMapOrStructValue(doMapConvertForMapOrStructValueInput{
|
||||
IsRoot: false,
|
||||
Value: reflectValue.Index(i).Interface(),
|
||||
RecursiveType: in.RecursiveType,
|
||||
RecursiveOption: in.RecursiveType == RecursiveTypeTrue,
|
||||
Option: in.Option,
|
||||
})
|
||||
if err != nil && !in.Option.ContinueOnError {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return array, nil
|
||||
|
||||
default:
|
||||
}
|
||||
return in.Value, nil
|
||||
}
|
||||
140
util/gconv/internal/converter/converter_maptomap.go
Normal file
140
util/gconv/internal/converter/converter_maptomap.go
Normal file
@ -0,0 +1,140 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package converter
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
)
|
||||
|
||||
// MapToMap converts any map type variable `params` to another map type variable `pointer`.
|
||||
//
|
||||
// The parameter `params` can be any type of map, like:
|
||||
// map[string]string, map[string]struct, map[string]*struct, reflect.Value, etc.
|
||||
//
|
||||
// The parameter `pointer` should be type of *map, like:
|
||||
// map[int]string, map[string]struct, map[string]*struct, reflect.Value, etc.
|
||||
//
|
||||
// The optional parameter `mapping` is used for struct attribute to map key mapping, which makes
|
||||
// sense only if the items of original map `params` is type struct.
|
||||
func (c *Converter) MapToMap(
|
||||
params, pointer any, mapping map[string]string, option MapOption,
|
||||
) (err error) {
|
||||
var (
|
||||
paramsRv reflect.Value
|
||||
paramsKind reflect.Kind
|
||||
)
|
||||
if v, ok := params.(reflect.Value); ok {
|
||||
paramsRv = v
|
||||
} else {
|
||||
paramsRv = reflect.ValueOf(params)
|
||||
}
|
||||
paramsKind = paramsRv.Kind()
|
||||
if paramsKind == reflect.Ptr {
|
||||
paramsRv = paramsRv.Elem()
|
||||
paramsKind = paramsRv.Kind()
|
||||
}
|
||||
if paramsKind != reflect.Map {
|
||||
m, err := c.Map(params, option)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.MapToMap(m, pointer, mapping, option)
|
||||
}
|
||||
// Empty params map, no need continue.
|
||||
if paramsRv.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
var pointerRv reflect.Value
|
||||
if v, ok := pointer.(reflect.Value); ok {
|
||||
pointerRv = v
|
||||
} else {
|
||||
pointerRv = reflect.ValueOf(pointer)
|
||||
}
|
||||
pointerKind := pointerRv.Kind()
|
||||
for pointerKind == reflect.Ptr {
|
||||
pointerRv = pointerRv.Elem()
|
||||
pointerKind = pointerRv.Kind()
|
||||
}
|
||||
if pointerKind != reflect.Map {
|
||||
return gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`destination pointer should be type of *map, but got: %s`,
|
||||
pointerKind,
|
||||
)
|
||||
}
|
||||
defer func() {
|
||||
// Catch the panic, especially the reflection operation panics.
|
||||
if exception := recover(); exception != nil {
|
||||
if v, ok := exception.(error); ok && gerror.HasStack(v) {
|
||||
err = v
|
||||
} else {
|
||||
err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception)
|
||||
}
|
||||
}
|
||||
}()
|
||||
var (
|
||||
paramsKeys = paramsRv.MapKeys()
|
||||
pointerKeyType = pointerRv.Type().Key()
|
||||
pointerValueType = pointerRv.Type().Elem()
|
||||
pointerValueKind = pointerValueType.Kind()
|
||||
dataMap = reflect.MakeMapWithSize(pointerRv.Type(), len(paramsKeys))
|
||||
convertOption = ConvertOption{
|
||||
StructOption: StructOption{ContinueOnError: option.ContinueOnError},
|
||||
SliceOption: SliceOption{ContinueOnError: option.ContinueOnError},
|
||||
MapOption: option,
|
||||
}
|
||||
)
|
||||
// Retrieve the true element type of target map.
|
||||
if pointerValueKind == reflect.Ptr {
|
||||
pointerValueKind = pointerValueType.Elem().Kind()
|
||||
}
|
||||
for _, key := range paramsKeys {
|
||||
mapValue := reflect.New(pointerValueType).Elem()
|
||||
switch pointerValueKind {
|
||||
case reflect.Map, reflect.Struct:
|
||||
structOption := StructOption{
|
||||
ParamKeyToAttrMap: mapping,
|
||||
PriorityTag: "",
|
||||
ContinueOnError: option.ContinueOnError,
|
||||
}
|
||||
if err = c.Struct(paramsRv.MapIndex(key).Interface(), mapValue, structOption); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
convertResult, err := c.doConvert(
|
||||
doConvertInput{
|
||||
FromValue: paramsRv.MapIndex(key).Interface(),
|
||||
ToTypeName: pointerValueType.String(),
|
||||
ReferValue: mapValue,
|
||||
},
|
||||
convertOption,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mapValue.Set(reflect.ValueOf(convertResult))
|
||||
}
|
||||
convertResult, err := c.doConvert(
|
||||
doConvertInput{
|
||||
FromValue: key.Interface(),
|
||||
ToTypeName: pointerKeyType.Name(),
|
||||
ReferValue: reflect.New(pointerKeyType).Elem().Interface(),
|
||||
},
|
||||
convertOption,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var mapKey = reflect.ValueOf(convertResult)
|
||||
dataMap.SetMapIndex(mapKey, mapValue)
|
||||
}
|
||||
pointerRv.Set(dataMap)
|
||||
return nil
|
||||
}
|
||||
122
util/gconv/internal/converter/converter_maptomaps.go
Normal file
122
util/gconv/internal/converter/converter_maptomaps.go
Normal file
@ -0,0 +1,122 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package converter
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
)
|
||||
|
||||
// MapToMaps converts any map type variable `params` to another map slice variable `pointer`.
|
||||
//
|
||||
// The parameter `params` can be type of []map, []*map, []struct, []*struct.
|
||||
//
|
||||
// The parameter `pointer` should be type of []map, []*map.
|
||||
//
|
||||
// The optional parameter `mapping` is used for struct attribute to map key mapping, which makes
|
||||
// sense only if the item of `params` is type struct.
|
||||
func (c *Converter) MapToMaps(
|
||||
params any, pointer any, paramKeyToAttrMap map[string]string, option MapOption,
|
||||
) (err error) {
|
||||
// Params and its element type check.
|
||||
var (
|
||||
paramsRv reflect.Value
|
||||
paramsKind reflect.Kind
|
||||
)
|
||||
if v, ok := params.(reflect.Value); ok {
|
||||
paramsRv = v
|
||||
} else {
|
||||
paramsRv = reflect.ValueOf(params)
|
||||
}
|
||||
paramsKind = paramsRv.Kind()
|
||||
if paramsKind == reflect.Ptr {
|
||||
paramsRv = paramsRv.Elem()
|
||||
paramsKind = paramsRv.Kind()
|
||||
}
|
||||
if paramsKind != reflect.Array && paramsKind != reflect.Slice {
|
||||
return gerror.NewCode(
|
||||
gcode.CodeInvalidParameter,
|
||||
"params should be type of slice, example: []map/[]*map/[]struct/[]*struct",
|
||||
)
|
||||
}
|
||||
var (
|
||||
paramsElem = paramsRv.Type().Elem()
|
||||
paramsElemKind = paramsElem.Kind()
|
||||
)
|
||||
if paramsElemKind == reflect.Ptr {
|
||||
paramsElem = paramsElem.Elem()
|
||||
paramsElemKind = paramsElem.Kind()
|
||||
}
|
||||
if paramsElemKind != reflect.Map &&
|
||||
paramsElemKind != reflect.Struct &&
|
||||
paramsElemKind != reflect.Interface {
|
||||
return gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
"params element should be type of map/*map/struct/*struct, but got: %s",
|
||||
paramsElemKind,
|
||||
)
|
||||
}
|
||||
// Empty slice, no need continue.
|
||||
if paramsRv.Len() == 0 {
|
||||
return nil
|
||||
}
|
||||
// Pointer and its element type check.
|
||||
var (
|
||||
pointerRv = reflect.ValueOf(pointer)
|
||||
pointerKind = pointerRv.Kind()
|
||||
)
|
||||
for pointerKind == reflect.Ptr {
|
||||
pointerRv = pointerRv.Elem()
|
||||
pointerKind = pointerRv.Kind()
|
||||
}
|
||||
if pointerKind != reflect.Array && pointerKind != reflect.Slice {
|
||||
return gerror.NewCode(gcode.CodeInvalidParameter, "pointer should be type of *[]map/*[]*map")
|
||||
}
|
||||
var (
|
||||
pointerElemType = pointerRv.Type().Elem()
|
||||
pointerElemKind = pointerElemType.Kind()
|
||||
)
|
||||
if pointerElemKind == reflect.Ptr {
|
||||
pointerElemKind = pointerElemType.Elem().Kind()
|
||||
}
|
||||
if pointerElemKind != reflect.Map {
|
||||
return gerror.NewCode(gcode.CodeInvalidParameter, "pointer element should be type of map/*map")
|
||||
}
|
||||
defer func() {
|
||||
// Catch the panic, especially the reflection operation panics.
|
||||
if exception := recover(); exception != nil {
|
||||
if v, ok := exception.(error); ok && gerror.HasStack(v) {
|
||||
err = v
|
||||
} else {
|
||||
err = gerror.NewCodeSkipf(gcode.CodeInternalPanic, 1, "%+v", exception)
|
||||
}
|
||||
}
|
||||
}()
|
||||
var (
|
||||
pointerSlice = reflect.MakeSlice(pointerRv.Type(), paramsRv.Len(), paramsRv.Len())
|
||||
)
|
||||
for i := 0; i < paramsRv.Len(); i++ {
|
||||
var item reflect.Value
|
||||
if pointerElemType.Kind() == reflect.Ptr {
|
||||
item = reflect.New(pointerElemType.Elem())
|
||||
if err = c.MapToMap(paramsRv.Index(i).Interface(), item, paramKeyToAttrMap, option); err != nil {
|
||||
return err
|
||||
}
|
||||
pointerSlice.Index(i).Set(item)
|
||||
} else {
|
||||
item = reflect.New(pointerElemType)
|
||||
if err = c.MapToMap(paramsRv.Index(i).Interface(), item, paramKeyToAttrMap, option); err != nil {
|
||||
return err
|
||||
}
|
||||
pointerSlice.Index(i).Set(item.Elem())
|
||||
}
|
||||
}
|
||||
pointerRv.Set(pointerSlice)
|
||||
return
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user