
FXCM 的 JSON 接口(如 /DataDisplayer?callback=Tick)实际返回的是 JSONP 格式,即 JavaScript 函数调用包裹的 JSON 字符串,需先剥离 callback(...) 外壳,提取合法 JSON 片段后才能解码。
fxcm 的 json 接口(如 `/datadisplayer?callback=tick`)实际返回的是 jsonp 格式,即 javascript 函数调用包裹的 json 字符串,需先剥离 `callback(...)` 外壳,提取合法 json 片段后才能解码。
FXCM 提供的实时汇率接口(例如 https://ratesjson.fxcm.com/DataDisplayer?&callback=Tick)并非标准 JSON,而是 JSONP(JSON with Padding) 格式。其响应形如:
Tick({"Rates":[{"Symbol":"EUR/USD","Bid":1.08245,"Ask":1.08255,"Spread":10,"ProductType":"CFD"}]})这种格式无法被 Go 的 encoding/json 或 ffjson 直接解析,因为最外层是 JavaScript 函数调用,而非纯 JSON 对象或数组。
✅ 正确处理步骤
- 发起 HTTP 请求获取原始响应体;
-
定位并截取合法 JSON 子串:从第一个
{开始,到最后一个}结束; -
使用标准
json.Unmarshal解析截取后的字节切片(无需第三方库)。
以下是修复后的完整可运行示例(已移除过时的 ioutil,适配 Go 1.16+):
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
)
type MsgRateQuoteJson struct {
SymbolName string `json:"Symbol"`
SymbolBid int64 `json:"Bid"`
SymbolAsk int64 `json:"Ask"`
SymbolSpread int64 `json:"Spread"`
SymbolPT string `json:"ProductType"`
}
type MsgRatesArray struct {
RateQuote []MsgRateQuoteJson `json:"Rates"`
}
func main() {
resp, err := http.Get("https://ratesjson.fxcm.com/DataDisplayer?&callback=Tick")
if err != nil {
log.Fatal("HTTP request failed:", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Fatal("Failed to read response body:", err)
}
// 提取合法 JSON:从第一个 '{' 到最后一个 '}'
start := bytes.Index(body, []byte("{"))
end := bytes.LastIndex(body, []byte("}"))
if start == -1 || end == -1 || start > end {
log.Fatal("No valid JSON object found in response")
}
jsonBytes := body[start : end+1]
var msg MsgRatesArray
if err := json.Unmarshal(jsonBytes, &msg); err != nil {
log.Fatalf("JSON unmarshal failed: %v\nRaw snippet: %s", err, string(jsonBytes))
}
fmt.Printf("Parsed %d rate quotes\n", len(msg.RateQuote))
for _, q := range msg.RateQuote {
fmt.Printf("Symbol: %s, Bid: %d, Ask: %d, Spread: %d\n",
q.SymbolName, q.SymbolBid, q.SymbolAsk, q.SymbolSpread)
}
}⚠️ 注意事项
-
不要使用
ffjson或其他高性能 JSON 库来绕过格式问题:根本问题在于输入非法,而非性能瓶颈; -
避免硬编码
callback=Tick的假设:实际回调名可能变化,但{和}的 JSON 结构始终可靠; -
务必校验
bytes.Index返回值:若接口返回错误页(如 404 HTML),start或end可能为-1,需做空检查; -
生产环境建议添加超时与重试机制:使用
http.Client配置Timeout; -
SetEscapeHTML(true)在本场景无意义——该设置仅影响json.Encoder输出(如服务端渲染),而此处是解析(Unmarshal),与编码无关。
通过精准截取 JSON 片段再解析,即可稳定对接 FXCM 的 JSONP 接口,无需依赖外部工具或复杂正则,简洁、健壮、符合 Go 的惯用实践。


















