binance-exchange / binance-java-api

binance-java-api is a lightweight Java library for the Binance API, supporting synchronous and asynchronous requests, as well as event streaming using WebSockets.
MIT License
830 stars 622 forks source link

Can not construct instance of java.lang.Long from String value #359

Open JitanderVerma opened 3 years ago

JitanderVerma commented 3 years ago

Code :

try {
            candlestickBars = client.getCandlestickBars(symbol.toUpperCase(), interval, Integer.valueOf(TOTAL_PERIODS), null, null);
            closingPriceList = candlestickBars.stream().map(c -> Double.valueOf(c.getClose())).collect(Collectors.toList());
            values = new double[closingPriceList.size()];

            for (int i = 0; i < closingPriceList.size(); i++) {
                values[i] = closingPriceList.get(i);
            }
        }

Exception : com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not construct instance of java.lang.Long from String value ("2774.64000000"): not a valid Long value at [Source: okhttp3.ResponseBody$BomAwareReader@4a1c12c; line: 1, column: 17] (through reference chain: java.util.ArrayList[0]->com.binance.api.client.domain.market.Candlestick["closeTime"])

JitanderVerma commented 3 years ago

Code runs smoothly on command line. but on android, it produces this exception. Any help is appreciated. Thanks.

KillerInk commented 3 years ago

hm not sure but from the error it seems that you candelsticks close field is long type. but it should be double. that problem get caused by the deserializer. on android it seems to fail when the field order do not match or the naming. you can write your own deserializer. or apply the json tags to the fields

omeronce1994 commented 3 years ago

You can do the following steps which worked for me (written in Kotlin): You can use this custom deserializer:

` class CandleSticksConverter: Converter<ResponseBody, List> {

private val TAG = "CandleSticksConverter"

override fun convert(value: ResponseBody): List<Candlestick> {
    val result = mutableListOf<Candlestick>()
    val text = value.string()
    val array = JSONArray(text)
    for (i in 0 until array.length()) {
        val current: JSONArray = array[i] as JSONArray
        val newCandle = Candlestick()
        for (k in 0 until current.length()) {
            updateCandleItem(newCandle, current, k)
        }
        result.add(newCandle)
    }
    return result
}

private fun updateCandleItem(item: Candlestick, jsonItem: JSONArray, index: Int) {
    when (index) {
        0 -> item.openTime = jsonItem[index] as Long?
        1 -> item.open = jsonItem[index] as String?
        2 -> item.high = jsonItem[index] as String?
        3 -> item.low = jsonItem[index] as String?
        4 -> item.close = jsonItem[index] as String?
        5 -> item.volume = jsonItem[index] as String?
        6 -> item.closeTime = jsonItem[index] as Long?
        7 -> item.quoteAssetVolume = jsonItem[index] as String?
        8 -> item.numberOfTrades = (jsonItem[index] as Int?)?.toLong()
        9 -> item.takerBuyBaseAssetVolume = jsonItem[index] as String?
        10 -> item.takerBuyQuoteAssetVolume = jsonItem[index] as String?
    }
}

} ` with the following Converter.factory

` class ConverterFactory(private val default: JacksonConverterFactory): Converter.Factory() {

override fun responseBodyConverter(type: Type, annotations: Array<Annotation>, retrofit: Retrofit): Converter<ResponseBody, *>? {
    return if (type == TypeUtils.parameterize(List::class.java, Candlestick::class.java)) {
        CandleSticksConverter()
    }
    else {
        default.responseBodyConverter(type, annotations, retrofit)
    }
}

override fun requestBodyConverter(type: Type, parameterAnnotations: Array<Annotation>, methodAnnotations: Array<Annotation>, retrofit: Retrofit): Converter<*, RequestBody>? {
    return default.requestBodyConverter(type, parameterAnnotations, methodAnnotations, retrofit)
}

} `

Then use this decorator class for client, which accept implementation for BinanceRestApiClient in the constructor: ` class MyBinanceClient(default: BinanceApiRestClient): BinanceApiRestClient by default {

    private val loggingInterceptor = HttpLoggingInterceptor().apply {
        level = HttpLoggingInterceptor.Level.BODY
    }

    private val sharedClient = OkHttpClient.Builder()
            .addInterceptor(loggingInterceptor)
            .pingInterval(20, TimeUnit.SECONDS)
            .build()

    private val converterFactory: Converter.Factory = ConverterFactory(JacksonConverterFactory.create())

    var retrofitBuilder = Retrofit.Builder()
            .baseUrl(BinanceApiConstants.API_BASE_URL)
            .addConverterFactory(converterFactory)
            .client(sharedClient)
            .build()

    private val binanceApiService: BinanceApiService = retrofitBuilder.create(BinanceApiService::class.java)

    override fun getCandlestickBars(symbol: String?, interval: CandlestickInterval?): MutableList<Candlestick> {
        return binanceApiService.getCandlestickBars(symbol, interval?.intervalId, null, null, null).execute().body() ?: mutableListOf()
    }
}

`