1package jsoninfo 2 3import ( 4 "encoding/json" 5 "fmt" 6 "reflect" 7) 8 9// MarshalStrictStruct function: 10// * Marshals struct fields, ignoring MarshalJSON() and fields without 'json' tag. 11// * Correctly handles StrictStruct semantics. 12func MarshalStrictStruct(value StrictStruct) ([]byte, error) { 13 encoder := NewObjectEncoder() 14 if err := value.EncodeWith(encoder, value); err != nil { 15 return nil, err 16 } 17 return encoder.Bytes() 18} 19 20type ObjectEncoder struct { 21 result map[string]json.RawMessage 22} 23 24func NewObjectEncoder() *ObjectEncoder { 25 return &ObjectEncoder{ 26 result: make(map[string]json.RawMessage, 8), 27 } 28} 29 30// Bytes returns the result of encoding. 31func (encoder *ObjectEncoder) Bytes() ([]byte, error) { 32 return json.Marshal(encoder.result) 33} 34 35// EncodeExtension adds a key/value to the current JSON object. 36func (encoder *ObjectEncoder) EncodeExtension(key string, value interface{}) error { 37 data, err := json.Marshal(value) 38 if err != nil { 39 return err 40 } 41 encoder.result[key] = data 42 return nil 43} 44 45// EncodeExtensionMap adds all properties to the result. 46func (encoder *ObjectEncoder) EncodeExtensionMap(value map[string]json.RawMessage) error { 47 if value != nil { 48 result := encoder.result 49 for k, v := range value { 50 result[k] = v 51 } 52 } 53 return nil 54} 55 56func (encoder *ObjectEncoder) EncodeStructFieldsAndExtensions(value interface{}) error { 57 reflection := reflect.ValueOf(value) 58 59 // Follow "encoding/json" semantics 60 if reflection.Kind() != reflect.Ptr { 61 // Panic because this is a clear programming error 62 panic(fmt.Errorf("value %s is not a pointer", reflection.Type().String())) 63 } 64 if reflection.IsNil() { 65 // Panic because this is a clear programming error 66 panic(fmt.Errorf("value %s is nil", reflection.Type().String())) 67 } 68 69 // Take the element 70 reflection = reflection.Elem() 71 72 // Obtain typeInfo 73 typeInfo := GetTypeInfo(reflection.Type()) 74 75 // Declare result 76 result := encoder.result 77 78 // Supported fields 79iteration: 80 for _, field := range typeInfo.Fields { 81 // Fields without JSON tag are ignored 82 if !field.HasJSONTag { 83 continue 84 } 85 86 // Marshal 87 fieldValue := reflection.FieldByIndex(field.Index) 88 if v, ok := fieldValue.Interface().(json.Marshaler); ok { 89 if fieldValue.Kind() == reflect.Ptr && fieldValue.IsNil() { 90 if field.JSONOmitEmpty { 91 continue iteration 92 } 93 result[field.JSONName] = []byte("null") 94 continue 95 } 96 fieldData, err := v.MarshalJSON() 97 if err != nil { 98 return err 99 } 100 result[field.JSONName] = fieldData 101 continue 102 } 103 switch fieldValue.Kind() { 104 case reflect.Ptr, reflect.Interface: 105 if fieldValue.IsNil() { 106 if field.JSONOmitEmpty { 107 continue iteration 108 } 109 result[field.JSONName] = []byte("null") 110 continue 111 } 112 case reflect.Struct: 113 case reflect.Map: 114 if field.JSONOmitEmpty && (fieldValue.IsNil() || fieldValue.Len() == 0) { 115 continue iteration 116 } 117 case reflect.Slice: 118 if field.JSONOmitEmpty && fieldValue.Len() == 0 { 119 continue iteration 120 } 121 case reflect.Bool: 122 x := fieldValue.Bool() 123 if field.JSONOmitEmpty && !x { 124 continue iteration 125 } 126 s := "false" 127 if x { 128 s = "true" 129 } 130 result[field.JSONName] = []byte(s) 131 continue iteration 132 case reflect.Int64, reflect.Int, reflect.Int32: 133 if field.JSONOmitEmpty && fieldValue.Int() == 0 { 134 continue iteration 135 } 136 case reflect.Uint64, reflect.Uint, reflect.Uint32: 137 if field.JSONOmitEmpty && fieldValue.Uint() == 0 { 138 continue iteration 139 } 140 case reflect.Float64: 141 if field.JSONOmitEmpty && fieldValue.Float() == 0.0 { 142 continue iteration 143 } 144 case reflect.String: 145 if field.JSONOmitEmpty && len(fieldValue.String()) == 0 { 146 continue iteration 147 } 148 default: 149 panic(fmt.Errorf("field %q has unsupported type %s", field.JSONName, field.Type.String())) 150 } 151 152 // No special treament is needed 153 // Use plain old "encoding/json".Marshal 154 fieldData, err := json.Marshal(fieldValue.Addr().Interface()) 155 if err != nil { 156 return err 157 } 158 result[field.JSONName] = fieldData 159 } 160 161 return nil 162} 163