简体   繁体   English

关于Golang的JSON - Unmarshal Graphite数据

[英]JSON on Golang - Unmarshal Graphite Data

I am playing around with Golang and JSON trying to do some calculations with data extracted from Graphite API. 我正在使用Golang和JSON尝试使用从Graphite API中提取的数据进行一些计算。

For simplicity, a snippet of the data sent by Graphite is: 为简单起见,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
        ]
    ]
}
]

I've been reading through the go json tutorial about how to use a generic interface{} for JSON arbitrary data, but I'm struggling with some aspects of the process. 我一直在阅读关于如何使用通用接口{}获取JSON任意数据的go json教程 ,但我正在努力解决该过程的某些方面。

I've tried to define a structure that will hold this data, read the file contents and unmarshal it to this structure: 我试图定义一个结构来保存这些数据,读取文件内容并将其解组为此结构:

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)

But the result is: 但结果是:

[{{ []}} {{ []}}]

I could do it of course with a generic interface{} but I would like to know what am I missing here. 我当然可以使用通用接口{}但我想知道我在这里缺少什么。

Could you please help me? 请你帮助我好吗?

Thank you! 谢谢!

I like to start at the simplest type and work my way out. 我喜欢从最简单的类型开始,然后继续努力。 First you need to represent your data point. 首先,您需要表示您的数据点。

type DataPoint []float64

Then a metric is just a target and a series of data points. 然后,度量标准只是一个目标和一系列数据点。

type Metric struct {
    Target string      `json:"target"`
    Points []DataPoint `json:"datapoints"`
}

There is no need for your Graphite struct. 您不需要Graphite结构。 Your JSON is just a JSON array of Metric s. 您的JSON只是Metric的JSON数组。

var results []Metric
err := json.Unmarshal([]byte(data), &results)

Here's a playground link with a complete example. 这是一个带有完整示例的游乐场链接。

The problem with the above answer is it turns null points into points with value 0, which is not correct. 上述答案的问题是它将零点转换为值为0的点,这是不正确的。 Null means "not known". Null表示“未知”。 Some people suggest using pointers to floats so nil pointer means "no value", but that has a significant overhead (eg 8bytes on most 64bit platforms, not to mention the memory dereferencing overhead). 有些人建议使用指针浮点数,因此nil指针意味着“没有价值”,但这有很大的开销(例如大多数64位平台上的8字节,更不用说内存解除引用开销)。

Better to use golang's math NaN support to mark nulls, which requires no additional data as it's built into the float representation. 最好使用golang的数学NaN支持来标记空值,这不需要额外的数据,因为它内置在float表示中。 You can do this with a custom Unmarshal function like so: 您可以使用自定义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
}

Full example program: on playground 完整示例程序: 在操场上

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM