Skip to content

Commit d810180

Browse files
committed
fine tune
1 parent f302ca8 commit d810180

File tree

7 files changed

+59
-30
lines changed

7 files changed

+59
-30
lines changed

modules/json/json_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package json
55

66
import (
7+
"bytes"
78
"testing"
89

910
"github.com/stretchr/testify/assert"
@@ -16,3 +17,12 @@ func TestGiteaDBJSONUnmarshal(t *testing.T) {
1617
err = UnmarshalHandleDoubleEncode([]byte(""), &m)
1718
assert.NoError(t, err)
1819
}
20+
21+
func TestIndent(t *testing.T) {
22+
buf := &bytes.Buffer{}
23+
err := Indent(buf, []byte(`{"a":1}`), ">", " ")
24+
assert.NoError(t, err)
25+
assert.Equal(t, `{
26+
> "a": 1
27+
>}`, buf.String())
28+
}

modules/json/jsonlegacy.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,20 @@
55

66
package json
77

8-
import jsoniter "github.com/json-iterator/go"
8+
import (
9+
"io"
10+
11+
jsoniter "github.com/json-iterator/go"
12+
)
913

1014
func getDefaultJSONHandler() Interface {
1115
return JSONiter{jsoniter.ConfigCompatibleWithStandardLibrary}
1216
}
1317

14-
func MarshalKeepOptionalEmpty(v any) ([]byte, error) {
15-
return DefaultJSONHandler.Marshal(v)
18+
func NewEncoderOptionalEmpty(writer io.Writer) Encoder {
19+
return DefaultJSONHandler.NewEncoder(writer)
20+
}
21+
22+
func NewDecoderCaseInsensitive(reader io.Reader) Decoder {
23+
return DefaultJSONHandler.NewDecoder(reader)
1624
}

modules/json/jsonv2.go

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,9 @@ import (
1616
// Requires GOEXPERIMENT=jsonv2 to be set at build time
1717
type JSONv2 struct {
1818
marshalOptions jsonv2.Options
19-
marshalKeepOptionalEmptyOptions jsonv2.Options
19+
marshalOptionalEmptyOptions jsonv2.Options
2020
unmarshalOptions jsonv2.Options
21+
unmarshalCaseInsensitiveOptions jsonv2.Options
2122
}
2223

2324
var jsonV2 JSONv2
@@ -28,58 +29,63 @@ func init() {
2829
jsonv2.FormatNilMapAsNull(true),
2930
}
3031
jsonV2.marshalOptions = jsonv2.JoinOptions(commonMarshalOptions...)
32+
jsonV2.unmarshalOptions = jsonv2.DefaultOptionsV2()
3133

32-
// Some JSON structs like oci.ImageConfig uses case-insensitive matching
33-
jsonV2.unmarshalOptions = jsonv2.JoinOptions(jsonv2.MatchCaseInsensitiveNames(true))
34-
35-
// by default, "json/v2" omitempty removes all `""` empty strings, no matter where it comes from.
34+
// By default, "json/v2" omitempty removes all `""` empty strings, no matter where it comes from.
3635
// v1 has a different behavior: if the `""` is from a null pointer, or a Marshal function, it is kept.
37-
jsonV2.marshalKeepOptionalEmptyOptions = jsonv2.JoinOptions(append(commonMarshalOptions, jsonv1.OmitEmptyWithLegacySemantics(true))...)
36+
jsonV2.marshalOptionalEmptyOptions = jsonv2.JoinOptions(append(commonMarshalOptions, jsonv1.OmitEmptyWithLegacySemantics(true))...)
37+
38+
// Some legacy code uses case-insensitive matching (for example: parsing oci.ImageConfig)
39+
jsonV2.unmarshalCaseInsensitiveOptions = jsonv2.JoinOptions(jsonv2.MatchCaseInsensitiveNames(true))
3840
}
3941

4042
func getDefaultJSONHandler() Interface {
4143
return &jsonV2
4244
}
4345

44-
func MarshalKeepOptionalEmpty(v any) ([]byte, error) {
45-
return jsonv2.Marshal(v, jsonV2.marshalKeepOptionalEmptyOptions)
46-
}
47-
48-
func (j JSONv2) Marshal(v any) ([]byte, error) {
46+
func (j *JSONv2) Marshal(v any) ([]byte, error) {
4947
return jsonv2.Marshal(v, j.marshalOptions)
5048
}
5149

52-
func (j JSONv2) Unmarshal(data []byte, v any) error {
50+
func (j *JSONv2) Unmarshal(data []byte, v any) error {
5351
return jsonv2.Unmarshal(data, v, j.unmarshalOptions)
5452
}
5553

56-
func (j JSONv2) NewEncoder(writer io.Writer) Encoder {
57-
return &encoderV2{writer: writer, opts: j.marshalOptions}
54+
func (j *JSONv2) NewEncoder(writer io.Writer) Encoder {
55+
return &jsonV2Encoder{writer: writer, opts: j.marshalOptions}
5856
}
5957

60-
func (j JSONv2) NewDecoder(reader io.Reader) Decoder {
61-
return &decoderV2{reader: reader, opts: j.unmarshalOptions}
58+
func (j *JSONv2) NewDecoder(reader io.Reader) Decoder {
59+
return &jsonV2Decoder{reader: reader, opts: j.unmarshalOptions}
6260
}
6361

6462
// Indent implements Interface using standard library (JSON v2 doesn't have Indent yet)
65-
func (JSONv2) Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
63+
func (*JSONv2) Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
6664
return jsonv1.Indent(dst, src, prefix, indent)
6765
}
6866

69-
type encoderV2 struct {
67+
type jsonV2Encoder struct {
7068
writer io.Writer
7169
opts jsonv2.Options
7270
}
7371

74-
func (e *encoderV2) Encode(v any) error {
72+
func (e *jsonV2Encoder) Encode(v any) error {
7573
return jsonv2.MarshalWrite(e.writer, v, e.opts)
7674
}
7775

78-
type decoderV2 struct {
76+
type jsonV2Decoder struct {
7977
reader io.Reader
8078
opts jsonv2.Options
8179
}
8280

83-
func (d *decoderV2) Decode(v any) error {
81+
func (d *jsonV2Decoder) Decode(v any) error {
8482
return jsonv2.UnmarshalRead(d.reader, v, d.opts)
8583
}
84+
85+
func NewEncoderOptionalEmpty(writer io.Writer) Encoder {
86+
return &jsonV2Encoder{writer: writer, opts: jsonV2.marshalOptionalEmptyOptions}
87+
}
88+
89+
func NewDecoderCaseInsensitive(reader io.Reader) Decoder {
90+
return &jsonV2Decoder{reader: reader, opts: jsonV2.unmarshalCaseInsensitiveOptions}
91+
}

modules/optional/serialization_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package optional_test
55

66
import (
77
std_json "encoding/json" //nolint:depguard // for testing purpose
8+
"strings"
89
"testing"
910

1011
"code.gitea.io/gitea/modules/json"
@@ -54,11 +55,12 @@ func TestOptionalToJson(t *testing.T) {
5455
}
5556
for _, tc := range tests {
5657
t.Run(tc.name, func(t *testing.T) {
57-
b, err := json.MarshalKeepOptionalEmpty(tc.obj)
58+
buf := &strings.Builder{}
59+
err := json.NewEncoderOptionalEmpty(buf).Encode(tc.obj)
5860
assert.NoError(t, err)
59-
assert.Equal(t, tc.want, string(b), "gitea json module returned unexpected")
61+
assert.Equal(t, tc.want, buf.String(), "gitea json module returned unexpected")
6062

61-
b, err = std_json.Marshal(tc.obj)
63+
b, err := std_json.Marshal(tc.obj)
6264
assert.NoError(t, err)
6365
assert.Equal(t, tc.want, string(b), "std json module returned unexpected")
6466
})

modules/packages/container/metadata.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,9 @@ func ParseImageConfig(mediaType string, r io.Reader) (*Metadata, error) {
103103

104104
func parseOCIImageConfig(r io.Reader) (*Metadata, error) {
105105
var image oci.Image
106-
if err := json.NewDecoder(r).Decode(&image); err != nil {
106+
// FIXME: JSON-KEY-CASE: here seems a abuse of the case-insensitive decoding feature, spec is case-sensitive
107+
// https://github.com/opencontainers/image-spec/blob/main/schema/config-schema.json
108+
if err := json.NewDecoderCaseInsensitive(r).Decode(&image); err != nil {
107109
return nil, err
108110
}
109111

modules/packages/container/metadata_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func TestParseImageConfig(t *testing.T) {
2222
repositoryURL := "https://gitea.com/gitea"
2323
documentationURL := "https://docs.gitea.com"
2424

25-
// FIXME: the test case is not right, the config fields are capitalized in the spec
25+
// FIXME: JSON-KEY-CASE: the test case is not right, the config fields are capitalized in the spec
2626
// https://github.com/opencontainers/image-spec/blob/main/schema/config-schema.json
2727
configOCI := `{"config": {"labels": {"` + labelAuthors + `": "` + author + `", "` + labelLicenses + `": "` + license + `", "` + labelURL + `": "` + projectURL + `", "` + labelSource + `": "` + repositoryURL + `", "` + labelDocumentation + `": "` + documentationURL + `", "` + labelDescription + `": "` + description + `"}}, "history": [{"created_by": "do it 1"}, {"created_by": "dummy #(nop) do it 2"}]}`
2828

tests/integration/integration_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,8 @@ func logUnexpectedResponse(t testing.TB, recorder *httptest.ResponseRecorder) {
413413
func DecodeJSON(t testing.TB, resp *httptest.ResponseRecorder, v any) {
414414
t.Helper()
415415

416-
decoder := json.NewDecoder(resp.Body)
416+
// FIXME: JSON-KEY-CASE: for testing purpose only, because many structs don't provide `json` tags, they just use capitalized field names
417+
decoder := json.NewDecoderCaseInsensitive(resp.Body)
417418
require.NoError(t, decoder.Decode(v))
418419
}
419420

0 commit comments

Comments
 (0)