我正在使用Golang和JSON尝试使用从Graphite API中提取的数据进行一些计算。
为简单起见,Graphite发送的数据片段为:
[
{
"target": "server1.loadavg.1min",
"datapoints": [
[
0.16,
1422770850
],
[
0.16,
1422770880
],
[
null,
1422771120
]
]
},
{
"target": "server2.loadavg.1min",
"datapoints": [
[
0.19,
1422770850
],
[
null,
1422771390
],
[
0.14,
1422771420
]
]
}
]
我一直在阅读the go json tutorial关于如何使用通用接口{}获取JSON任意数据,但我正在努力解决该过程的某些方面。
我试图定义一个结构来保存这些数据,读取文件内容并将其解组到这个结构中:
type Graphite struct {
Metric struct {
Target string `json:"target"`
Datapoints [][]float64 `json:"datapoints"`
}
}
var results []Graphite
err = json.Unmarshal(d, &r)
if err != nil {
panic(err)
}
fmt.Printf("%v\n", r)
但结果是:
[{{ []}} {{ []}}]
我当然可以使用通用接口{}但我想知道我在这里缺少什么。
你能帮我吗?
谢谢!
答案 0 :(得分:5)
我喜欢从最简单的类型开始,然后继续努力。首先,您需要表示您的数据点。
type DataPoint []float64
然后,指标只是一个目标和一系列数据点。
type Metric struct {
Target string `json:"target"`
Points []DataPoint `json:"datapoints"`
}
您的Graphite
结构不需要。您的JSON只是Metric
s的JSON数组。
var results []Metric
err := json.Unmarshal([]byte(data), &results)
这里有一个playground链接,上面有一个完整的例子。
答案 1 :(得分:0)
上述答案的问题是它将零点转换为值为0的点,这是不正确的。 Null表示“未知”。有些人建议使用浮点指针,因此nil指针意味着“没有价值”,但这有很大的开销(例如大多数64位平台上的8字节,更不用说内存解除引用开销了。)
最好使用golang的数学NaN支持来标记空值,这不需要额外的数据,因为它内置于float表示中。 您可以使用自定义Unmarshal函数执行此操作,如下所示:
type Metric struct {
Target string
Datapoints []Point
}
type Point struct {
Val float64
Ts uint32
}
var errInvalidFormat = errors.New("invalid format")
func (p *Point) UnmarshalJSON(data []byte) error {
if len(data) < 2 {
return errInvalidFormat
}
// find first digit or 'n' for "null"
for (data[0] < 48 || data[0] > 57) && data[0] != 110 {
if len(data) == 1 {
return errInvalidFormat
}
data = data[1:]
}
// find comma
var i int
for i = 0; i < len(data); i++ {
if data[i] == 44 {
break
}
}
if i == 0 {
return errInvalidFormat
}
if bytes.HasPrefix(data[:i], []byte("null")) {
p.Val = math.NaN()
} else {
fl, err := strconv.ParseFloat(string(data[:i]), 64)
if err != nil {
return err
}
p.Val = fl
}
data = data[i:]
if len(data) < 2 {
return errInvalidFormat
}
// find first digit
for (data[0] < 48 || data[0] > 57) && data[0] != 110 {
if len(data) == 1 {
return errInvalidFormat
}
data = data[1:]
}
// find last digit
for i = 0; data[i] >= 48 && data[i] <= 57 && i < len(data); i++ {
}
if i == 0 {
return errInvalidFormat
}
ts, err := strconv.ParseUint(string(data[:i]), 10, 32)
if err != nil {
return err
}
p.Ts = uint32(ts)
return nil
}
完整的示例程序:on playground