I got this code:
package main
import (
"fmt"
"gopkg.in/yaml.v2"
)
func main() {
kkk := "common:\n vartest1: 1.2000\n vartest2: 1.22233"
tmp := map[string]interface{}{}
yaml.Unmarshal([]byte(kkk), tmp)
fmt.Println(tmp)
}
What I expect is the precision of float keeps the same with the string. But I got this output:
map[common:map[vartest1:1.2 vartest2:1.22233]]
I tried this:
package main
import (
"fmt"
"gopkg.in/yaml.v2"
)
type Myfloat64 float64
func (e *Myfloat64) UnmarshalYAML(unmarshal func(interface{}) error) error {
var test Myfloat64
err := unmarshal(&test)
if err != nil {
return err
}
fmt.Println(test)
*e = test
return nil
}
func main() {
kkk := "common:\n vartest1: 1.2000\n vartest2: 1.22233"
tmp := map[string]interface{}{}
yaml.Unmarshal([]byte(kkk), tmp)
fmt.Println(tmp)
}
But the UnmarshalYAML
hasn't been called as the type Myfloat64 is not in map[string]interface{}{}
. I have to ensure map[string]interface{}{}
unchanged, because I can not have a fixed struct defining that unmarshaled structure.
Is there any way to keep the precision? The input must be "common:\n vartest1: 1.2000\n vartest2: 1.22233". Updating 1.2000 to string is not allowed.
CodePudding user response:
Is there any way to keep the precision?
Don't parse the string as float, instead keep the raw value, i.e. keep the string
.
Here's an example:
- note #1: the code will need to be extended for it to be able to handle other types.
- note #2: this is using
yaml.v3
type Any struct {
Val any
}
func (a Any) String() string {
return fmt.Sprint(a.Val)
}
func (a *Any) UnmarshalYAML(n *yaml.Node) error {
switch n.Kind {
case yaml.MappingNode:
m := map[string]Any{}
if err := n.Decode(&m); err != nil {
return err
}
a.Val = m
case yaml.ScalarNode:
switch n.Tag {
case "!!float":
// Don't parse the raw string value,
// use it as is if you want to retain
// its formatting.
a.Val = n.Value
}
}
return nil
}
Try it on playground.
# output:
map[common:map[vartest1:1.2000 vartest2:1.22233]]