Go – 自定义解组器 – 字符串到浮点数

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

Go - Custom unmarshaler - string to float

问题

我消费一个第三方API,它以字符串形式给出价格,有三种类型:

  1. //输入
  2. [
  3. {"id": "A", "price": "123"},
  4. {"id": "B", "price": "123.5"},
  5. {"id": "C", "price": ""}
  6. ]
  7. //期望输出
  8. [
  9. A 123,
  10. B 123.5,
  11. C 0
  12. ]

我知道我可以在我的结构体中添加,string部分,但这不能处理订单C。我在这个 GitHub问题中找到了一个关于int的问题。在那里有一个playground代码,我稍微修改了一下以适应我的需求。

如果我将其更改为float64而不是int,那么现在订单A的日志将为0。

  1. json: cannot unmarshal string into Go struct field Order.price of type float64
  2. [{A 0}]

我认为代码中的if string(data) == '""' {部分将检查空字符串(订单C的情况),如果是空字符串,则设置为0。但是"123"不等于""。我理解自定义解组器的其余部分,除了下面的部分。有人可以指点我正确的方向吗?

  1. p := (*float64)(foe)
  2. *p = f

完整代码也在这里

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "strings"
  6. )
  7. type StringToFloat float64
  8. type Order struct {
  9. Id string `json:"id"`
  10. Price StringToFloat `json:"price"`
  11. }
  12. func (foe *StringToFloat) UnmarshalJSON(data []byte) error {
  13. if string(data) == `""` {
  14. if foe != nil {
  15. *foe = 0
  16. }
  17. return nil
  18. }
  19. var f float64
  20. err := json.Unmarshal(data, &f)
  21. if err != nil {
  22. return err
  23. }
  24. p := (*float64)(foe)
  25. *p = f
  26. return nil
  27. }
  28. func main() {
  29. jsonString := `[
  30. {"id": "A", "price": "123"},
  31. {"id": "B", "price": "123.5"},
  32. {"id": "C", "price": ""}
  33. ]`
  34. var orders []Order
  35. if err := json.NewDecoder(strings.NewReader(jsonString)).Decode(&orders); err != nil {
  36. fmt.Println(err)
  37. }
  38. fmt.Println(orders)
  39. }

编辑:评论@mkopriva

我调整了解组器:

  1. func (foe *StringToFloat) UnmarshalJSON(data []byte) {
  2. fmt.Println(string(data))
  3. if string(data) == `""` {
  4. if foe != nil {
  5. *foe = 0
  6. }
  7. }
  8. n, err := strconv.ParseFloat(string(data), 64)
  9. if err != nil {
  10. fmt.Println(err)
  11. }
  12. *foe = StringToFloat(n)
  13. }

这给我输出:

  1. json: cannot unmarshal string into Go struct field Order.price of type main.StringToFloat
  2. [{A 0} {B 0} {C 0}]
  3. ```
  4. <details>
  5. <summary>英文:</summary>
  6. I consume a 3th party API that gives me the price as a string, there are 3 flavours:

//Input
[
{"id": "A", "price": "123"},
{"id": "B", "price": "123.5"},
{"id": "C", "price": ""}
]

//Expected output

[
A 123,
B 123.5,
C 0
]

  1. I know i can add the `,string` part inside my struct but this will not handle order C. I have found [this](https://github.com/golang/go/issues/26800) github issue where this question is asked about int&#39;s. In there there is a [playground](https://go.dev/play/p/nWl2toE2RAE) code that i altered a little bit to my needs.
  2. If i change this to float64 instead of int i get an error with the log of order A that is now 0.

json: cannot unmarshal string into Go struct field Order.price of type float64
[{A 0}]

  1. I thought that this `if string(data) == &#39;&quot;&quot;&#39; {` part of the code will check for the empty string (case order C) if so: set to 0. But `&quot;123&quot;` is not `&quot;&quot;`. The rest of the custom unmarshaler i understand apart from the part below. Can someone point me to the right direction?

p := (*float64)(foe)
*p = f

  1. **The full code** [Also here](https://go.dev/play/p/-_BArOztsOj)

package main

import (
"encoding/json"
"fmt"
"strings"
)

type StringToFloat float64

type Order struct {
Id string json:&quot;id&quot;
Price StringToFloat json:&quot;price&quot;
}

func (foe *StringToFloat) UnmarshalJSON(data []byte) error {
if string(data) == &quot;&quot; {
if foe != nil {
*foe = 0
}
return nil
}

  1. var f float64
  2. err := json.Unmarshal(data, &amp;f)
  3. if err != nil {
  4. return err
  5. }
  6. p := (*float64)(foe)
  7. *p = f
  8. return nil

}

func main() {
jsonString := [
{&quot;id&quot;: &quot;A&quot;, &quot;price&quot;: &quot;123&quot;},
{&quot;id&quot;: &quot;B&quot;, &quot;price&quot;: &quot;123.5&quot;},
{&quot;id&quot;: &quot;C&quot;, &quot;price&quot;: &quot;&quot;}
]

var orders []Order
if err := json.NewDecoder(strings.NewReader(jsonString)).Decode(&orders); err != nil {
fmt.Println(err)
}

  1. fmt.Println(orders)

}

  1. EDIT: comment @mkopriva
  2. I adjusted the unmarshaler:

func (foe *StringToFloat) UnmarshalJSON(data []byte) {
fmt.Println(string(data))
if string(data) == &quot;&quot; {
if foe != nil {
*foe = 0
}
}

  1. n, err := strconv.ParseFloat(string(data), 64)
  2. if err != nil {
  3. fmt.Println(err)
  4. }
  5. *foe = StringToFloat(n)

}

  1. What gives me this output:

json: cannot unmarshal string into Go struct field Order.price of type main.StringToFloat
[{A 0} {B 0} {C 0}]

  1. </details>
  2. # 答案1
  3. **得分**: 1
  4. 请稍等,我会为您翻译这段代码。
  5. <details>
  6. <summary>英文:</summary>
  7. Try this
  8. ```go
  9. package main
  10. import (
  11. &quot;encoding/json&quot;
  12. &quot;fmt&quot;
  13. &quot;strconv&quot;
  14. &quot;strings&quot;
  15. )
  16. type StringToFloat float64
  17. type Order struct {
  18. Id string `json:&quot;id&quot;`
  19. Price StringToFloat `json:&quot;price&quot;`
  20. }
  21. func (foe *StringToFloat) UnmarshalJSON(data []byte) error {
  22. if string(data) == &quot;\&quot;\&quot;&quot; {
  23. if foe != nil {
  24. *foe = 0
  25. }
  26. return nil
  27. }
  28. num := strings.ReplaceAll(string(data), &quot;\&quot;&quot;, &quot;&quot;)
  29. n, err := strconv.ParseFloat(num, 64)
  30. if err != nil {
  31. return err
  32. }
  33. *foe = StringToFloat(n)
  34. return nil
  35. }
  36. func main() {
  37. jsonString := `[
  38. {&quot;id&quot;: &quot;A&quot;, &quot;price&quot;: &quot;123&quot;},
  39. {&quot;id&quot;: &quot;B&quot;, &quot;price&quot;: &quot;123.5&quot;},
  40. {&quot;id&quot;: &quot;C&quot;, &quot;price&quot;: &quot;&quot;}
  41. ]`
  42. var orders []Order
  43. if err := json.NewDecoder(strings.NewReader(jsonString)).Decode(&amp;orders); err != nil {
  44. fmt.Println(err)
  45. }
  46. fmt.Println(orders)
  47. }
  48. ```
  49. Go playground: https://go.dev/play/p/cHjNQ447eX9
  50. Things to note:
  51. - The function signature of UmarshalJSON func needs to be exactly the same as given to be picked up by Decode. In your updated code `error` return type was missing
  52. - `string(data)` gives `&quot;123&quot;` so the quotes need to be removed before `ParseFloat`
  53. - `return nil` is required after string = `&quot;&quot;` check so it dosent go further
  54. </details>

huangapple
  • 本文由 发表于 2022年9月2日 02:00:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/73573695.html
匿名

发表评论

匿名网友

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

确定