英文:
Go How to deal with float infinity before converting to JSON
问题
我遇到了一个情况,其中我有一些可能是无穷大/NaN的float64字段,尝试将其编组为JSON会导致错误,错误信息是不支持+Inf类型。
这个结构体最初是通过另一个库(Google Firestore)进行填充的。
实际上,这个结构体要大得多,有很多其他的浮点字段。
我认为我可以使用下面的循环来使用反射找到它们,不过我想知道是否有更简洁或更符合惯例的方法。
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) {
// ... 我将在这里放置一些逻辑
}
}
我不太理解如何实现自定义编组,也许这可以是处理+Inf的一种选项?
英文:
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?
答案1
得分: 2
自定义处理值可以通过实现Marshaler
接口的自定义类型来完成。不过,你的Something
类型是有问题的。它被定义为type Something interface{}
,而实际上应该是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) {
// 处理无穷大,将期望的值赋给v
// 或者使用+/-表示无穷大
s := "+"
if math.IsInf(v, -1) {
s = "-"
}
return []byte(s), nil
}
return json.Marshal(v) // 将结果作为标准的float64进行编组
}
func (j *JSONFloat) UnmarshalJSON(v []byte) error {
if s := string(v); s == "+" || s == "-" {
// 如果+/-表示无穷大
if s == "+" {
*j = JSONFloat(math.Inf(1))
return nil
}
*j = JSONFloat(math.Inf(-1))
return nil
}
// 只是一个普通的浮点数值
var fv float64
if err := json.Unmarshal(v, &fv); err != nil {
return err
}
*j = JSONFloat(fv)
return nil
}
这样应该可以了。
英文:
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
答案2
得分: 0
我创建了xhhuango/json来支持NaN、+Inf和-Inf。
type T struct {
N float64
IP float64
IN float64
}
func TestMarshalNaNAndInf(t *testing.T) {
s := T{
N: math.NaN(),
IP: math.Inf(1),
IN: math.Inf(-1),
}
got, err := Marshal(s)
if err != nil {
t.Errorf("Marshal() error: %v", err)
}
want := `{"N":NaN,"IP":+Inf,"IN":-Inf}`
if string(got) != want {
t.Errorf("Marshal() = %s, want %s", got, want)
}
}
func TestUnmarshalNaNAndInf(t *testing.T) {
data := []byte(`{"N":NaN,"IP":+Inf,"IN":-Inf}`)
var s T
err := Unmarshal(data, &s)
if err != nil {
t.Fatalf("Unmarshal: %v", err)
}
if !math.IsNaN(s.N) || !math.IsInf(s.IP, 1) || !math.IsInf(s.IN, -1) {
t.Fatalf("after Unmarshal, s.N=%f, s.IP=%f, s.IN=%f, want NaN, +Inf, -Inf", s.N, s.IP, s.IN)
}
}
英文:
I created xhhuango/json to support NaN, +Inf, and -Inf.
type T struct {
N float64
IP float64
IN float64
}
func TestMarshalNaNAndInf(t *testing.T) {
s := T{
N: math.NaN(),
IP: math.Inf(1),
IN: math.Inf(-1),
}
got, err := Marshal(s)
if err != nil {
t.Errorf("Marshal() error: %v", err)
}
want := `{"N":NaN,"IP":+Inf,"IN":-Inf}`
if string(got) != want {
t.Errorf("Marshal() = %s, want %s", got, want)
}
}
func TestUnmarshalNaNAndInf(t *testing.T) {
data := []byte(`{"N":NaN,"IP":+Inf,"IN":-Inf}`)
var s T
err := Unmarshal(data, &s)
if err != nil {
t.Fatalf("Unmarshal: %v", err)
}
if !math.IsNaN(s.N) || !math.IsInf(s.IP, 1) || !math.IsInf(s.IN, -1) {
t.Fatalf("after Unmarshal, s.N=%f, s.IP=%f, s.IN=%f, want NaN, +Inf, -Inf", s.N, s.IP, s.IN)
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论