关于Golang的JSON - Unmarshal Graphite数据

时间:2015-03-11 15:47:56

标签: json go

我正在使用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)

但结果是:

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

我当然可以使用通用接口{}但我想知道我在这里缺少什么。

你能帮我吗?

谢谢!

2 个答案:

答案 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