Skip to content

Commit 73440d7

Browse files
committed
feat: make JSON lib configurable
Currently, a mix of goccy/json and encoding/json is used. Both use the same interface; goccy/json is faster, however, it also does questionable things with go:linkname. Create a common package that can be switched (via build tags) from one implementation to another, and use that in all packages.
1 parent ed6737b commit 73440d7

17 files changed

+136
-14
lines changed

Diff for: api-bucket-notification.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626
"net/url"
2727
"time"
2828

29-
"github.com/goccy/go-json"
29+
"github.com/minio/minio-go/v7/internal/json"
3030
"github.com/minio/minio-go/v7/pkg/notification"
3131
"github.com/minio/minio-go/v7/pkg/s3utils"
3232
)

Diff for: api-bucket-replication.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ package minio
2020
import (
2121
"bytes"
2222
"context"
23-
"encoding/json"
2423
"encoding/xml"
2524
"io"
2625
"net/http"
2726
"net/url"
2827
"time"
2928

3029
"github.com/google/uuid"
30+
"github.com/minio/minio-go/v7/internal/json"
3131
"github.com/minio/minio-go/v7/pkg/replication"
3232
"github.com/minio/minio-go/v7/pkg/s3utils"
3333
)

Diff for: api-prompt-object.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323
"io"
2424
"net/http"
2525

26-
"github.com/goccy/go-json"
26+
"github.com/minio/minio-go/v7/internal/json"
2727
"github.com/minio/minio-go/v7/pkg/s3utils"
2828
)
2929

Diff for: api-put-object-fan-out.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ package minio
1919

2020
import (
2121
"context"
22-
"encoding/json"
2322
"errors"
2423
"io"
2524
"mime/multipart"
@@ -28,6 +27,7 @@ import (
2827
"strings"
2928
"time"
3029

30+
"github.com/minio/minio-go/v7/internal/json"
3131
"github.com/minio/minio-go/v7/pkg/encrypt"
3232
)
3333

Diff for: internal/json/json_goccy.go

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//go:build !stdlibjson
2+
3+
/*
4+
* MinIO Go Library for Amazon S3 Compatible Cloud Storage
5+
* Copyright 2025 MinIO, Inc.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
package json
21+
22+
import "github.com/goccy/go-json"
23+
24+
// This file defines the JSON functions used internally and forwards them
25+
// to goccy/go-json. Alternatively, the standard library can be used by setting
26+
// the build tag stdlibjson. This can be useful for testing purposes or if
27+
// goccy/go-json causes issues.
28+
//
29+
// This file does not contain all definitions from goccy/go-json; if needed, more
30+
// can be added, but keep in mind that json_stdlib.go will also need to be
31+
// updated.
32+
33+
var (
34+
// Unmarshal is a wrapper around goccy/go-json Unmarshal function.
35+
Unmarshal = json.Unmarshal
36+
// Marshal is a wrapper around goccy/go-json Marshal function.
37+
Marshal = json.Marshal
38+
// NewEncoder is a wrapper around goccy/go-json NewEncoder function.
39+
NewEncoder = json.NewEncoder
40+
// NewDecoder is a wrapper around goccy/go-json NewDecoder function.
41+
NewDecoder = json.NewDecoder
42+
)
43+
44+
type (
45+
// Encoder is an alias for goccy/go-json Encoder.
46+
Encoder = json.Encoder
47+
// Decoder is an alias for goccy/go-json Decoder.
48+
Decoder = json.Decoder
49+
)

Diff for: internal/json/json_stdlib.go

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//go:build stdlibjson
2+
3+
/*
4+
* MinIO Go Library for Amazon S3 Compatible Cloud Storage
5+
* Copyright 2025 MinIO, Inc.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
20+
package json
21+
22+
import "encoding/json"
23+
24+
// This file defines the JSON functions used internally and forwards them
25+
// to encoding/json. This is only enabled by setting the build tag stdlibjson,
26+
// otherwise json_goccy.go applies.
27+
// This can be useful for testing purposes or if goccy/go-json (which is used otherwise) causes issues.
28+
//
29+
// This file does not contain all definitions from encoding/json; if needed, more
30+
// can be added, but keep in mind that json_goccy.go will also need to be
31+
// updated.
32+
33+
var (
34+
// Unmarshal is a wrapper around encoding/json Unmarshal function.
35+
Unmarshal = json.Unmarshal
36+
// Marshal is a wrapper around encoding/json Marshal function.
37+
Marshal = json.Marshal
38+
// NewEncoder is a wrapper around encoding/json NewEncoder function.
39+
NewEncoder = json.NewEncoder
40+
// NewDecoder is a wrapper around encoding/json NewDecoder function.
41+
NewDecoder = json.NewDecoder
42+
)
43+
44+
type (
45+
// Encoder is an alias for encoding/json Encoder.
46+
Encoder = json.Encoder
47+
// Decoder is an alias for encoding/json Decoder.
48+
Decoder = json.Decoder
49+
)

Diff for: pkg/credentials/file_aws_credentials.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
package credentials
1919

2020
import (
21-
"encoding/json"
2221
"errors"
2322
"os"
2423
"os/exec"
@@ -27,6 +26,7 @@ import (
2726
"time"
2827

2928
"github.com/go-ini/ini"
29+
"github.com/minio/minio-go/v7/internal/json"
3030
)
3131

3232
// A externalProcessCredentials stores the output of a credential_process

Diff for: pkg/credentials/file_minio_client.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import (
2222
"path/filepath"
2323
"runtime"
2424

25-
"github.com/goccy/go-json"
25+
"github.com/minio/minio-go/v7/internal/json"
2626
)
2727

2828
// A FileMinioClient retrieves credentials from the current user's home

Diff for: pkg/credentials/iam_aws.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import (
3131
"strings"
3232
"time"
3333

34-
"github.com/goccy/go-json"
34+
"github.com/minio/minio-go/v7/internal/json"
3535
)
3636

3737
// DefaultExpiryWindow - Default expiry window.

Diff for: pkg/encrypt/server-side.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import (
2323
"errors"
2424
"net/http"
2525

26-
"github.com/goccy/go-json"
26+
"github.com/minio/minio-go/v7/internal/json"
2727
"golang.org/x/crypto/argon2"
2828
)
2929

Diff for: pkg/lifecycle/lifecycle.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@
1919
package lifecycle
2020

2121
import (
22-
"encoding/json"
2322
"encoding/xml"
2423
"errors"
2524
"time"
25+
26+
"github.com/minio/minio-go/v7/internal/json"
2627
)
2728

2829
var errMissingStorageClass = errors.New("storage-class cannot be empty")

Diff for: pkg/lifecycle/lifecycle_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ package lifecycle
1919

2020
import (
2121
"bytes"
22-
"encoding/json"
2322
"encoding/xml"
2423
"testing"
2524
"time"
25+
26+
"github.com/minio/minio-go/v7/internal/json"
2627
)
2728

2829
func TestLifecycleUnmarshalJSON(t *testing.T) {

Diff for: pkg/policy/bucket-policy-condition.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@
1717

1818
package policy
1919

20-
import "github.com/minio/minio-go/v7/pkg/set"
20+
import (
21+
"encoding/json"
22+
23+
"github.com/minio/minio-go/v7/pkg/set"
24+
)
2125

2226
// ConditionKeyMap - map of policy condition key and value.
2327
type ConditionKeyMap map[string]set.StringSet
@@ -92,6 +96,13 @@ func (cond ConditionMap) Remove(condKey string) {
9296
delete(cond, condKey)
9397
}
9498

99+
// MarshalJSON is a custom json marshaler. It is needed due to
100+
// https://github.com/goccy/go-json/issues/543
101+
// and circumvents the issue by using the stdlib json package.
102+
func (cond ConditionMap) MarshalJSON() ([]byte, error) {
103+
return json.Marshal(map[string]ConditionKeyMap(cond))
104+
}
105+
95106
// mergeConditionMap - returns new ConditionMap which contains merged key/value of two ConditionMap.
96107
func mergeConditionMap(condMap1, condMap2 ConditionMap) ConditionMap {
97108
out := make(ConditionMap)

Diff for: pkg/policy/bucket-policy-condition_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ func TestConditionKeyMapAdd(t *testing.T) {
4242

4343
for _, testCase := range testCases {
4444
condKeyMap.Add(testCase.key, testCase.value)
45+
// Must be encoded with stdlib json due to https://github.com/goccy/go-json/issues/543
4546
if data, err := json.Marshal(condKeyMap); err != nil {
4647
t.Fatalf("Unable to marshal ConditionKeyMap to JSON, %s", err)
4748
} else {

Diff for: pkg/policy/bucket-policy.go

+11-1
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@
1818
package policy
1919

2020
import (
21-
"encoding/json"
21+
stdjson "encoding/json"
2222
"errors"
2323
"reflect"
2424
"strings"
2525

26+
"github.com/minio/minio-go/v7/internal/json"
2627
"github.com/minio/minio-go/v7/pkg/set"
2728
)
2829

@@ -121,6 +122,15 @@ type Statement struct {
121122
Sid string
122123
}
123124

125+
// MarshalJSON is a custom json marshaler. It is needed due to
126+
// the error reported in https://github.com/goccy/go-json/pull/545
127+
// (which prevents goccy/go-json from omitting the empty Conditions field)
128+
// and circumvents the issue by using the stdlib json package.
129+
func (s Statement) MarshalJSON() ([]byte, error) {
130+
type AliasStatement Statement
131+
return stdjson.Marshal(AliasStatement(s))
132+
}
133+
124134
// BucketAccessPolicy - minio policy collection
125135
type BucketAccessPolicy struct {
126136
Version string // date in YYYY-MM-DD format

Diff for: pkg/policy/bucket-policy_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@
1818
package policy
1919

2020
import (
21-
"encoding/json"
2221
"fmt"
2322
"reflect"
2423
"testing"
2524

25+
"github.com/minio/minio-go/v7/internal/json"
2626
"github.com/minio/minio-go/v7/pkg/set"
2727
)
2828

Diff for: pkg/set/stringset.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import (
2121
"fmt"
2222
"sort"
2323

24-
"github.com/goccy/go-json"
24+
"github.com/minio/minio-go/v7/internal/json"
2525
)
2626

2727
// StringSet - uses map as set of strings.

0 commit comments

Comments
 (0)