介绍一下Json的Number

Bryce · · 4689 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

Json的使用基本没有什么难度,就拿Golang来说,直接来个encoding/json包里的func Marshal(v interface{}) ([]byte, error)func Unmarshal(data []byte, v interface{}) error就能对Json进行编解码了。具体的文件就是采用反射的方法,可以参考我之前的文章『Golang通过反射实现结构体转成JSON数据』

现在问题来了,如下的map需要大家是如何解析的?

{"10000000000":10000000000,"111":1}

如果直接定义一个map来解析,定义成map[string]int64,我们是肯定可以解析成功的,解析的时候会将数据转换为我们需要的数据类型。那么问题来了:如果把类型定义成map[string]interface{}会是如何解析的呢?

我一直是用显示定义的来解析,也就是map[string]int64,当我用map[string]interface{}解析的时候,我就想当然的认为,interface{}里面存的是int64的数据。后来调试了一通,最后才发现是本末倒置了。

Json数据其实就是一个字符串,里面按照一定的格式保存我们的数据。Json支持的数据类型与Golang语言的关系如下:

bool, for JSON booleans
float64, for JSON numbers
string, for JSON strings
[]interface{}, for JSON arrays
map[string]interface{}, for JSON objects
nil for JSON null

我们可以注意到,Json格式的数字和Golang语言里面的float64是相关联的。也就是说,默认情况下数字类型将会转换成float64类型。如果我们显示的指出了数字类型,比如int64,他会将数字再转成int64

我们看一下源码,encoding/json/decode.go func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool)

func (d *decodeState) literalStore(item []byte, v reflect.Value, fromQuoted bool) {
	...
	switch c := item[0]; c {
		case 'n': // null
		...
		case 't', 'f': // true, false
		...
		case '"': // string
		...
		default: // number
		if c != '-' && (c < '0' || c > '9') {
			if fromQuoted {
				d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
			} else {
				d.error(errPhase)
			}
		}
		s := string(item)
		switch v.Kind() {
		default:
			if v.Kind() == reflect.String && v.Type() == numberType {
				v.SetString(s)
				break
			}
			if fromQuoted {
				d.error(fmt.Errorf("json: invalid use of ,string struct tag, trying to unmarshal %q into %v", item, v.Type()))
			} else {
				d.error(&UnmarshalTypeError{"number", v.Type(), int64(d.off)})
			}
		case reflect.Interface:
			n, err := d.convertNumber(s)
			if err != nil {
				d.saveError(err)
				break
			}
			if v.NumMethod() != 0 {
				d.saveError(&UnmarshalTypeError{"number", v.Type(), int64(d.off)})
				break
			}
			v.Set(reflect.ValueOf(n))

		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			n, err := strconv.ParseInt(s, 10, 64)
			if err != nil || v.OverflowInt(n) {
				d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)})
				break
			}
			v.SetInt(n)

		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
			n, err := strconv.ParseUint(s, 10, 64)
			if err != nil || v.OverflowUint(n) {
				d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)})
				break
			}
			v.SetUint(n)

		case reflect.Float32, reflect.Float64:
			n, err := strconv.ParseFloat(s, v.Type().Bits())
			if err != nil || v.OverflowFloat(n) {
				d.saveError(&UnmarshalTypeError{"number " + s, v.Type(), int64(d.off)})
				break
			}
			v.SetFloat(n)
		}
	}
}

func (d *decodeState) convertNumber(s string) (interface{}, error) {
	if d.useNumber {
		return Number(s), nil
	}
	f, err := strconv.ParseFloat(s, 64)
	if err != nil {
		return nil, &UnmarshalTypeError{"number " + s, reflect.TypeOf(0.0), int64(d.off)}
	}
	return f, nil
}

可以看出来,Json解析实现的时候通过反射来判断要生成的具体的类型。如果是interface{}类型,通过converNumber方法转成float64(里面是通过strconv.ParseFloat实现),如果类型是整形相关,通过strconv.ParseInt方法转换。无符号整形是通过strconv.ParseUint实现。



原文链接:介绍一下Json的Number,转载请注明来源!


有疑问加站长微信联系(非本文作者)

本文来自:Cyeam

感谢作者:Bryce

查看原文:介绍一下Json的Number

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

4689 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传