github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/depends/kit/httptransport/transformer/tsfm_json.go (about) 1 package transformer 2 3 import ( 4 "context" 5 "encoding/json" 6 "io" 7 "net/textproto" 8 "reflect" 9 "strconv" 10 11 "github.com/machinefi/w3bstream/pkg/depends/kit/httptransport/httpx" 12 vldterr "github.com/machinefi/w3bstream/pkg/depends/kit/validator/errors" 13 "github.com/machinefi/w3bstream/pkg/depends/x/typesx" 14 ) 15 16 func init() { DefaultFactory.Register(&JSON{}) } 17 18 type JSON struct{} 19 20 func (JSON) Names() []string { return []string{httpx.MIME_JSON, "json"} } 21 22 func (JSON) NamedByTag() string { return "json" } 23 24 func (t *JSON) String() string { return httpx.MIME_JSON } 25 26 func (JSON) New(context.Context, typesx.Type) (Transformer, error) { return &JSON{}, nil } 27 28 func (t *JSON) EncodeTo(ctx context.Context, w io.Writer, v interface{}) error { 29 if rv, ok := v.(reflect.Value); ok { 30 v = rv.Interface() 31 } 32 33 httpx.MaybeWriteHeader(ctx, w, t.String(), map[string]string{ 34 "charset": "utf-8", 35 }) 36 37 return json.NewEncoder(w).Encode(v) 38 } 39 40 func (JSON) DecodeFrom(ctx context.Context, r io.Reader, v interface{}, _ ...textproto.MIMEHeader) error { 41 if rv, ok := v.(reflect.Value); ok { 42 if rv.Kind() != reflect.Ptr && rv.CanAddr() { 43 rv = rv.Addr() 44 } 45 v = rv.Interface() 46 } 47 48 dec := json.NewDecoder(r) 49 if err := dec.Decode(v); err != nil { 50 return WrapLocationDecoderError(dec, err) 51 } 52 return nil 53 } 54 55 func WrapLocationDecoderError(dec *json.Decoder, err error) error { 56 switch e := err.(type) { 57 case *json.UnmarshalTypeError: 58 r := reflect.ValueOf(dec).Elem() 59 errs := vldterr.NewErrorSet() 60 errs.AddErr(e, location(r.Field(1 /* .buf */).Bytes(), int(e.Offset))) 61 return errs.Err() 62 case *json.SyntaxError: 63 return e 64 default: 65 r := reflect.ValueOf(dec).Elem() 66 // json.Decoder.d.off 67 offset := r.Field(2).Field(1).Int() 68 if offset > 0 { 69 errs := vldterr.NewErrorSet() 70 // json.Decoder.buf 71 errs.AddErr(e, location(r.Field(1).Bytes(), int(offset-1))) 72 return errs.Err() 73 } 74 return e 75 } 76 } 77 78 func location(data []byte, offset int) string { 79 i := 0 80 arrayPaths := map[string]bool{} 81 arrayIdxSet := map[string]int{} 82 pw := &PathWalker{} 83 84 markObjectKey := func() { 85 jsonKey, l := nextString(data[i:]) 86 i += l 87 88 if i < int(offset) && len(jsonKey) > 0 { 89 key, _ := strconv.Unquote(string(jsonKey)) 90 pw.Enter(key) 91 } 92 } 93 94 markArrayIdx := func(path string) { 95 if arrayPaths[path] { 96 arrayIdxSet[path]++ 97 } else { 98 arrayPaths[path] = true 99 } 100 pw.Enter(arrayIdxSet[path]) 101 } 102 103 for i < offset { 104 i += nextToken(data[i:]) 105 char := data[i] 106 107 switch char { 108 case '"': 109 _, l := nextString(data[i:]) 110 i += l 111 case '[', '{': 112 i++ 113 114 if char == '[' { 115 markArrayIdx(pw.String()) 116 } else { 117 markObjectKey() 118 } 119 case '}', ']', ',': 120 i++ 121 pw.Exit() 122 123 if char == ',' { 124 path := pw.String() 125 126 if _, ok := arrayPaths[path]; ok { 127 markArrayIdx(path) 128 } else { 129 markObjectKey() 130 } 131 } 132 default: 133 i++ 134 } 135 } 136 137 return pw.String() 138 } 139 140 func nextToken(data []byte) int { 141 for i, c := range data { 142 switch c { 143 case ' ', '\n', '\r', '\t': 144 continue 145 default: 146 return i 147 } 148 } 149 return -1 150 } 151 152 func nextString(data []byte) (finalData []byte, l int) { 153 quoteStartAt := -1 154 for i, c := range data { 155 switch c { 156 case '"': 157 if i > 0 && string(data[i-1]) == "\\" { 158 continue 159 } 160 if quoteStartAt >= 0 { 161 return data[quoteStartAt : i+1], i + 1 162 } else { 163 quoteStartAt = i 164 } 165 default: 166 continue 167 } 168 } 169 return nil, 0 170 }