github.com/kaptinlin/jsonschema@v0.4.6/rat.go (about) 1 package jsonschema 2 3 import ( 4 "fmt" 5 "math/big" 6 "strings" 7 8 "github.com/goccy/go-json" 9 ) 10 11 // Rat wraps a big.Rat to enable custom JSON marshaling and unmarshaling. 12 type Rat struct { 13 *big.Rat 14 } 15 16 // UnmarshalJSON implements the json.Unmarshaler interface for Rat. 17 func (r *Rat) UnmarshalJSON(data []byte) error { 18 var tmp interface{} 19 if err := json.Unmarshal(data, &tmp); err != nil { 20 return err 21 } 22 23 converted, err := convertToBigRat(tmp) 24 if err != nil { 25 return err 26 } 27 28 r.Rat = converted 29 return nil 30 } 31 32 // MarshalJSON implements the json.Marshaler interface for Rat. 33 func (r *Rat) MarshalJSON() ([]byte, error) { 34 formattedValue := FormatRat(r) 35 if strings.Contains(formattedValue, "/") { 36 // Output as a JSON string if it still contains a fraction 37 return json.Marshal(formattedValue) 38 } 39 // Output as a JSON number 40 return []byte(formattedValue), nil 41 } 42 43 // convertToBigRat converts various types to big.Rat. 44 func convertToBigRat(data interface{}) (*big.Rat, error) { 45 var str string 46 switch v := data.(type) { 47 case float64, float32, int, int64, int32, int16, int8, uint, uint64, uint32, uint16, uint8: 48 str = fmt.Sprint(v) 49 case string: 50 str = v 51 default: 52 return nil, ErrUnsupportedTypeForRat 53 } 54 55 numRat := new(big.Rat) 56 if _, ok := numRat.SetString(str); !ok { 57 return nil, ErrFailedToConvertToRat 58 } 59 return numRat, nil 60 } 61 62 // NewRat creates a new Rat instance from a given value. 63 func NewRat(value interface{}) *Rat { 64 converted, err := convertToBigRat(value) 65 if err != nil { 66 return nil 67 } 68 return &Rat{converted} 69 } 70 71 // FormatRat formats a Rat as a string. 72 func FormatRat(r *Rat) string { 73 if r == nil { 74 return "null" 75 } 76 77 // Check if the Rat is an integer 78 if r.IsInt() { 79 return r.Num().String() // Output as a plain integer string 80 } 81 82 // Format as a decimal maintaining precision 83 dec := r.FloatString(10) // You might adjust precision as needed 84 85 // Trim unnecessary trailing zeros and decimal point if no fractional part 86 trimmedDec := strings.TrimRight(dec, "0") 87 trimmedDec = strings.TrimRight(trimmedDec, ".") 88 89 if trimmedDec == "" { 90 return "0" // correct trimming edge case of "0.0000" 91 } 92 93 return trimmedDec 94 }