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 github issue where this question is asked about int's. In there there is a playground 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
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}]
CodePudding user response:
Try this
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 beforeParseFloat
return nil
is required after string =""
check so it dosent go further