在反序列化 YAML 时保持精度。

huangapple go评论71阅读模式
英文:

keep precision when unmarshling yaml

问题

我得到了这段代码:

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)
}

我期望的是浮点数的精度与字符串保持一致。但是我得到了以下输出:

map[common:map[vartest1:1.2 vartest2:1.22233]]

我尝试了以下代码:

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)
}

但是UnmarshalYAML没有被调用,因为类型Myfloat64不在map[string]interface{}{}中。我必须确保map[string]interface{}{}保持不变,因为我不能有一个固定的结构来定义那个解组结构。

有没有办法保持精度?输入必须是"common:\n vartest1: 1.2000\n vartest2: 1.22233"。不允许将1.2000更新为字符串。

英文:

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.

答案1

得分: 1

有没有办法保持精度?

不要将字符串解析为浮点数,而是保留原始值,即保留字符串。

这是一个示例:

  • 注意1:代码需要扩展以处理其他类型。
  • 注意2:这里使用的是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":
			// 不要解析原始字符串值,
			// 如果要保留其格式,则直接使用它。
			a.Val = n.Value
		}
	}
	return nil
}

playground上尝试一下。

# 输出:
map[common:map[vartest1:1.2000 vartest2:1.22233]]
英文:

> 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]]

huangapple
  • 本文由 发表于 2022年11月16日 16:39:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/74457421.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定