Reputation: 1610
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}]
Upvotes: 0
Views: 771
Reputation: 1238
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:
error
return type was missingstring(data)
gives "123"
so the quotes need to be removed before ParseFloat
return nil
is required after string = ""
check so it dosent go furtherUpvotes: 1