diff --git a/api.go b/api.go index fa738f21d..9525b7afd 100644 --- a/api.go +++ b/api.go @@ -68,7 +68,11 @@ type Config struct { // ValidateString indicates decoder and encoder to valid string values: decoder will return errors // when unescaped control chars(\u0000-\u001f) in the string value of JSON. - ValidateString bool + ValidateString bool + + // NoValidateJSONMarshaler indicates that the encoder should not validate the output string + // after encoding the JSONMarshaler to JSON. + NoValidateJSONMarshaler bool } var ( @@ -87,6 +91,7 @@ var ( // ConfigFastest is the fastest config of APIs, aiming at speed. ConfigFastest = Config{ NoQuoteTextMarshaler: true, + NoValidateJSONMarshaler: true, }.Froze() ) diff --git a/encoder/encoder_amd64.go b/encoder/encoder_amd64.go index 0ef336b72..e93b09a25 100644 --- a/encoder/encoder_amd64.go +++ b/encoder/encoder_amd64.go @@ -59,6 +59,10 @@ const ( // before encoding it into JSON. ValidateString Options = encoder.ValidateString + // NoValidateJSONMarshaler indicates that the encoder should not validate the output string + // after encoding the JSONMarshaler to JSON. + NoValidateJSONMarshaler Options = encoder.NoValidateJSONMarshaler + // CompatibleWithStd is used to be compatible with std encoder. CompatibleWithStd Options = encoder.CompatibleWithStd ) diff --git a/encoder/encoder_compat.go b/encoder/encoder_compat.go index 222eea5b2..2e02b59cd 100644 --- a/encoder/encoder_compat.go +++ b/encoder/encoder_compat.go @@ -41,6 +41,7 @@ const ( bitNoQuoteTextMarshaler bitNoNullSliceOrMap bitValidateString + bitNoValidateJSONMarshaler // used for recursive compile bitPointerValue = 63 @@ -72,6 +73,10 @@ const ( // ValidateString indicates that encoder should validate the input string // before encoding it into JSON. ValidateString Options = 1 << bitValidateString + + // NoValidateJSONMarshaler indicates that the encoder should not validate the output string + // after encoding the JSONMarshaler to JSON. + NoValidateJSONMarshaler Options = 1 << bitNoValidateJSONMarshaler // CompatibleWithStd is used to be compatible with std encoder. CompatibleWithStd Options = SortMapKeys | EscapeHTML | CompactMarshaler @@ -116,6 +121,15 @@ func (self *Encoder) SetValidateString(f bool) { } } +// SetNoValidateJSONMarshaler specifies if option NoValidateJSONMarshaler opens +func (self *Encoder) SetNoValidateJSONMarshaler(f bool) { + if f { + self.Opts |= NoValidateJSONMarshaler + } else { + self.Opts &= ^NoValidateJSONMarshaler + } +} + // SetCompactMarshaler specifies if option CompactMarshaler opens func (self *Encoder) SetCompactMarshaler(f bool) { if f { diff --git a/internal/encoder/encoder.go b/internal/encoder/encoder.go index 757e73ff0..bd8bae357 100644 --- a/internal/encoder/encoder.go +++ b/internal/encoder/encoder.go @@ -40,6 +40,7 @@ const ( bitNoQuoteTextMarshaler bitNoNullSliceOrMap bitValidateString + bitNoValidateJSONMarshaler // used for recursive compile bitPointerValue = 63 @@ -71,6 +72,10 @@ const ( // ValidateString indicates that encoder should validate the input string // before encoding it into JSON. ValidateString Options = 1 << bitValidateString + + // NoValidateJSONMarshaler indicates that the encoder should not validate the output string + // after encoding the JSONMarshaler to JSON. + NoValidateJSONMarshaler Options = 1 << bitNoValidateJSONMarshaler // CompatibleWithStd is used to be compatible with std encoder. CompatibleWithStd Options = SortMapKeys | EscapeHTML | CompactMarshaler @@ -115,6 +120,15 @@ func (self *Encoder) SetValidateString(f bool) { } } +// SetNoValidateJSONMarshaler specifies if option NoValidateJSONMarshaler opens +func (self *Encoder) SetNoValidateJSONMarshaler(f bool) { + if f { + self.Opts |= NoValidateJSONMarshaler + } else { + self.Opts &= ^NoValidateJSONMarshaler + } +} + // SetCompactMarshaler specifies if option CompactMarshaler opens func (self *Encoder) SetCompactMarshaler(f bool) { if f { diff --git a/internal/encoder/primitives.go b/internal/encoder/primitives.go index 318038967..0e47987c7 100644 --- a/internal/encoder/primitives.go +++ b/internal/encoder/primitives.go @@ -93,8 +93,10 @@ func encodeJsonMarshaler(buf *[]byte, val json.Marshaler, opt Options) error { if opt & CompactMarshaler != 0 { return compact(buf, ret) } - if ok, s := Valid(ret); !ok { - return error_marshaler(ret, s) + if opt & NoValidateJSONMarshaler == 0 { + if ok, s := Valid(ret); !ok { + return error_marshaler(ret, s) + } } *buf = append(*buf, ret...) return nil diff --git a/sonic.go b/sonic.go index 58edb2829..1da238895 100644 --- a/sonic.go +++ b/sonic.go @@ -58,6 +58,9 @@ func (cfg Config) Froze() API { if cfg.ValidateString { api.encoderOpts |= encoder.ValidateString } + if cfg.NoValidateJSONMarshaler { + api.encoderOpts |= encoder.NoValidateJSONMarshaler + } // configure decoder options: if cfg.UseInt64 { @@ -139,23 +142,23 @@ func (cfg frozenConfig) Valid(data []byte) bool { // Opts are the compile options, for example, "option.WithCompileRecursiveDepth" is // a compile option to set the depth of recursive compile for the nested struct type. func Pretouch(vt reflect.Type, opts ...option.CompileOption) error { - if err := encoder.Pretouch(vt, opts...); err != nil { - return err - } - if err := decoder.Pretouch(vt, opts...); err != nil { - return err - } - // to pretouch the corresponding pointer type as well - if vt.Kind() == reflect.Ptr { - vt = vt.Elem() - } else { - vt = reflect.PtrTo(vt) - } - if err := encoder.Pretouch(vt, opts...); err != nil { - return err - } - if err := decoder.Pretouch(vt, opts...); err != nil { - return err - } - return nil + if err := encoder.Pretouch(vt, opts...); err != nil { + return err + } + if err := decoder.Pretouch(vt, opts...); err != nil { + return err + } + // to pretouch the corresponding pointer type as well + if vt.Kind() == reflect.Ptr { + vt = vt.Elem() + } else { + vt = reflect.PtrTo(vt) + } + if err := encoder.Pretouch(vt, opts...); err != nil { + return err + } + if err := decoder.Pretouch(vt, opts...); err != nil { + return err + } + return nil }