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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.