I've come across a situation where I have some float64 fields that could be infinity/NaN and trying to marshal to JSON would result in an error regarding Inf type isn't supported.
type Something interface {
Id string `firestore:"id"`
NumberA float64 `firestore:"numberA"`
NumberB float64 `firestore:"numberB"`
NumberC float64 `firestore:"numberC"`
}
This struct gets initially populated via another library (Google Firestore).
In reality this struct is much larger with a lot more fields that are floats.
I think I could use something like this loop below using reflect to to find them all, though I wonder if there is a cleaner way or more idiomatic approach.
v := reflect.ValueOf(structVar)
typeOfS := v.Type()
for i := 0; i< v.NumField(); i {
if typeOfS.Field(i).Type.Kind() == reflect.Float64 && math.IsInf(v.Field(i).Interface().(float64), 1) {
// ... some logic I'll put here
}
}
I don't understand how to implement custom marshalling so maybe that could be an option to handle Inf?
CodePudding user response:
Custom handling of values can be done through custom types that implement the Marshaler
interface. Your Something
type, though, is malformed. It's defined as type Something interface{}
, whereas that really ought the be a type Something struct
:
type Something struct {
Id string `firestore:"id"`
NumberA JSONFloat `firestore:"numberA"`
NumberB JSONFloat `firestore:"numberB"`
NumberC JSONFloat `firestore:"numberC"`
}
type JSONFloat float64
func (j JSONFloat) MarshalJSON() ([]byte, error) {
v := float64(j)
if math.IsInf(j, 0) {
// handle infinity, assign desired value to v
// or say /- indicates infinity
s := " "
if math.IsInf(v, -1) {
s = "-"
}
return []byte(s), nil
}
return json.Marshal(v) // marshal result as standard float64
}
func (j *JSONFloat) UnsmarshalJSON(v []byte) error {
if s := string(v); s == " " || s == "-" {
// if /- indiciates infinity
if s == " " {
*j = JSONFloat(math.Inf(1))
return nil
}
*j = JSONFloat(math.Inf(-1))
return nil
}
// just a regular float value
var fv float64
if err := json.Unmarshal(v, &fv); err != nil {\
return err
}
*j = JSONFloat(fv)
return nil
}
That should do it