From 91bac371ffac64070bb3fab7edcd3b04ce8fe9c6 Mon Sep 17 00:00:00 2001 From: Mark Rosemaker <48681726+MarkRosemaker@users.noreply.github.com> Date: Tue, 24 Dec 2024 23:10:20 +0100 Subject: [PATCH] marshal URLs using jsonutil --- internal/json/opts.go | 5 +- internal/json/url.go | 40 --------------- internal/json/url_test.go | 105 -------------------------------------- 3 files changed, 3 insertions(+), 147 deletions(-) delete mode 100644 internal/json/url.go delete mode 100644 internal/json/url_test.go diff --git a/internal/json/opts.go b/internal/json/opts.go index d4efe1c..bf1f50b 100644 --- a/internal/json/opts.go +++ b/internal/json/opts.go @@ -1,6 +1,7 @@ package json import ( + "github.com/MarkRosemaker/jsonutil" "github.com/go-json-experiment/json" "github.com/go-json-experiment/json/jsontext" ) @@ -9,7 +10,7 @@ var Options = json.JoinOptions([]json.Options{ // unevaluatedProperties is set to false in most objects according to the OpenAPI specification // also protect against deleting unknown fields when overwriting later json.RejectUnknownMembers(true), - json.WithMarshalers(URLMarshal), - json.WithUnmarshalers(URLUnmarshal), + json.WithMarshalers(json.NewMarshalers(json.MarshalFuncV2(jsonutil.URLMarshal))), + json.WithUnmarshalers(json.NewUnmarshalers(json.UnmarshalFuncV2(jsonutil.URLUnmarshal))), jsontext.WithIndent(" "), // indent with two spaces }...) diff --git a/internal/json/url.go b/internal/json/url.go deleted file mode 100644 index f42a056..0000000 --- a/internal/json/url.go +++ /dev/null @@ -1,40 +0,0 @@ -package json - -import ( - "fmt" - "net/url" - - "github.com/go-json-experiment/json" - "github.com/go-json-experiment/json/jsontext" -) - -// URLMarshal is a custom marshaler for URL values, marshaling them as strings. -var URLMarshal = json.MarshalFuncV2(func( - enc *jsontext.Encoder, u *url.URL, _ json.Options, -) error { - return enc.WriteToken(jsontext.String(u.String())) -}) - -// URLUnmarshal is a custom unmarshaler for URL values, unmarshaling them from strings. -var URLUnmarshal = json.UnmarshalFuncV2(func( - dec *jsontext.Decoder, u *url.URL, _ json.Options, -) error { - tkn, err := dec.ReadToken() - if err != nil { - return err - } - - switch tkn.Kind() { - case '"': - parsed, err := url.Parse(tkn.String()) - if err != nil { - return err - } - - *u = *parsed - - return nil - default: - return fmt.Errorf("expected string, got %s", tkn) - } -}) diff --git a/internal/json/url_test.go b/internal/json/url_test.go deleted file mode 100644 index 9979ce8..0000000 --- a/internal/json/url_test.go +++ /dev/null @@ -1,105 +0,0 @@ -package json - -import ( - "errors" - "net/url" - "reflect" - "testing" - - "github.com/go-json-experiment/json" - "github.com/go-json-experiment/json/jsontext" -) - -var urlType = reflect.TypeFor[*url.URL]() - -type hasURL struct { - A *url.URL `json:"a,omitempty"` - B url.URL `json:"b,omitempty"` - C *url.URL `json:"c"` - D url.URL `json:"d"` -} - -func TestURL(t *testing.T) { - t.Parallel() - - for _, tc := range []struct { - name string - in string - out string - }{ - { - name: "empty", - in: `{}`, - out: `{"c":null,"d":""}`, - }, - { - name: "pointer", - in: `{"a":"http://example.com","c":"http://example.com"}`, - out: `{"a":"http://example.com","c":"http://example.com","d":""}`, - }, - { - name: "non-pointer", - in: `{"b":"http://example.com","d":"http://example.com"}`, - out: `{"b":"http://example.com","c":null,"d":"http://example.com"}`, - }, - } { - t.Run(tc.name, func(t *testing.T) { - var got hasURL - if err := json.Unmarshal([]byte(tc.in), &got, Options); err != nil { - t.Fatal(err) - } - - b, err := json.Marshal(got, Options) - if err != nil { - t.Fatal(err) - } - - js := jsontext.Value(b) - _ = js.Compact() - - if js.String() != tc.out { - t.Fatalf("got: %v, want: %v", js.String(), tc.out) - } - }) - } -} - -func TestURL_Error(t *testing.T) { - t.Parallel() - - for _, tc := range []struct { - name string - data string - err string - }{ - { - name: "invalid json", - data: `{"a":"`, - err: `unexpected EOF`, - }, - { - name: "int instead of string", - data: `{"a":42}`, - err: `expected string, got 42`, - }, - { - name: "invalid URL", - data: `{"a":"\t"}`, - err: `parse "\t": net/url: invalid control character in URL`, - }, - } { - t.Run(tc.name, func(t *testing.T) { - var got hasURL - jsonErr := &json.SemanticError{} - if err := json.Unmarshal([]byte(tc.data), &got, Options); err == nil { - t.Fatal("expected error") - } else if !errors.As(err, &jsonErr) { - t.Fatalf("expected json.SemanticError, got %T", err) - } else if jsonErr.GoType != urlType { - t.Fatalf("got: %s, want: %s", jsonErr.GoType, urlType) - } else if jsonErr.Err.Error() != tc.err { - t.Fatalf("got: %v, want: %v", jsonErr.Err, tc.err) - } - }) - } -}