在转换为JSON之前,如何处理浮点无穷大的问题?

huangapple go评论124阅读模式
英文:

Go How to deal with float infinity before converting to JSON

问题

我遇到了一个情况,其中我有一些可能是无穷大/NaN的float64字段,尝试将其编组为JSON会导致错误,错误信息是不支持+Inf类型。

这个结构体最初是通过另一个库(Google Firestore)进行填充的。

实际上,这个结构体要大得多,有很多其他的浮点字段。

我认为我可以使用下面的循环来使用反射找到它们,不过我想知道是否有更简洁或更符合惯例的方法。

  1. v := reflect.ValueOf(structVar)
  2. typeOfS := v.Type()
  3. for i := 0; i < v.NumField(); i++ {
  4. if typeOfS.Field(i).Type.Kind() == reflect.Float64 && math.IsInf(v.Field(i).Interface().(float64), 1) {
  5. // ... 我将在这里放置一些逻辑
  6. }
  7. }

我不太理解如何实现自定义编组,也许这可以是处理+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.

  1. type Something interface {
  2. Id string `firestore:&quot;id&quot;`
  3. NumberA float64 `firestore:&quot;numberA&quot;`
  4. NumberB float64 `firestore:&quot;numberB&quot;`
  5. NumberC float64 `firestore:&quot;numberC&quot;`
  6. }

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.

  1. v := reflect.ValueOf(structVar)
  2. typeOfS := v.Type()
  3. for i := 0; i&lt; v.NumField(); i++ {
  4. if typeOfS.Field(i).Type.Kind() == reflect.Float64 &amp;&amp; math.IsInf(v.Field(i).Interface().(float64), 1) {
  5. // ... some logic I&#39;ll put here
  6. }
  7. }

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

  1. type Something struct {
  2. Id string `firestore:"id"`
  3. NumberA JSONFloat `firestore:"numberA"`
  4. NumberB JSONFloat `firestore:"numberB"`
  5. NumberC JSONFloat `firestore:"numberC"`
  6. }
  7. type JSONFloat float64
  8. func (j JSONFloat) MarshalJSON() ([]byte, error) {
  9. v := float64(j)
  10. if math.IsInf(j, 0) {
  11. // 处理无穷大,将期望的值赋给v
  12. // 或者使用+/-表示无穷大
  13. s := "+"
  14. if math.IsInf(v, -1) {
  15. s = "-"
  16. }
  17. return []byte(s), nil
  18. }
  19. return json.Marshal(v) // 将结果作为标准的float64进行编组
  20. }
  21. func (j *JSONFloat) UnmarshalJSON(v []byte) error {
  22. if s := string(v); s == "+" || s == "-" {
  23. // 如果+/-表示无穷大
  24. if s == "+" {
  25. *j = JSONFloat(math.Inf(1))
  26. return nil
  27. }
  28. *j = JSONFloat(math.Inf(-1))
  29. return nil
  30. }
  31. // 只是一个普通的浮点数值
  32. var fv float64
  33. if err := json.Unmarshal(v, &fv); err != nil {
  34. return err
  35. }
  36. *j = JSONFloat(fv)
  37. return nil
  38. }

这样应该可以了。

英文:

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:

  1. type Something struct {
  2. Id string `firestore:&quot;id&quot;`
  3. NumberA JSONFloat `firestore:&quot;numberA&quot;`
  4. NumberB JSONFloat `firestore:&quot;numberB&quot;`
  5. NumberC JSONFloat `firestore:&quot;numberC&quot;`
  6. }
  7. type JSONFloat float64
  8. func (j JSONFloat) MarshalJSON() ([]byte, error) {
  9. v := float64(j)
  10. if math.IsInf(j, 0) {
  11. // handle infinity, assign desired value to v
  12. // or say +/- indicates infinity
  13. s := &quot;+&quot;
  14. if math.IsInf(v, -1) {
  15. s = &quot;-&quot;
  16. }
  17. return []byte(s), nil
  18. }
  19. return json.Marshal(v) // marshal result as standard float64
  20. }
  21. func (j *JSONFloat) UnsmarshalJSON(v []byte) error {
  22. if s := string(v); s == &quot;+&quot; || s == &quot;-&quot; {
  23. // if +/- indiciates infinity
  24. if s == &quot;+&quot; {
  25. *j = JSONFloat(math.Inf(1))
  26. return nil
  27. }
  28. *j = JSONFloat(math.Inf(-1))
  29. return nil
  30. }
  31. // just a regular float value
  32. var fv float64
  33. if err := json.Unmarshal(v, &amp;fv); err != nil {\
  34. return err
  35. }
  36. *j = JSONFloat(fv)
  37. return nil
  38. }

That should do it

答案2

得分: 0

我创建了xhhuango/json来支持NaN、+Inf和-Inf。

  1. type T struct {
  2. N float64
  3. IP float64
  4. IN float64
  5. }
  6. func TestMarshalNaNAndInf(t *testing.T) {
  7. s := T{
  8. N: math.NaN(),
  9. IP: math.Inf(1),
  10. IN: math.Inf(-1),
  11. }
  12. got, err := Marshal(s)
  13. if err != nil {
  14. t.Errorf("Marshal() error: %v", err)
  15. }
  16. want := `{"N":NaN,"IP":+Inf,"IN":-Inf}`
  17. if string(got) != want {
  18. t.Errorf("Marshal() = %s, want %s", got, want)
  19. }
  20. }
  21. func TestUnmarshalNaNAndInf(t *testing.T) {
  22. data := []byte(`{"N":NaN,"IP":+Inf,"IN":-Inf}`)
  23. var s T
  24. err := Unmarshal(data, &s)
  25. if err != nil {
  26. t.Fatalf("Unmarshal: %v", err)
  27. }
  28. if !math.IsNaN(s.N) || !math.IsInf(s.IP, 1) || !math.IsInf(s.IN, -1) {
  29. t.Fatalf("after Unmarshal, s.N=%f, s.IP=%f, s.IN=%f, want NaN, +Inf, -Inf", s.N, s.IP, s.IN)
  30. }
  31. }
英文:

I created xhhuango/json to support NaN, +Inf, and -Inf.

  1. type T struct {
  2. N float64
  3. IP float64
  4. IN float64
  5. }
  6. func TestMarshalNaNAndInf(t *testing.T) {
  7. s := T{
  8. N: math.NaN(),
  9. IP: math.Inf(1),
  10. IN: math.Inf(-1),
  11. }
  12. got, err := Marshal(s)
  13. if err != nil {
  14. t.Errorf(&quot;Marshal() error: %v&quot;, err)
  15. }
  16. want := `{&quot;N&quot;:NaN,&quot;IP&quot;:+Inf,&quot;IN&quot;:-Inf}`
  17. if string(got) != want {
  18. t.Errorf(&quot;Marshal() = %s, want %s&quot;, got, want)
  19. }
  20. }
  21. func TestUnmarshalNaNAndInf(t *testing.T) {
  22. data := []byte(`{&quot;N&quot;:NaN,&quot;IP&quot;:+Inf,&quot;IN&quot;:-Inf}`)
  23. var s T
  24. err := Unmarshal(data, &amp;s)
  25. if err != nil {
  26. t.Fatalf(&quot;Unmarshal: %v&quot;, err)
  27. }
  28. if !math.IsNaN(s.N) || !math.IsInf(s.IP, 1) || !math.IsInf(s.IN, -1) {
  29. t.Fatalf(&quot;after Unmarshal, s.N=%f, s.IP=%f, s.IN=%f, want NaN, +Inf, -Inf&quot;, s.N, s.IP, s.IN)
  30. }
  31. }

huangapple
  • 本文由 发表于 2022年10月24日 17:48:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/74179310.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定