Home > Enterprise >  keep precision when unmarshling yaml
keep precision when unmarshling yaml

Time:11-17

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]]
  •  Tags:  
  • go
  • Related