0

Given the following REST call...

https://api.bitfinex.com/v2/ticker/fUSD

... I get back the following result as a JSON array:

[0.00073995,0.00067,30,10082128.5775703,0.000664,2,191337.1001453,-0.00003991,-0.057,0.00066009,94710166.80102274,0,0]

Here below is how I parse the result:

const (
    URL = "https://api.bitfinex.com/v2/ticker/f"
)

type Tick struct {
    FlashReturnRate float64
    Bid             float64
    BidPeriod       int64
    BidSize         float64
    Ask             float64
    AskPeriod       int64
    AskSize         float64
    DailyChange     float64
    DailyChangePerc float64
    LastPrice       float64
    Volume          float64
    High            float64
    Low             float64
}

...

func (s *TickerService) Get(curry string) (*Tick, error) {
    req, err = http.NewRequest("GET", URL + curry, nil)
    if err != nil {
        return nil, err
    }

    resp, err := http.DefaultClient.Do(req); if err != nil {
        return nil, err
    }

    defer resp.Body.Close()

    var v []float64
    err = json.Unmarshal(resp.Body, &v)
    if err != nil {
        return nil, err
    }

    t := Tick{
        FlashReturnRate: v[0],
        Bid:             v[1],
        BidSize:         v[2],
        BidPeriod:       int64(v[3]),
        Ask:             v[4],
        AskSize:         v[5],
        AskPeriod:       int64(v[6]),
        DailyChange:     v[7],
        DailyChangePerc: v[8],
        LastPrice:       v[9],
        Volume:          v[10],
        High:            v[11],
        Low:             v[12],
    }

    return &t, nil
}

Is there a better way to build the Tick object?

2
  • 2
    The JSON is just a plain array.. then I say your doing it to the way... there aren't any "headers" to unmarshal directly into. The JSON array would have to be sent as a JSON object to unmarshal directly into a struct. Commented Sep 12, 2017 at 18:27
  • 3
    If style is the issue you can always create a custom type with a custom unmarshaller choly.ca/post/go-json-marshalling Commented Sep 12, 2017 at 18:40

1 Answer 1

1

You're on the right track, but a more usable solution would be to create a custom unmarshaler:

func (t *Tick) UnmarshalJSON(data []byte) error {
    var v []float64
    if err := json.Unmarshal(data, &v); err != nil {
        return err
    }
    t.FlashReturnRate = v[0]
    t.Bid = v[1]
    t.BidSize = int64(v[2]) // Is this an int? Your sample data suggests it is, your code suggests it isn't.
    // ... etc

    return nil
}

Now you can unmarshal directly to your Tick data type as:

var tick Tick
if err := json.Unmarshal(res.Body, &tick); err != nil {
    // Handle error
}
Sign up to request clarification or add additional context in comments.

3 Comments

I already modified v from []float64 to []interface{}... but to parse an int I have to do something like t.BidSize = int64(v[2].(float64))... otherwise the compilation fails with error interface conversion: interface {} is float64, not int64... even if the REST response actually contains an int in third position (i.e. 30). .
@j3d: Mmm, to do proper int unmarshaling will be more complex then. How large are these integers? If they're smaller than 2^52, then using float64 should not result in a loss of precision. If they can be larger than that, then you can parse each array element individually; it's more complex, but I can update the answer with an example if it would be helpful.
No, those integers are smaller than 2^52.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.