需要将JSON中的整数解析为整数,而不是浮点数 [英] Need to parse integers in JSON as integers, not floats

查看:232
本文介绍了需要将JSON中的整数解析为整数,而不是浮点数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先让我解释一下这个问题.

我有一个JSON记录流进入我的Golang应用程序.基本上,将它们转发到数据存储(InfluxDB). JSON中有一些整数值,还有一些浮点值.必须将它们与原始数据类型一起转发到数据存储.否则,将发生类型冲突,并且写入操作将失败.

Ruby JSON解析器可以轻松做到这一点:

require 'json'
obj = { "a" => 123, "b" => 12.3 }
parsed = JSON.parse(obj.to_json)

print parsed["a"].class # => Integer
print parsed["b"].class # => Float

Golang中的encoding/json软件包确实有一些麻烦(所有数字都解析为浮点数):

package main

import "encoding/json"
import "fmt"

func main () {
  str := "{\"a\":123,\"b\":12.3}"
  var parsed map[string]interface{}
  json.Unmarshal([]byte(str), &parsed)
  for key, val := range parsed {
    switch val.(type) {
    case int:
      fmt.Println("int type: ", key)
    case float64:
      fmt.Println("float type: ", key)
    default:
      fmt.Println("unknown type: ", key)
    }
  }
}

哪些印刷品:

float type:  a
float type:  b

我需要一种将int解析为int的方法,并像Ruby JSON解析器一样将float转换为float.

在这种情况下,将所有内容解析为字符串并检查是否有小数位是不可行的.某些值以字符串形式出现,例如"123",我需要将这些推送为字符串.

我没有解析对象的结构,也不是一个选项. golang应用程序实际上并不关心模式,而只是在接收到输入时转发该输入.


我尝试了此处概述的方法:其他方式int和float64 的reflect.Type的验证(使用reflect),但是无法准确识别int:

t := reflect.TypeOf(parsed["a"])
k := t.Kind()
k == reflect.Int // false
k == reflect.Float64 // true

解决方案

例如,使用通用Go机制自定义JSON值的Ruby JSON数字类型,

package main

import (
    "encoding/json"
    "fmt"
    "strconv"
)

func main() {
    str := `{"a":123,"b":12.3,"c":"123","d":"12.3","e":true}`
    var raw map[string]json.RawMessage
    err := json.Unmarshal([]byte(str), &raw)
    if err != nil {
        panic(err)
    }
    parsed := make(map[string]interface{}, len(raw))
    for key, val := range raw {
        s := string(val)
        i, err := strconv.ParseInt(s, 10, 64)
        if err == nil {
            parsed[key] = i
            continue
        }
        f, err := strconv.ParseFloat(s, 64)
        if err == nil {
            parsed[key] = f
            continue
        }
        var v interface{}
        err = json.Unmarshal(val, &v)
        if err == nil {
            parsed[key] = v
            continue
        }
        parsed[key] = val
    }
    for key, val := range parsed {
        fmt.Printf("%T: %v %v\n", val, key, val)
    }
}

游乐场: https://play.golang.org/p/VmG8IZV4CG_Y

输出:

int64: a 123 
float64: b 12.3 
string: c 123 
string: d 12.3 
bool: e true 


另一个示例,使用Go json.Number类型的Ruby JSON数字类型,

package main

import (
    "encoding/json"
    "fmt"
    "strings"
)

func main() {
    str := `{"a":123,"b":12.3,"c":"123","d":"12.3","e":true}`
    var parsed map[string]interface{}
    d := json.NewDecoder(strings.NewReader(str))
    d.UseNumber()
    err := d.Decode(&parsed)
    if err != nil {
        panic(err)
    }
    for key, val := range parsed {
        n, ok := val.(json.Number)
        if !ok {
            continue
        }
        if i, err := n.Int64(); err == nil {
            parsed[key] = i
            continue
        }
        if f, err := n.Float64(); err == nil {
            parsed[key] = f
            continue
        }
    }
    for key, val := range parsed {
        fmt.Printf("%T: %v %v\n", val, key, val)
    }
}

游乐场: https://play.golang.org/p/Hk_Wb0EM-aY

输出:

int64: a 123
float64: b 12.3
string: c 123
string: d 12.3
bool: e true

@ShudiptaSharma的建议的工作版本.

First of all let me explain the problem.

I have a stream of JSON records coming into my Golang app. It basically forwards these to a data store (InfluxDB). There are some integer values in the JSON, and also some float values. It is essential that these get forwarded to the data store with the original data type. If they don't, there will be type conflicts and the write operation will fail.

The Ruby JSON parser has no trouble doing this:

require 'json'
obj = { "a" => 123, "b" => 12.3 }
parsed = JSON.parse(obj.to_json)

print parsed["a"].class # => Integer
print parsed["b"].class # => Float

The encoding/json package in Golang, does have some trouble (all numbers are parsed as floats):

package main

import "encoding/json"
import "fmt"

func main () {
  str := "{\"a\":123,\"b\":12.3}"
  var parsed map[string]interface{}
  json.Unmarshal([]byte(str), &parsed)
  for key, val := range parsed {
    switch val.(type) {
    case int:
      fmt.Println("int type: ", key)
    case float64:
      fmt.Println("float type: ", key)
    default:
      fmt.Println("unknown type: ", key)
    }
  }
}

Which prints:

float type:  a
float type:  b

I need a way to parse ints as ints, and floats as floats, in the way the Ruby JSON parser does.

It is not feasible in this case to parse everything as strings and check whether or not there is a decimal. Certain values come in as strings such as "123" and I need to push those as strings.

I do not have structs for the parsed objects, nor is that an option. The golang app doesn't actually care about the schema and just forwards the input as it receives it.


I tried the approach outlined here: Other ways of verifying reflect.Type for int and float64 (using reflect) but it did not accurately identify the int:

t := reflect.TypeOf(parsed["a"])
k := t.Kind()
k == reflect.Int // false
k == reflect.Float64 // true

解决方案

For example, Ruby JSON number types using the general Go mechanism for custom JSON values,

package main

import (
    "encoding/json"
    "fmt"
    "strconv"
)

func main() {
    str := `{"a":123,"b":12.3,"c":"123","d":"12.3","e":true}`
    var raw map[string]json.RawMessage
    err := json.Unmarshal([]byte(str), &raw)
    if err != nil {
        panic(err)
    }
    parsed := make(map[string]interface{}, len(raw))
    for key, val := range raw {
        s := string(val)
        i, err := strconv.ParseInt(s, 10, 64)
        if err == nil {
            parsed[key] = i
            continue
        }
        f, err := strconv.ParseFloat(s, 64)
        if err == nil {
            parsed[key] = f
            continue
        }
        var v interface{}
        err = json.Unmarshal(val, &v)
        if err == nil {
            parsed[key] = v
            continue
        }
        parsed[key] = val
    }
    for key, val := range parsed {
        fmt.Printf("%T: %v %v\n", val, key, val)
    }
}

Playground: https://play.golang.org/p/VmG8IZV4CG_Y

Output:

int64: a 123 
float64: b 12.3 
string: c 123 
string: d 12.3 
bool: e true 


Another example, Ruby JSON number types using the Go json.Number type,

package main

import (
    "encoding/json"
    "fmt"
    "strings"
)

func main() {
    str := `{"a":123,"b":12.3,"c":"123","d":"12.3","e":true}`
    var parsed map[string]interface{}
    d := json.NewDecoder(strings.NewReader(str))
    d.UseNumber()
    err := d.Decode(&parsed)
    if err != nil {
        panic(err)
    }
    for key, val := range parsed {
        n, ok := val.(json.Number)
        if !ok {
            continue
        }
        if i, err := n.Int64(); err == nil {
            parsed[key] = i
            continue
        }
        if f, err := n.Float64(); err == nil {
            parsed[key] = f
            continue
        }
    }
    for key, val := range parsed {
        fmt.Printf("%T: %v %v\n", val, key, val)
    }
}

Playground: https://play.golang.org/p/Hk_Wb0EM-aY

Output:

int64: a 123
float64: b 12.3
string: c 123
string: d 12.3
bool: e true

A working version of @ShudiptaSharma's suggestion.

这篇关于需要将JSON中的整数解析为整数,而不是浮点数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆