【问题标题】:JSON on Golang - Unmarshal Graphite DataGolang 上的 JSON - Unmarshal Graphite Data
【发布时间】:2015-05-13 12:20:45
【问题描述】:

我正在使用 Golang 和 JSON 尝试使用从 Graphite API 提取的数据进行一些计算。

为简单起见,Graphite 发送的数据的一个 sn-p 为:

[
{
    "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)

但结果是:

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

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

你能帮帮我吗?

谢谢!

【问题讨论】:

  • 如果您不反对使用第三方解决方案,This 库非常好。
  • 谢谢,这似乎是个不错的选择!

标签: json go


【解决方案1】:

我喜欢从最简单的类型开始,然后逐步解决。首先,您需要表示您的数据点。

type DataPoint []float64

那么指标只是一个目标和一系列数据点。

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

不需要您的Graphite 结构。您的 JSON 只是 Metrics 的 JSON 数组。

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

这是一个带有完整示例的 playground 链接。

【讨论】:

  • 感谢您抽出宝贵时间对此进行解释,非常感谢!
  • 如果像您的示例数据一样,所有数据点都是两个值,您可以使用数组:type DataPoint [2]float64。如果您需要检测输入数据中 0 和 null 之间的差异(或将 0 和 nil 之间的差异重新编码为 JSON),您可以将其设为指针:type DataPoint []*float64type DataPoint [2]*float64
【解决方案2】:

上述答案的问题在于它将空点变成值为0的点,这是不正确的。 Null 表示“未知”。有人建议使用指向浮点数的指针,因此 nil 指针意味着“没有值”,但这会产生很大的开销(例如,在大多数 64 位平台上是 8 字节,更不用说内存取消引用开销了)。

最好使用 golang 的数学 NaN 支持来标记空值,这不需要额外的数据,因为它内置在浮点表示中。 您可以使用自定义 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-05
    • 2015-03-15
    • 1970-01-01
    • 2013-06-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多