IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY while using Retrofit 2 - java

I'm trying to execute some code on some parsed JSON using Retrofit, but I am getting an Illegal State Exception. Here is my Retrofit instance:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.pro.coinbase.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
CoinbaseProAPI coinbaseProAPI = retrofit.create(CoinbaseProAPI.class);
The Coinbase Pro API interface:
#GET("products/{productId}/candles")
Call<ArrayList<HistoricRates>> getHistoricRates(#Header("CB-ACCESS-SIGN") String signature,
#Header("CB-ACCESS-TIMESTAMP") String timeStamp,
#Path("productId") String productId,
#Query("start") String intervalStart,
#Query("end") String intervalEnd,
#Query("granularity") int granularity
);
The call request to the server (this is what's causing the error):
Call<ArrayList<HistoricRates>> call = coinbaseProAPI.getHistoricRates(signature, timeStamp, productId, intervalStart, intervalEnd, granularity);
try {
//this is being executed inside a for loop; requests are rate limited so consecutive
//iterations must wait
if (i > 0) {
call.wait(1010);
}
//this is the line the debugger doesn't like:
Response<ArrayList<HistoricRates>> response = call.execute();
if (!response.isSuccessful()) {
Message eMsg = errorHandler.obtainMessage();
String e = R.string.error_header + response.toString();
eMsg.what = 2;
eMsg.obj = e;
errorHandler.sendMessage(eMsg);
} else {
ArrayList<HistoricRates> candles = response.body();
int n = 0;
double[][] aCandles = new double[3][3];
for (HistoricRates historicRates : candles) {
aCandles[n][0] = historicRates.getOpen();
aCandles[n][1] = historicRates.getClose();
aCandles[n][2] = historicRates.getVolume();
n++;
}
selectedAsset.setCandles(aCandles);
}
} catch (Exception e) {
Message eMsg = errorHandler.obtainMessage();
eMsg.what = 2;
eMsg.obj = e.getMessage();
errorHandler.sendMessage(eMsg);
e.printStackTrace();
}
And finally the class object the JSON is being parsed into:
public class HistoricRates {
private int time;
private double low;
private double high;
private double open;
private double close;
private double volume;
public double getOpen() {
return open;
}
public double getClose() {
return close;
}
public double getVolume() {
return volume;
}
}
So from what I gather from similar posts, Illegal State Exception occurs because the object I'm trying to pass to Gson is not of the correct type. But I don't understand how this could be, given that I'm using Retrofit; shouldn't the GsonConverterFactory take care of this for me? Also, even though I have this code enclosed within a try/catch block, it is not actually throwing an exception; it just fails silently. The only reason I even know about the Illegal State Exception is because of the Debug Console. How can I improve my error reporting or is there something else I'm not understanding?
EDIT TO ADD:
Tried Capps99's solution (with added accessor method included in HistoricRatesList so I can get an iterator to loop through the values) Here is what that looks like:
HistoricRatesList candles = response.body();
Iterator candleIterator = candles.getList().iterator();
int n = 0;
double[][] aCandles = new double[3][3];
while (candleIterator.hasNext()) {
HistoricRates historicRates = (HistoricRates) candleIterator.next();
aCandles[n][0] = historicRates.getOpen();
aCandles[n][1] = historicRates.getClose();
aCandles[n][2] = historicRates.getVolume();
n++;
}
Unfortunately I'm still getting an Illegal State Exception. At the request of another commenter, here is the expected JSON response from the server(apologies I should have included this to begin with):
[
//these are all decimal values except for time; which is an integer value
//representing the UNIX epoch time
[time, low, high, open, close, volume],
[time, low, high, open, close, volume],
...
]
Perhaps the trouble is that the server appears to be returning the data in the form of a 2D array, which I don't have the experience to know how to parse properly.

Try this.
Create the following class to parse the list.
public class HistoricRatesList {
List<HistoricRates> historicRates;
}
and then use like this.
#GET("products/{productId}/candles")
Call<HistoricRatesList> getHistoricRates(#Header("CB-ACCESS-SIGN") String signature,
#Header("CB-ACCESS-TIMESTAMP") String timeStamp,
#Path("productId") String productId,
#Query("start") String intervalStart,
#Query("end") String intervalEnd,
#Query("granularity") int granularity
);

Related

Passing dynamic method name to another method as parameter in java

I have a calling method as genJsonPayload(). Inside this method I am calling a replace() method to replace some string with randomly generated values. I want to call the replace() by passing another method as parameter. My sample code below.
public static StringBuffer replace(String jsonparam, StringBuffer jsondata, String replacedWith) {
int start = 0;
int last = 0;
while (jsondata.indexOf(jsonparam)!= jsondata.lastIndexOf(jsonparam)) {
start = jsondata.indexOf(jsonparam);
last = start + jsonparam.length();
jsondata = jsondata.replace(start,last,replacedwith);
}
start = jsondata.indexOf(jsonparam);
last = start + jsonparam.length();
jsondata = jsondata.replace(start,last,replacedwith);
return jsondata;
}
public String genJSONPayload(String jsonpayload) {
StringBuffer sbuffer = new StringBuffer(jsonpayload);
try {
jsondata = replace("expectedAreaCode",jsondata, CreateRandom.createAreaCDRandom());
}
catch (Exception e) {
....
}
try {
jsondata = replace("expectedextensionNo",jsondata, CreateRandom.createExtensionNumberRandom());
}
catch (Exception e) {
....
}
}
Json payload as below :
{
...
...
"phonenumber": [
{
"areaCode": "expectedAreaCode",
"extensionNo": "expectedextensionNo",
...
}
{
"areaCode": "expectedAreaCode",
"extensionNo": "expectedextensionNo",
...
}
{
"areaCode": "expectedAreaCode",
"extensionNo": "expectedextensionNo",
...
}],
...
...
}
Note :
CreateRandom.createAreaCDRandom() return a random String.
Problem statement :
My requirement is to replace "expectedAreaCode" of json file with randomly generated area code using the replace() method. But as I am executing the CreateRandom.createAreaCDRandom() from genJSONPayload() only once so replacedWith in replace() method is getting store with fixed area code and for all the area code in the json array phonenumber, I am getting the same areacode.
Question :
Instead of passing the replacedWith String to the replace() method, can I pass the createAreaCDRandom() static method as parameter and instead of executing createAreaCDRandom() method from genJSONPayload, then I will execute it inside replace method multiple times inside the while loop. I have more than 5 such random mentods, basically for different json parameters.
How can I pass the createAreaCDRandom(), createExtensionNumberRandom(), etc methods as the parameter in the repace() method?
Use Supplier class as a method parameter (https://docs.oracle.com/javase/8/docs/api/java/util/function/Supplier.html).
Then You can pass lambda or method reference to provide value generating logic.
public static StringBuffer replace(String jsonparam, StringBuffer jsondata, Supplier<String> supplier) {
int start = 0;
int last = 0;
while (jsondata.indexOf(jsonparam) != jsondata.lastIndexOf(jsonparam)) {
start = jsondata.indexOf(jsonparam);
last = start + jsonparam.length();
jsondata = jsondata.replace(start, last, supplier.get());
}
start = jsondata.indexOf(jsonparam);
last = start + jsonparam.length();
jsondata = jsondata.replace(start, last, supplier.get());
return jsondata;
}
public String genJSONPayload(String jsonpayload) {
StrignBuffer sbuffer = new StrignBuffer(jsonpayload);
try{
jsondata = replace("expectedAreaCode",jsondata, CreateRandom::createAreaCDRandom);
}
catch(Exception e){
....
}
try{
jsondata = replace("expectedextensionNo",jsondata, CreateRandom::createExtensionNumberRandom);
}
catch(Exception e){
....
}
}

How to load records into a jTable from a file?

I'm making a GUI program, using netbeans, that is supposed to be an interface for managing records in a video store.
This is the interface. It's two tabs, and one side allows a person to add records, whilst the other tab displays them. When a person adds records, they are added to a .dat file named output. I would like to use the .dat file as a permanent storage area for the video records, and basically what I want to happen is that when one loads the GUI class, the program loads all the records from the .dat file. I have already created my code, but I'm getting the following error:
run:
java.io.EOFException
at java.io.RandomAccessFile.readChar(RandomAccessFile.java:773)
at videostore.BinaryFile.getString(BinaryFile.java:82)
at videostore.BinaryFile.load(BinaryFile.java:116)
at videostore.VideoStore.main(VideoStore.java:409)
Exception in thread "main" java.lang.NullPointerException
at videostore.VideoStore.main(VideoStore.java:420)
/Users/(my Name)/Library/Caches/NetBeans/8.1/executor-snippets/run.xml:53: Java returned: 1
BUILD FAILED (total time: 2 seconds)
And then I'll paste all relevant code below.
In the main method of the GUI class, named VideoStore.java:
file = new BinaryFile("/Users/hanaezz/Desktop/output.dat");
int length = file.length();
int ID = 1;
for (int xx = 0; xx < length; xx += file.getRecordLength()) {
Video load = file.load(xx);
String tempName = load.getVideoName();
String tempProd = load.getProducer();
String tempRat = load.getRating();
String tempGenre = load.getGenre();
short tempNum = load.getVidNum();
float tempPrice = load.getvideoPrice();
Object[] row = {ID, tempName, tempProd, tempGenre, tempRat, tempNum, tempPrice};
model.addRow(row);
ID++;
}
in the VideoStore constructor class:
public VideoStore() {
initComponents();
model = (DefaultTableModel) displayVideos.getModel();
}
And within the BinaryFile class:
private static final int RecordLength = 112;
public static Video load(int place){
String name = "", prod="", rat="", genre="";
float price = 1;
short number = 1;
try {
raf.seek(place);
name = getString(20);
prod = getString(15);
rat = getString(20);
genre = getString(10);
price = Float.parseFloat(getString(4));
number = Short.parseShort(getString(4));
writeString(20, name);
writeString(15, prod);
writeString(10, genre);
writeString(4, VideoStore.vPrice.getText());
writeString(4, VideoStore.vNumber.getText());
writeString(4, rat);
} catch (Exception e) {
e.printStackTrace();
}
Video r = new Video(name, prod, genre, rat, number, price);
return r;
}
public static int getRecordLength() throws IOException{
return RecordLength;
}
public static int length() throws IOException {
return (int)raf.length();
}
And finally, my Video class:
private static String videoName;
private static String producer;
private static String rating;
private static String genre;
private static short videoNumber;
private static float videoPrice;
public Video(String a, String b, String c, String d, short e, float f){
videoName = a;
producer = b;
rating = c;
genre = d;
videoNumber = e;
videoPrice = f;
}
...Then mutator and accessor methods for each variable in the class...
#Override
public String toString(){
return videoName + "\t" + producer +
"\t" + rating + "\t" + genre +
"\t" + videoNumber + "\t" + videoPrice;
}
So yeah, my issue is that I can't figure out how to load records from the file into the table. In my code I tried to use a loop that would iterate through each record in the file based on the record's size.. however it doesn't seem to have worked. If anyone would like to see my full code or needs more information, don't hesitate to contact me :)
First, you should use a more Object Oriented approach.
Your video class contains nothing but static attributes, when it should looks like this:
public class Video implements Serializable{
private String name;
private String producer; //consider using an object for this
private String rating; //consider using a numeric type for this
private String genre; //consider using an object for this
private int number;
private double price;
//getters and setters
}
Check the Object-Oriented Programming Concepts.
To add a new video, you get the user input from the graphic interface, and use it to create a Video object.
Video video = new Video();
video.setName(nameTextField.getText());
//same for the other attributes
Then you can keep all your videos in a List.
List<Video> videosList = new ArrayList<>();
videoList.add(video);
Then you can serialize your list to a file.
try(FileOutputStream outputFile = new FileOutputStream("/path/to/file");
ObjectOutputStream out = new ObjectOutputStream(outputFile)){
out.writeObject(videoList);
} catch (IOException e1) {
// Handle the exception
}
To read back your list from the file, you need to deserialize it:
try(FileInputStream inputFile = new FileInputStream("/path/to/file");
ObjectInputStream in = new ObjectInputStream(inputFile)){
videoList = (List<Video>)in.readObject();
} catch (IOException e1) {
// Handle the exception
}

need advice regarding design and performance issue

I have data retrieved from a server at very high rate, the datas sent are in form of a message that resembles the following format:
$FMSn,par1,par2...,...,...,...,..,...,....,par20 //where n is number ranges from 1 to 12
this message I need to process to parse some data.
but less frequently the server sends other message in different format, that message is not important and could be discarded and the difference between it and the previously described messages in format is that
the previous message starts with $FMS while the other message not.
to distinguish between these messages to know which one is that should be processed, i created a class FMSParser as shown below and i checked if the message header is
$FMS
or not.
my question is, should i create a new object of FMSParser class in the loop in which the messages from the server are received or create one object in the whole
program and in the loop in which the data are recived i just call isValid method and getParam(). in other words in code:
should i choose solution 1 or 2?
solution 1:
loop for messages receiving:
msg = receive message();
fmsParser = new FMSParser(msg);
if (fmsParser.isValid) {
params = fmsParser.getParam();
}
solution 2:
fmsParser = new FMSParser();
loop for messages receiving:
msg = receive message();
if (fmsParser.isValid(msg)) {
params = fmsParser.getParam();
}
code:
private class FMSParser {
private final static String HEADER = "$FMS"
private String[] mSplittedMsg;
FMSParser() {}
public boolean isValidMsg(String msg) {
boolean isValid = false;
this.mSplittedMsg = msg.split(",");
for (int i = 0; i < 12; i++) {
if (splittedMsg[0].equals(HEADER+i)) {
valid = true;
break;
}
}
return valid;
}
public String [] getParam() {
return this.mSplittedMsg;
}
}
If you construct a new FMSParser each time through the loop, it will require memory allocation and garbage collection.
I would choose option 3 which makes the FMSParser immutable, meaning it is thread-safe.
FMSParser fmsParser = new FMSParser();
while (messageIterator.hasNext()) {
String msg = messageIterator.next();
if (fmsParser.isValid(msg)) {
params = fmsParser.getParam(msg);
}
}
Eg:
public class FMSParser {
public boolean isValid(String msg) {
return msg.startsWith("$FMS");
}
public String[] getParams(String msg) {
return msg.split(",");
}
}

Grabbing tagged instagram photos in real time

I'm trying to download photos posted with specific tag in real time. I found real time api pretty useless so I'm using long polling strategy. Below is pseudocode with comments of sublte bugs in it
newMediaCount = getMediaCount();
delta = newMediaCount - mediaCount;
if (delta > 0) {
// if mediaCount changed by now, realDelta > delta, so realDelta - delta photos won't be grabbed and on next poll if mediaCount didn't change again realDelta - delta would be duplicated else ...
// if photo posted from private account last photo will be duplicated as counter changes but nothing is added to recent
recentMedia = getRecentMedia(delta);
// persist recentMedia
mediaCount = newMediaCount;
}
Second issue can be addressed with Set of some sort I gueess. But first really bothers me. I've moved two calls to instagram api as close as possible but is this enough?
Edit
As Amir suggested I've rewritten the code with use of min/max_tag_ids. But it still skips photos. I couldn't find better way to test this than save images on disk for some time and compare result to instagram.com/explore/tags/.
public class LousyInstagramApiTest {
#Test
public void testFeedContinuity() throws Exception {
Instagram instagram = new Instagram(Settings.getClientId());
final String TAG_NAME = "portrait";
String id = instagram.getRecentMediaTags(TAG_NAME).getPagination().getMinTagId();
HashtagEndpoint endpoint = new HashtagEndpoint(instagram, TAG_NAME, id);
for (int i = 0; i < 10; i++) {
Thread.sleep(3000);
endpoint.recentFeed().forEach(d -> {
try {
URL url = new URL(d.getImages().getLowResolution().getImageUrl());
BufferedImage img = ImageIO.read(url);
ImageIO.write(img, "png", new File("D:\\tmp\\" + d.getId() + ".png"));
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
}
class HashtagEndpoint {
private final Instagram instagram;
private final String hashtag;
private String minTagId;
public HashtagEndpoint(Instagram instagram, String hashtag, String minTagId) {
this.instagram = instagram;
this.hashtag = hashtag;
this.minTagId = minTagId;
}
public List<MediaFeedData> recentFeed() throws InstagramException {
TagMediaFeed feed = instagram.getRecentMediaTags(hashtag, minTagId, null);
List<MediaFeedData> dataList = feed.getData();
if (dataList.size() == 0) return Collections.emptyList();
String maxTagId = feed.getPagination().getNextMaxTagId();
if (maxTagId != null && maxTagId.compareTo(minTagId) > 0) dataList.addAll(paginateFeed(maxTagId));
Collections.reverse(dataList);
// dataList.removeIf(d -> d.getId().compareTo(minTagId) < 0);
minTagId = feed.getPagination().getMinTagId();
return dataList;
}
private Collection<? extends MediaFeedData> paginateFeed(String maxTagId) throws InstagramException {
System.out.println("pagination required");
List<MediaFeedData> dataList = new ArrayList<>();
do {
TagMediaFeed feed = instagram.getRecentMediaTags(hashtag, null, maxTagId);
maxTagId = feed.getPagination().getNextMaxTagId();
dataList.addAll(feed.getData());
} while (maxTagId.compareTo(minTagId) > 0);
return dataList;
}
}
Using the Tag endpoints to get the recent media with a desired tag, it returns a min_tag_id in its pagination info, which is tied to the most recently tagged media at the time of your call. As the API also accepts a min_tag_id parameter, you can pass that number from your last query to only receive those media that are tagged after your last query.
So based on whatever polling mechanism you have, you just call the API to get the new recent media if any based on last received min_tag_id.
You will also need to pass a large count parameter and follow the pagination of the response to receive all data without losing anything when the speed of tagging is faster than your polling.
Update:
Based on your updated code:
public List<MediaFeedData> recentFeed() throws InstagramException {
TagMediaFeed feed = instagram.getRecentMediaTags(hashtag, minTagId, null, 100000);
List<MediaFeedData> dataList = feed.getData();
if (dataList.size() == 0) return Collections.emptyList();
// follow the pagination
MediaFeed recentMediaNextPage = instagram.getRecentMediaNextPage(feed.getPagination());
while (recentMediaNextPage.getPagination() != null) {
dataList.addAll(recentMediaNextPage.getData());
recentMediaNextPage = instagram.getRecentMediaNextPage(recentMediaNextPage.getPagination());
}
Collections.reverse(dataList);
minTagId = feed.getPagination().getMinTagId();
return dataList;
}

How to overcome this java syntax so that double become string?

I am stuck in a situation whereby my Bean class - CartItemBean is in double.
public double getTotalCost() {
return dblTotalCost;
And the SetExpressCheckOutService class requires me to put the amount in String.
String amount = "";
CartItemBean details = new CartItemBean();
amount = details.getTotalCost();
try {
//calling the service, setting up the checkoutpage
String token = setExpressCheckoutService.setExpressCheckout(userId, amount,
currencyCode, returnURL, cancelURL, paymentAction);
log.info("Url to redirect to: https://www.sandbox.paypal.com
/webscr?cmd=_express-checkout&useraction=commit&token=" + token);
} catch (PayPalException e) {
// Log the exception
log.log(Level.WARNING, "Paypal exception", e);
}
}
I hope someone can advise me how to overcome a problem like this.
Thanks.
Use the toString method of the Double object:
String string = Double.toString(double);
So in your code do this:
String token = setExpressCheckoutService.setExpressCheckout(userId, Double.toString(amount), currencyCode, returnURL, cancelURL, paymentAction);
I have found the solution:
amount = Double.toString(details.getTotalCost());

Categories