github.com/prebid/prebid-server/v2@v2.18.0/util/jsonutil/merge.go (about) 1 package jsonutil 2 3 import ( 4 "encoding/json" 5 "errors" 6 "reflect" 7 "unsafe" 8 9 jsoniter "github.com/json-iterator/go" 10 "github.com/modern-go/reflect2" 11 jsonpatch "gopkg.in/evanphx/json-patch.v4" 12 13 "github.com/prebid/prebid-server/v2/errortypes" 14 "github.com/prebid/prebid-server/v2/util/reflectutil" 15 ) 16 17 // jsonConfigMergeClone uses the same configuration as the `ConfigCompatibleWithStandardLibrary` profile 18 // with extensions added to support the merge clone behavior. 19 var jsonConfigMergeClone = jsoniter.Config{ 20 EscapeHTML: true, 21 SortMapKeys: true, 22 ValidateJsonRawMessage: true, 23 }.Froze() 24 25 func init() { 26 jsonConfigMergeClone.RegisterExtension(&mergeCloneExtension{}) 27 } 28 29 // MergeClone unmarshals json data on top of an existing object and clones pointers of 30 // the existing object before setting new values. Slices and maps are also cloned. 31 // Fields of type json.RawMessage are merged rather than replaced. 32 func MergeClone(v any, data json.RawMessage) error { 33 err := jsonConfigMergeClone.Unmarshal(data, v) 34 if err != nil { 35 return &errortypes.FailedToUnmarshal{ 36 Message: tryExtractErrorMessage(err), 37 } 38 } 39 return err 40 } 41 42 type mergeCloneExtension struct { 43 jsoniter.DummyExtension 44 } 45 46 func (e *mergeCloneExtension) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder { 47 if typ == jsonRawMessageType { 48 return &extMergeDecoder{sliceType: typ.(*reflect2.UnsafeSliceType)} 49 } 50 return nil 51 } 52 53 func (e *mergeCloneExtension) DecorateDecoder(typ reflect2.Type, decoder jsoniter.ValDecoder) jsoniter.ValDecoder { 54 if typ.Kind() == reflect.Ptr { 55 ptrType := typ.(*reflect2.UnsafePtrType) 56 return &ptrCloneDecoder{valueDecoder: decoder, elemType: ptrType.Elem()} 57 } 58 59 // don't use json.RawMessage on fields handled by extMergeDecoder 60 if typ.Kind() == reflect.Slice && typ != jsonRawMessageType { 61 return &sliceCloneDecoder{valueDecoder: decoder, sliceType: typ.(*reflect2.UnsafeSliceType)} 62 } 63 64 if typ.Kind() == reflect.Map { 65 return &mapCloneDecoder{valueDecoder: decoder, mapType: typ.(*reflect2.UnsafeMapType)} 66 } 67 68 return decoder 69 } 70 71 type ptrCloneDecoder struct { 72 elemType reflect2.Type 73 valueDecoder jsoniter.ValDecoder 74 } 75 76 func (d *ptrCloneDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { 77 // don't clone if field is being set to nil. checking for nil "consumes" the null 78 // token, so must be handled in this decoder. 79 if iter.ReadNil() { 80 *((*unsafe.Pointer)(ptr)) = nil 81 return 82 } 83 84 // clone if there is an existing object. creation of new objects is handled by the 85 // original decoder. 86 if *((*unsafe.Pointer)(ptr)) != nil { 87 obj := d.elemType.UnsafeNew() 88 d.elemType.UnsafeSet(obj, *((*unsafe.Pointer)(ptr))) 89 *((*unsafe.Pointer)(ptr)) = obj 90 } 91 92 d.valueDecoder.Decode(ptr, iter) 93 } 94 95 type sliceCloneDecoder struct { 96 sliceType *reflect2.UnsafeSliceType 97 valueDecoder jsoniter.ValDecoder 98 } 99 100 func (d *sliceCloneDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { 101 // don't clone if field is being set to nil. checking for nil "consumes" the null 102 // token, so must be handled in this decoder. 103 if iter.ReadNil() { 104 d.sliceType.UnsafeSetNil(ptr) 105 return 106 } 107 108 // clone if there is an existing object. creation of new objects is handled by the 109 // original decoder. 110 if !d.sliceType.UnsafeIsNil(ptr) { 111 clone := reflectutil.UnsafeSliceClone(ptr, d.sliceType) 112 d.sliceType.UnsafeSet(ptr, clone) 113 } 114 115 d.valueDecoder.Decode(ptr, iter) 116 } 117 118 type mapCloneDecoder struct { 119 mapType *reflect2.UnsafeMapType 120 valueDecoder jsoniter.ValDecoder 121 } 122 123 func (d *mapCloneDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { 124 // don't clone if field is being set to nil. checking for nil "consumes" the null 125 // token, so must be handled in this decoder. 126 if iter.ReadNil() { 127 *(*unsafe.Pointer)(ptr) = nil 128 d.mapType.UnsafeSet(ptr, d.mapType.UnsafeNew()) 129 return 130 } 131 132 // clone if there is an existing object. creation of new objects is handled by the 133 // original decoder. 134 if !d.mapType.UnsafeIsNil(ptr) { 135 clone := d.mapType.UnsafeMakeMap(0) 136 mapIter := d.mapType.UnsafeIterate(ptr) 137 for mapIter.HasNext() { 138 key, elem := mapIter.UnsafeNext() 139 d.mapType.UnsafeSetIndex(clone, key, elem) 140 } 141 d.mapType.UnsafeSet(ptr, clone) 142 } 143 144 d.valueDecoder.Decode(ptr, iter) 145 } 146 147 type extMergeDecoder struct { 148 sliceType *reflect2.UnsafeSliceType 149 } 150 151 func (d *extMergeDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { 152 // incoming nil value, keep existing 153 if iter.ReadNil() { 154 return 155 } 156 157 existing := *((*json.RawMessage)(ptr)) 158 incoming := iter.SkipAndReturnBytes() 159 160 // check for read errors to avoid calling jsonpatch.MergePatch on bad data. 161 if iter.Error != nil { 162 return 163 } 164 165 // existing empty value, use incoming 166 if len(existing) == 0 { 167 *((*json.RawMessage)(ptr)) = incoming 168 return 169 } 170 171 // non-empty incoming and existing values, merge 172 merged, err := jsonpatch.MergePatch(existing, incoming) 173 if err != nil { 174 if errors.Is(err, jsonpatch.ErrBadJSONDoc) { 175 iter.ReportError("merge", "invalid json on existing object") 176 } else { 177 iter.ReportError("merge", err.Error()) 178 } 179 return 180 } 181 182 *((*json.RawMessage)(ptr)) = merged 183 }