英文:
Go - Custom unmarshaler - string to float
问题
我消费一个第三方API,它以字符串形式给出价格,有三种类型:
//输入
[
{"id": "A", "price": "123"},
{"id": "B", "price": "123.5"},
{"id": "C", "price": ""}
]
//期望输出
[
A 123,
B 123.5,
C 0
]
我知道我可以在我的结构体中添加,string
部分,但这不能处理订单C。我在这个 GitHub问题中找到了一个关于int的问题。在那里有一个playground代码,我稍微修改了一下以适应我的需求。
如果我将其更改为float64而不是int,那么现在订单A的日志将为0。
json: cannot unmarshal string into Go struct field Order.price of type float64
[{A 0}]
我认为代码中的if string(data) == '""' {
部分将检查空字符串(订单C的情况),如果是空字符串,则设置为0。但是"123"
不等于""
。我理解自定义解组器的其余部分,除了下面的部分。有人可以指点我正确的方向吗?
p := (*float64)(foe)
*p = f
完整代码也在这里
package main
import (
"encoding/json"
"fmt"
"strings"
)
type StringToFloat float64
type Order struct {
Id string `json:"id"`
Price StringToFloat `json:"price"`
}
func (foe *StringToFloat) UnmarshalJSON(data []byte) error {
if string(data) == `""` {
if foe != nil {
*foe = 0
}
return nil
}
var f float64
err := json.Unmarshal(data, &f)
if err != nil {
return err
}
p := (*float64)(foe)
*p = f
return nil
}
func main() {
jsonString := `[
{"id": "A", "price": "123"},
{"id": "B", "price": "123.5"},
{"id": "C", "price": ""}
]`
var orders []Order
if err := json.NewDecoder(strings.NewReader(jsonString)).Decode(&orders); err != nil {
fmt.Println(err)
}
fmt.Println(orders)
}
编辑:评论@mkopriva
我调整了解组器:
func (foe *StringToFloat) UnmarshalJSON(data []byte) {
fmt.Println(string(data))
if string(data) == `""` {
if foe != nil {
*foe = 0
}
}
n, err := strconv.ParseFloat(string(data), 64)
if err != nil {
fmt.Println(err)
}
*foe = StringToFloat(n)
}
这给我输出:
json: cannot unmarshal string into Go struct field Order.price of type main.StringToFloat
[{A 0} {B 0} {C 0}]
```
<details>
<summary>英文:</summary>
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
]
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's. In there there is a [playground](https://go.dev/play/p/nWl2toE2RAE) code that i altered a little bit to my needs.
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}]
I thought that this `if string(data) == '""' {` part of the code will check for the empty string (case order C) if so: set to 0. But `"123"` is not `""`. 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
**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:"id"
Price StringToFloat json:"price"
}
func (foe *StringToFloat) UnmarshalJSON(data []byte) error {
if string(data) == ""
{
if foe != nil {
*foe = 0
}
return nil
}
var f float64
err := json.Unmarshal(data, &f)
if err != nil {
return err
}
p := (*float64)(foe)
*p = f
return nil
}
func main() {
jsonString := [
{"id": "A", "price": "123"},
{"id": "B", "price": "123.5"},
{"id": "C", "price": ""}
]
var orders []Order
if err := json.NewDecoder(strings.NewReader(jsonString)).Decode(&orders); err != nil {
fmt.Println(err)
}
fmt.Println(orders)
}
EDIT: comment @mkopriva
I adjusted the unmarshaler:
func (foe *StringToFloat) UnmarshalJSON(data []byte) {
fmt.Println(string(data))
if string(data) == ""
{
if foe != nil {
*foe = 0
}
}
n, err := strconv.ParseFloat(string(data), 64)
if err != nil {
fmt.Println(err)
}
*foe = StringToFloat(n)
}
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}]
</details>
# 答案1
**得分**: 1
请稍等,我会为您翻译这段代码。
<details>
<summary>英文:</summary>
Try this
```go
package main
import (
"encoding/json"
"fmt"
"strconv"
"strings"
)
type StringToFloat float64
type Order struct {
Id string `json:"id"`
Price StringToFloat `json:"price"`
}
func (foe *StringToFloat) UnmarshalJSON(data []byte) error {
if string(data) == "\"\"" {
if foe != nil {
*foe = 0
}
return nil
}
num := strings.ReplaceAll(string(data), "\"", "")
n, err := strconv.ParseFloat(num, 64)
if err != nil {
return err
}
*foe = StringToFloat(n)
return nil
}
func main() {
jsonString := `[
{"id": "A", "price": "123"},
{"id": "B", "price": "123.5"},
{"id": "C", "price": ""}
]`
var orders []Order
if err := json.NewDecoder(strings.NewReader(jsonString)).Decode(&orders); err != nil {
fmt.Println(err)
}
fmt.Println(orders)
}
```
Go playground: https://go.dev/play/p/cHjNQ447eX9
Things to note:
- 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
- `string(data)` gives `"123"` so the quotes need to be removed before `ParseFloat`
- `return nil` is required after string = `""` check so it dosent go further
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论