I am stuck in one of the issue. Suppose i have a phone number field 010-9999-9999, this number should split in 3 text field. I tried to do this but i get only prefix number i.e 010 in all three text field. I am using jface databinding.
I created Model as
class A{
String phoneNo;
}
Jface Databinding:
IObservableValue ssn1TextTextObserveWidget = SWTObservables.observeText(text_ph11, SWT.Modify);
IObservableValue ssn2TextTextObserveWidget = SWTObservables.observeText(text_ph2, SWT.Modify);
IObservableValue ssn2TextTextObserveWidget1 = SWTObservables.observeText(text_ph3, SWT.Modify);
IObservableValue simpleTableViewerSSN1ObserveDetailValue = BeansObservables.observeDetailValue(simpleTableViewerSelectionObserveSelection_employee, "phoneNo", String.class);
IObservableValue simpleTableViewerSSN2ObserveDetailValue = BeansObservables.observeDetailValue(simpleTableViewerSelectionObserveSelection_employee, "phoneNo", String.class);
IObservableValue simpleTableViewerSSN2ObserveDetailValue = BeansObservables.observeDetailValue(simpleTableViewerSelectionObserveSelection_employee, "phoneNo", String.class);
Databinding
bindingContext.bindValue(simpleTableViewerSSN1ObserveDetailValue, ssn1TextTextObserveWidget, null, null);
bindingContext.bindValue(simpleTableViewerSSN2ObserveDetailValue, ssn2TextTextObserveWidget, null, null);
bindingContext.bindValue(simpleTableViewerSSN2ObserveDetailValue, ssn2TextTextObserveWidget1, null, null);
When i try to do this value only prefix get populated in all three fields i.e 010-010-010. Please help me out to resolve this issue.
If you want your phone number to be divided into three different widgets you will probably need to save the phone number in three different attributes, each assigned a different part of the phone number.
There might be some way to bind a 'part' of a string value to a widget, maybe through getter methods, but for that wait and let someone more knowledgeable in this area give an answer for you.
Here is sample code that can be modified to work with your case. IConverter is used with UpdateValueStrategy to modify the value that will be set/get model to/from target text feild.
//GUI class
final Display display = new Display();
final Shell shell = new Shell(display);
shell.setLayout(new GridLayout(3, false));
Label lbl = new Label(shell, SWT.NONE);
lbl.setText("Name");
final Text firstName = new Text(shell, SWT.BORDER);
final Text lastName = new Text(shell, SWT.BORDER);
final Name name = new Name();
SWTObservables.getRealm(display).exec(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
Realm realm = SWTObservables.getRealm(display);
DataBindingContext cxt = new DataBindingContext(realm);
ISWTObservableValue firstNameObservable = SWTObservables.observeText(firstName, new int[] { SWT.Modify });
ISWTObservableValue lastNameObservable = SWTObservables.observeText(lastName, new int[] { SWT.Modify });
final IObservableValue nameObservable = BeansObservables.observeValue(realm, name, "name");
UpdateValueStrategy firstNameTxtToName = new UpdateValueStrategy();
firstNameTxtToName.setConverter(new IConverter() {
#Override
public Object getToType() {
// TODO Auto-generated method stub
return String.class;
}
#Override
public Object getFromType() {
// TODO Auto-generated method stub
return String.class;
}
#Override
public Object convert(Object fromObject) {
String val = fromObject.toString();
Object beanVal = nameObservable.getValue();
if (beanVal != null) {
String beanString = beanVal.toString();
int i = beanString.indexOf(',');
if (i != -1) {
return val + beanString.substring(i);
}
}
return val;
}
});
UpdateValueStrategy nameToFirstName = new UpdateValueStrategy();
nameToFirstName.setConverter(new IConverter() {
#Override
public Object getToType() {
// TODO Auto-generated method stub
return String.class;
}
#Override
public Object getFromType() {
// TODO Auto-generated method stub
return String.class;
}
#Override
public Object convert(Object fromObject) {
if (fromObject != null) {
Object objVal = nameObservable.getValue();
if (objVal != null) {
String val = objVal.toString();
int i = val.indexOf(',');
if (i != -1) {
return val.substring(0, i);
} else {
return val;
}
}
}
return "";
}
});
UpdateValueStrategy lastNameTxtToName = new UpdateValueStrategy();
lastNameTxtToName.setConverter(new IConverter() {
#Override
public Object getToType() {
// TODO Auto-generated method stub
return String.class;
}
#Override
public Object getFromType() {
// TODO Auto-generated method stub
return String.class;
}
#Override
public Object convert(Object fromObject) {
String val = fromObject.toString();
Object beanVal = nameObservable.getValue();
if (beanVal != null) {
String beanString = beanVal.toString();
int i = beanString.indexOf(',');
String fName = beanString;
if (i != -1) {
fName = beanString.substring(0, i + 1);
} else {
fName = fName + ",";
}
val = fName + val;
}
return val;
}
});
UpdateValueStrategy nameToLastName = new UpdateValueStrategy();
nameToLastName.setConverter(new IConverter() {
#Override
public Object getToType() {
// TODO Auto-generated method stub
return String.class;
}
#Override
public Object getFromType() {
// TODO Auto-generated method stub
return String.class;
}
#Override
public Object convert(Object fromObject) {
if (fromObject != null) {
String val = fromObject.toString();
int i = val.indexOf(',');
if (i != -1) {
return val.substring(i + 1);
}
}
return "";
}
});
cxt.bindValue(firstNameObservable, nameObservable, firstNameTxtToName, nameToFirstName);
cxt.bindValue(lastNameObservable, nameObservable, lastNameTxtToName, nameToLastName);
}
});
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
//POJO
public class Name {
private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(propertyName, listener);
}
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(propertyName, listener);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
changeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
changeSupport.removePropertyChangeListener(listener);
}
public void firePropertyChangeEvent(PropertyChangeEvent evt) {
changeSupport.firePropertyChange(evt);
}
String name;
/**
* #return the name
*/
public String getName() {
return name;
}
/**
* #param name the name to set
*/
public void setName(String name) {
this.name = name;
System.out.println(name);
}
}
Related
I populate a RecyclerView from JSON data pulled from a webserver using Volley. I have two columns called user1Read and user2Read that is yes or no. I would like to know how I can modify my Adapter to set the TypeFace to bold if the message is considered "unread"? I am unsure of how to access the TextView from within my onResponse logic. Should I create a member variable in my Messages model class? Should I create it in my Adapter? Where would be the best place to put this? In my JSON onResponse, it would be great if I could just say
onResponseMethod {
if(!hasRead) {
//set TypeFace BOLD here
//But how to access TextView???
}
}
My custom Adapter:
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.squareup.picasso.Picasso;
import java.util.ArrayList;
import de.hdodenhof.circleimageview.CircleImageView;
public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.ViewHolder> implements Filterable {
private ArrayList<Message> messages;
private OnItemClickListener mListener;
#Override
public Filter getFilter() {
return null;
}
public interface OnItemClickListener {
void onItemClick(int position);
}
public void setOnItemClickListener(OnItemClickListener listener) {
mListener = listener;
}
public MessageAdapter(ArrayList<Message> messages) {
this.messages = messages;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.message_list_item, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
Message message = messages.get(position);
holder.username.setText(message.getUsername());
holder.messageSnippet.setText(message.getMessageSnippet());
holder.singleMessageTimestamp.setText(message.getTimestamp());
Picasso.get().load(message.getAvatarUrl()).into(holder.avatarCircleImageView);
}
#Override
public int getItemCount() {
if (messages != null) {
return messages.size();
} else {
return 0;
}
}
public class ViewHolder extends RecyclerView.ViewHolder {
public final View view;
public final TextView username;
public final TextView messageSnippet;
public final TextView singleMessageTimestamp;
public final CircleImageView avatarCircleImageView;
public ViewHolder(View view) {
super(view);
this.view = view;
username = view.findViewById(R.id.message_item_username_textview);
messageSnippet = view.findViewById(R.id.message_item_message_snippet_textview);
singleMessageTimestamp = view.findViewById(R.id.message_item_timestamp);
avatarCircleImageView = view.findViewById(R.id.message_item_circle_avatar);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mListener != null) {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
mListener.onItemClick(position);
}
}
}
});
}
}
}
My Message Model:
import android.widget.ImageView;
public class Message {
private String username;
private String messageSnippet;
private String avatarUrl;
private String timestamp;
private ImageView dot;
public Message(String username, String message, String avatar, String timestamp) {
this.username = username;
this.messageSnippet = message;
this.avatarUrl = avatar;
this.timestamp = timestamp;
}
public Message(String username, String avatar) {
this.username = username;
this.avatarUrl = avatar;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getMessageSnippet() {
return messageSnippet;
}
public void setMessageSnippet(String messageSnippet) {
this.messageSnippet = messageSnippet;
}
public String getTimestamp() {
return timestamp;
}
public void setTimestamp(String timestamp) {
this.timestamp = timestamp;
}
public String getAvatarUrl() {
return avatarUrl;
}
public void setAvatarUrl(String avatarUrl) {
this.avatarUrl = avatarUrl;
}
}
My main Activity (MessagesActivity).
public class MessagesActivity extends AppCompatActivity implements MessageAdapter.OnItemClickListener {
private RecyclerView messages;
private MessageAdapter adapter;
private ImageView circle_avatar;
private TextView header_username_text;
private ImageView avatar;
private TextView username_heading;
private static JSONArray profile_array;
private static final String TAG = "MessagesActivity";
private static final String getMessagesUrl = "https://myURL.com/get_messages.php";
private static String username;
private static ProgressDialog messagesProgressDialog;
private static ArrayList<Message> list = new ArrayList<>();
private static JSONObject jsonObject;
private static Timer updateTimer;
private static String hasRead;
private static RecyclerView.LayoutManager mLayoutManager;
private DrawerLayout mDrawerLayout;
private ImageButton composeImageButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messages);
configureNavigationDrawer();
configureToolbar();
if(list != null) {
list.clear();
}
avatar = findViewById(R.id.profile_avatar);
username_heading = findViewById(R.id.username_text);
updateTimer = new Timer();
TimerTask updateTimerTask = new TimerTask() {
public void run() {
refreshMessages();
}
};
updateTimer.scheduleAtFixedRate(updateTimerTask, 3000, 3000);
messagesProgressDialog = new ProgressDialog(MessagesActivity.this, R.style.Custom_Progress_Dialog);
messagesProgressDialog.setIndeterminate(true);
messagesProgressDialog.setMessage("Fetching Messages...");
messagesProgressDialog.show();
SharedPreferences sharedPreferences = this.getSharedPreferences("MyPref", MODE_PRIVATE);
username = sharedPreferences.getString("username", null);
getProfile(username);
messages = findViewById(R.id.messages);
messages.addItemDecoration(new DividerItemDecoration(this, LinearLayout.VERTICAL));
ArrayList<Message> messages = initMessages();
mLayoutManager = new LinearLayoutManager(this);
this.messages.setLayoutManager(mLayoutManager);
adapter = new MessageAdapter(messages);
this.messages.setAdapter(adapter);
adapter.setOnItemClickListener(this);
getProfile(username);
composeImageButton = findViewById(R.id.custom_toolbar_image_button);
composeImageButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "Clicked compose Image Button");
Intent composeIntent = new Intent(MessagesActivity.this, ComposeActivity.class);
startActivity(composeIntent);
updateTimer.cancel();
updateTimer.purge();
}
});
}
private ArrayList<Message> initMessages() {
RequestQueue messagesQueue = Volley.newRequestQueue(this);
StringRequest messagesRequest = new StringRequest(Request.Method.POST, getMessagesUrl, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
messagesProgressDialog.dismiss();
try {
String baseUrl = "https://myURL.com";
String defaultbaseUrl = "https://myURL.com/css/images/user_default/default_avatar.png";
JSONObject responseObject = new JSONObject(response);
JSONArray responseArray = responseObject.getJSONArray("data");
for(int i = 0; i < responseArray.length(); i++) {
jsonObject = responseArray.getJSONObject(i);
hasRead = jsonObject.getString("has_read");
String avatar = jsonObject.getString("avatar");
String message = jsonObject.getString("message");
String sender = jsonObject.getString("sender");
String timestamp = jsonObject.getString("timestamp");
Log.d(TAG, "sender: "+sender+", hasRead: "+hasRead);
if(avatar.contains("../users")) {
String substring = avatar.substring(avatar.indexOf(".") + 2);
avatar = baseUrl+substring;
Log.d(TAG, "if(avatar.contains('../users'): "+avatar);
} else {
avatar = defaultbaseUrl;
}
list.add(new Message(sender, message, avatar, timestamp));
}
updateData();
} catch(Exception e) {
Log.d(TAG, "EXCEPTION: "+e.getMessage());
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "VolleyError: "+error.getMessage());
messagesProgressDialog.dismiss();
}
}) {
#Override
public Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("username", username);
return params;
}
};
messagesQueue.add(messagesRequest);
return list;
}
/*
Called with TimerTask
*/
private void refreshMessages() {
RequestQueue updateQueue = Volley.newRequestQueue(this);
StringRequest updateRequest = new StringRequest(Request.Method.POST, getMessagesUrl, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
list.clear();
try {
String baseUrl = "https://myURL.com";
String defaultbaseUrl = "https://myURL.com/css/images/user_default/default_avatar.png";
JSONObject updateObject = new JSONObject(response);
JSONArray updateResponseArray = updateObject.getJSONArray("data");
for (int i = 0; i < updateResponseArray.length(); i++) {
updateObject = updateResponseArray.getJSONObject(i);
hasRead = updateObject.getString("has_read");
String avatar = updateObject.getString("avatar");
String message = updateObject.getString("message");
String sender = updateObject.getString("sender");
String timestamp = updateObject.getString("timestamp");
if (avatar.contains("../users")) {
String substring = avatar.substring(avatar.indexOf(".") + 2);
avatar = baseUrl + substring;
} else {
avatar = defaultbaseUrl;
}
if(hasRead.equals("no")) {
}
list.add(new Message(sender, message, avatar, timestamp));
}
updateData();
adapter.notifyDataSetChanged();
} catch(Exception e) {
Log.d(TAG, "EXCEPTION: "+e.getMessage());
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "VolleyError: "+error.getMessage());
}
}) {
#Override
public Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("username", username);
return params;
}
};
updateQueue.add(updateRequest);
}
#Override
public void onBackPressed() {
super.onBackPressed();
list.clear();
updateTimer.cancel();
updateTimer.purge();
SharedPreferences prefs = MessagesActivity.this.getSharedPreferences("MyPref", MODE_PRIVATE);
prefs.edit().putBoolean("is_logged_in", false).apply();
finish();
}
#Override
protected void onResume() {
super.onResume();
}
public void updateData() {
ArrayList<Message> strs = new ArrayList<>();
for(int i = 0; i < list.size(); i++) {
strs.add(list.get(i));
MessageAdapter updateAdapter = new MessageAdapter(list);
messages = findViewById(R.id.messages);
messages.setAdapter(updateAdapter);
updateAdapter.setOnItemClickListener(this);
}
}
#Override
public void onItemClick(int position) {
Log.d(TAG, "onItemClick Called");
Intent conversationIntent = new Intent(MessagesActivity.this, ConversationActivity.class);
String recipient = ((TextView) Objects.requireNonNull(messages.findViewHolderForAdapterPosition(position)).itemView.findViewById(R.id.message_item_username_textview)).getText().toString();
conversationIntent.putExtra("recipient", recipient);
conversationIntent.putExtra("username", username);
updateTimer.cancel();
updateTimer.purge();
startActivity(conversationIntent);
list.clear();
finish();
}
public void getProfile(final String username) {
final String username_url = "https://myURL.com/get_profile.php?username=" + username;
RequestQueue queue = Volley.newRequestQueue(this);
StringRequest postRequest = new StringRequest(Request.Method.GET, username_url,
new com.android.volley.Response.Listener<String>() {
#Override
public void onResponse(String response) {
String baseUrl = "https://myURL.com";
String defaultbaseUrl = "https://myURL.com/css/images/user_default/default_avatar.png";
try {
profile_array = new JSONArray(response);
for(int i = 0; i < profile_array.length(); i++) {
Log.d(TAG, "getProfile inside for loop");
JSONObject jsonObject = profile_array.getJSONObject(i);
String name = jsonObject.getString("name");
String avatarString = jsonObject.getString("avatar");
Log.d(TAG, "getProfile avatarString: "+avatarString);
if(avatarString.contains("../users")) {
String substring = avatarString.substring(avatarString.indexOf(".") + 2);
Log.d(TAG, "substring: "+substring);
String urlString = baseUrl +substring;
Picasso.get().load(urlString).into(circle_avatar);
Log.d(TAG, "urlString: "+urlString);
//Log.d(TAG, "CircleImageView getDrawable(): "+avatar.getDrawable());
header_username_text.setText(name);
} else {
Picasso.get().load(defaultbaseUrl).into(circle_avatar);
header_username_text.setText(name);
}
}
} catch (JSONException e) {
ToastMaker.createLongToast(getApplicationContext(), "JSONException getProfile: "+ e.getMessage());
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
// error
Log.d(TAG, "VolleyError: "+error.getMessage());
//Toast.makeText(getApplicationContext(), TAG + " " + error, Toast.LENGTH_LONG).show();
}
}
) {
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
return params;
}
};
queue.add(postRequest);
}
public void showLogoutDialog() {
AlertDialog.Builder logoutBuilder = new AlertDialog.Builder(this, R.style.Custom_Alert_Dialog);
logoutBuilder.setIcon(R.mipmap.ic_launcher_foreground);
logoutBuilder.setTitle("Confirm Logout");
logoutBuilder.setMessage("You are about to logout.\nAre you sure?")
.setCancelable(true)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
SharedPreferences prefs = MessagesActivity.this.getSharedPreferences("MyPref", MODE_PRIVATE);
prefs.edit().putBoolean("is_logged_in", false).apply();
Intent logoutIntent = new Intent(MessagesActivity.this, LoginActivity.class);
startActivity(logoutIntent);
finish();
}
}).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
AlertDialog alertDialog = logoutBuilder.create();
alertDialog.show();
}
private void configureNavigationDrawer() {
mDrawerLayout = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.navigation_view);
circle_avatar = navigationView.getHeaderView(0).findViewById(R.id.profile_circle_avatar);
header_username_text = navigationView.getHeaderView(0).findViewById(R.id.header_username_text);
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.edit_profile:
//String name = username_heading.getText().toString();
//String pic = avatar.getDrawable().toString();
//String location = location_heading.getText().toString();
//String interests = interests_heading.getText().toString();
//editProfile(name, pic, location, interests);
Log.d(TAG, "Clicked Edit Profile");
break;
case R.id.messages:
Log.d(TAG, "Clicked Messages");
mDrawerLayout.closeDrawers();
break;
case R.id.privacy:
Intent privacyIntent = new Intent(MessagesActivity.this, PrivacyActivity.class);
startActivity(privacyIntent);
Log.d(TAG, "Clicked Privacy");
break;
case R.id.logout:
showLogoutDialog();
Log.d(TAG, "Clicked Logout Button");
break;
}
mDrawerLayout.closeDrawers();
return false;
}
});
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
switch(itemId) {
case android.R.id.home:
mDrawerLayout.openDrawer(GravityCompat.START);
return true;
}
return true;
}
private void configureToolbar() {
Toolbar toolbar = findViewById(R.id.custom_toolbar);
setSupportActionBar(toolbar);
ActionBar actionBar = getSupportActionBar();
assert actionBar != null : Log.d(TAG, "actionBar is NULL");
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setHomeAsUpIndicator(R.drawable.ic_action_menu_24dp);
actionBar.setDisplayHomeAsUpEnabled(true);
}
#Override
protected void onRestart() {
super.onRestart();
updateTimer = new Timer();
TimerTask updateTimerTask = new TimerTask() {
public void run() {
refreshMessages();
}
};
updateTimer.scheduleAtFixedRate(updateTimerTask, 3000, 3000);
}
}
Here is the array that is being sent from the webserver:
$data[] = array('has_read' => 'no', 'avatar' => $avatar, 'sender' => $sender, 'recipient' => $recipient, 'message' => $message, 'timestamp' => $time);
$response = array('data'=>$data, 'count'=>$count);
echo json_encode($response);
If you need only one Boolean throughout your app, here is a way to access the Boolean value:
Create a separate file named ext for example and inside, make this interface:
public interface ext {
Boolean hasRead = false;
}
This variable is made when the app runs and lives for as long as the app does. so you can change it whenever you want.
If you want to have 2 separate Booleans for the two users chatting you can use a Hashmap like so:
HashMap <String, Boolean> readStatus = new HashMap<>();
Update 2
Guess what? It was a typo in another php function not mentioned in my question called getConversation(). I stopped working on the file a while back to perfect my MessagesActivity. I logged any Exceptions on the Activity, and noticed Exception in getMessages(): No value for timestamp.
I had misspelled my timestamp variable. Lol. So, I have a fully functioning messaging system now! :-) Now to work on some notification bells and whistles. Have a great weekend.
Update: This is not completely solved. It's
bold when it's initially received on the MessagesActivity screen, but it goes back to regular Typeface once they refresh (via the TimerTask). Will debug for a while and come back, unless one of you discovers it first. :)
I think I figured it out!
My table is like so:
user1, user1ID, user2, user2ID, message, timestamp, user1read, user2read
When I make my request for a message, I receive these items via the php script:
if($sender === $username) {
$data[] = array('has_read'=>$user1read, 'avatar'=>$avatar, 'sender'=>$recipient, 'message'=>'You: '.$message, 'timestamp'=>$time);
} else {
$data[] = array('has_read' => $user2read, 'avatar' => $avatar, 'sender' => $sender, 'recipient' => $recipient, 'message' => $message, 'timestamp' => $time);
}
This was my original loop to get all the messages from my JSONObject:
for (int i = 0; i < updateResponseArray.length(); i++) {
updateObject = updateResponseArray.getJSONObject(i);
String hasRead = updateObject.getString("has_read");
String avatar = updateObject.getString("avatar");
String message = updateObject.getString("message");
String sender = updateObject.getString("sender");
String timestamp = updateObject.getString("timestamp");
if (avatar.contains("../users")) {
String substring = avatar.substring(avatar.indexOf(".") + 2);
avatar = baseUrl + substring;
} else {
avatar = defaultbaseUrl;
}
list.add(new Message(sender, message, avatar, timestamp));
So, I thought, "why don't I just create another member variable for my Messages model, and call it "hasRead"? So I did just that:
public class Message {
private String username;
private String messageSnippet;
private String avatarUrl;
private String timestamp;
private String hasRead; //<--- here we go
Then I make my getters and setters (leaving out for the sake of brevity).
#J7bits suggested I access the onBindViewHolder() in my custom Adapter class. So I did that, but I included logic to check the status of the new member variable "hasRead":
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
Message message = messages.get(position);
holder.username.setText(message.getUsername());
holder.messageSnippet.setText(message.getMessageSnippet());
holder.singleMessageTimestamp.setText(message.getTimestamp());
Picasso.get().load(message.getAvatarUrl()).into(holder.avatarCircleImageView);
if(message.getHasRead().equals("no")) {
holder.messageSnippet.setTypeface(null, Typeface.BOLD_ITALIC);
}
}
Then, finally, I changed my JSON onResponse method to include the has_read variable sent from php:
String hasRead = jsonObject.getString("has_read");
And I added it to my ArrayList<Message> list:
list.add(new Message(sender, message, avatar, timestamp, **hasRead**);
I entered a new message manually into the database, and set my status to "no" for user2read. And would you just look at it?!?! :D
I've recently been learning about how to use the Kafka Streams client and one thing that I've been struggling with is how to switch from the default state store (RocksDB) to a custom state store using something like Redis. The Confluent documentation makes it clear you have to implement the StateStore interface for your custom store and you must provide an implementation of StoreBuilder for creating instances of that store.
Here's what I have so far for my custom store. I've also added a simple write method to append a new entry into a specified stream via the Redis XADD command.
public class MyCustomStore<K,V> implements StateStore, MyWriteableCustomStore<K,V> {
private String name;
private volatile boolean open = false;
private boolean loggingEnabled = false;
public MyCustomStore(String name, boolean loggingEnabled) {
this.name = name;
this.loggingEnabled = loggingEnabled;
}
#Override
public String name() {
return this.name;
}
#Override
public void init(ProcessorContext context, StateStore root) {
if (root != null) {
// register the store
context.register(root, (key, value) -> {
write(key.toString(), value.toString());
});
}
this.open = true;
}
#Override
public void flush() {
// TODO Auto-generated method stub
}
#Override
public void close() {
// TODO Auto-generated method stub
}
#Override
public boolean persistent() {
// TODO Auto-generated method stub
return true;
}
#Override
public boolean isOpen() {
// TODO Auto-generated method stub
return false;
}
#Override
public void write(String key, String value) {
try(Jedis jedis = new Jedis("localhost", 6379)) {
Map<String, String> hash = new HashMap<>();
hash.put(key, value);
jedis.xadd("MyStream", StreamEntryID.NEW_ENTRY, hash);
}
}
}
public class MyCustomStoreBuilder implements StoreBuilder<MyCustomStore<String,String>> {
private boolean cached = true;
private String name;
private Map<String,String> logConfig=new HashMap<>();
private boolean loggingEnabled;
public MyCustomStoreBuilder(String name, boolean loggingEnabled){
this.name = name;
this.loggingEnabled = loggingEnabled;
}
#Override
public StoreBuilder<MyCustomStore<String,String>> withCachingEnabled() {
this.cached = true;
return this;
}
#Override
public StoreBuilder<MyCustomStore<String,String>> withCachingDisabled() {
this.cached = false;
return null;
}
#Override
public StoreBuilder<MyCustomStore<String,String>> withLoggingEnabled(Map<String, String> config) {
loggingEnabled=true;
return this;
}
#Override
public StoreBuilder<MyCustomStore<String,String>> withLoggingDisabled() {
this.loggingEnabled = false;
return this;
}
#Override
public MyCustomStore<String,String> build() {
return new MyCustomStore<String,String>(this.name, this.loggingEnabled);
}
#Override
public Map<String, String> logConfig() {
return logConfig;
}
#Override
public boolean loggingEnabled() {
return loggingEnabled;
}
#Override
public String name() {
return name;
}
}
And here's what my setup and topology look like.
#Bean
public KafkaStreams kafkaStreams(KafkaProperties kafkaProperties) {
final Properties props = new Properties();
props.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers());
props.put(StreamsConfig.APPLICATION_ID_CONFIG, appName);
props.put(AbstractKafkaSchemaSerDeConfig.SCHEMA_REGISTRY_URL_CONFIG, schemaRegistryUrl);
props.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Long().getClass());
props.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, Double().getClass());
props.put(StreamsConfig.STATE_DIR_CONFIG, "data");
props.put(StreamsConfig.APPLICATION_SERVER_CONFIG, appServerConfig);
props.put(JsonDeserializer.VALUE_DEFAULT_TYPE, JsonNode.class);
props.put(DEFAULT_DESERIALIZATION_EXCEPTION_HANDLER_CLASS_CONFIG, LogAndContinueExceptionHandler.class);
props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
final String storeName = "the-custome-store";
Topology topology = new Topology();
// Create CustomStoreSupplier for store name the-custom-store
MyCustomStoreBuilder customStoreBuilder = new MyCustomStoreBuilder(storeName, false);
topology.addSource("input","inputTopic");
topology.addProcessor("redis-processor", () -> new RedisProcessor(storeName), "input");
topology.addStateStore(customStoreBuilder, "redis-processor");
KafkaStreams kafkaStreams = new KafkaStreams(topology, props);
kafkaStreams.start();
return kafkaStreams;
}
public class MyCustomStoreType<K,V> implements QueryableStoreType<MyReadableCustomStore<String,String>> {
#Override
public boolean accepts(StateStore stateStore) {
return stateStore instanceof MyCustomStore;
}
#Override
public MyReadableCustomStore<String,String> create(final StateStoreProvider storeProvider, final String storeName) {
return new MyCustomStoreTypeWrapper<>(storeProvider, storeName, this);
}
}
public class MyCustomStoreTypeWrapper<K,V> implements MyReadableCustomStore<K,V> {
private final QueryableStoreType<MyReadableCustomStore<String, String>> customStoreType;
private final String storeName;
private final StateStoreProvider provider;
public MyCustomStoreTypeWrapper(final StateStoreProvider provider,
final String storeName,
final QueryableStoreType<MyReadableCustomStore<String, String>> customStoreType) {
this.provider = provider;
this.storeName = storeName;
this.customStoreType = customStoreType;
}
#Override
public String read(String key) {
try (Jedis jedis = new Jedis("localhost", 6379)) {
StreamEntryID start = new StreamEntryID(0, 0);
StreamEntryID end = null; // null -> until the last item in the stream
int count = 2;
List<StreamEntry> list = jedis.xrange("MyStream", start, end, count);
if (list != null) {
// Get the most recently added item, which is also the last item
StreamEntry streamData = list.get(list.size() - 1);
return streamData.toString();
} else {
System.out.println("No new data in the stream");
}
return "";
}
}
}
// This throws the InvalidStateStoreException when I try to get access to the custom store
MyReadableCustomStore<String,String> store = streams.store("the-custome-store", new MyCustomStoreType<String,String>());
String value = store.read("testKey");
So, my question is how do I actually get the state store data to persist into Redis now? I feel like I'm missing something in the state store initialization or with the StateRestoreCallback. Any help or clarification with this would be greatly appreciated.
It looks to me that you have the store wired up to the topology correctly. But you don't have any processors using the store.
It could look something like this:
final String storeName = "the-custome-store";
MyCustomStoreBuilder customStoreBuilder = new MyCustomStoreBuilder(storeName, false);
Topology topology = new Topology()
topology.addSource("input", "input-topic");
// makes the processor a child of the source node
// the source node forwards its records to the child processor node
topology.addProcessor("redis-processor", () -> new RedisProcessor(storeName), "input");
// add the store and specify the processor(s) that access the store
topology.addStateStore(storeBuilder, "redis-processor");
class RedisProcessor implements Processor<byte[], byte[]> {
final String storeName;
MyCustomStore<byte[],byte[]> stateStore;
public RedisProcessor(String storeName) {
this.storeName = storeName;
}
#Override
public void init(ProcessorContext context) {
stateStore = (MyCustomeStore<byte[], byte[]>) context.getStateStore(storeName);
}
#Override
public void process(byte[] key, byte[] value) {
stateStore.write(key, value);
}
#Override
public void close() {
}
}
HTH, and let me know how it works out for you.
Update to answer from comments:
I think you need to update MyCustomStore.isOpen() to return the open variable.
Right now it's hardcoded to return false
Override
public boolean isOpen() {
// TODO Auto-generated method stub
return false;
}
So i want to implement application which reads data from json format files. And I have created customer object for the data in json. And I want to send these object through kafka topic. So far i have successfully send String message to producer to consumer. But when i try to send object, in the consumer side, when I do .value().toString(). I got null value. The following is the code I have used:
This is producer:
public class MyProducer {
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
properties.put("bootstrap.servers", "kafka.kafka-cluster-shared.non-prod-5-az-scus.prod.us.walmart.net:9092");
properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
properties.put("value.serializer", "xxxxxxxxx.KafkaJsonSerializer");
properties.put("acks", "1");
properties.put("retries", "2");
properties.put("batch.size", "16384");
properties.put("linger.ms", "1");
properties.put("buffer.memory", "33554432");
KafkaProducer<String, pharmacyData> kafkaProducer = new KafkaProducer<String, pharmacyData>(
properties);
String topic = "insights";
//try {
Gson gson = new Gson();
Reader reader = Files.newBufferedReader(Paths.get("......./part.json"));
List<pharmacyData> pdata = new Gson().fromJson(reader, new TypeToken<List<pharmacyData>>() {}.getType());
//pdata.forEach(System.out::println);
reader.close();
//} catch (Exception e) {
//e.printStackTrace();
//}
for (pharmacyData data : pdata) {
kafkaProducer.send(new ProducerRecord<String, pharmacyData>(topic, data), new Callback() {
#Override
public void onCompletion(RecordMetadata recordMetadata, Exception e) {
if (e == null) {
System.out.println(recordMetadata.partition() + "--" + recordMetadata.serializedValueSize());
} else {
e.printStackTrace();
}
}
});
}
kafkaProducer.close();
}
}
This is the customer object class:
public class pharmacyData {
private String load_date;
private String store_nbr;
private String state;
private String pmp_flag;
private String zero_flag;
private String submit_ts;
public pharmacyData(String load_date, String store_nbr, String state, String pmp_flag, String zero_flag, String submit_ts) {
this.load_date = load_date;
this.store_nbr = store_nbr;
this.state = state;
this.pmp_flag = pmp_flag;
this.zero_flag = zero_flag;
this.submit_ts = submit_ts;
}
public String getLoad_date() {
return load_date;
}
public void setLoad_date(String load_date) {
this.load_date = load_date;
}
public String getStore_nbr() {
return store_nbr;
}
public void setStore_nbr(String store_nbr) {
this.store_nbr = store_nbr;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getPmp_flag() {
return pmp_flag;
}
public void setPmp_flag(String pmp_flag) {
this.pmp_flag = pmp_flag;
}
public String getZero_flag() {
return zero_flag;
}
public void setZero_flag(String zero_flag) {
this.zero_flag = zero_flag;
}
public String getSubmit_ts() {
return submit_ts;
}
public void setSubmit_ts(String submit_ts) {
this.submit_ts = submit_ts;
}
#Override
public String toString() {
return "pharmacyData{" +
"load_date='" + load_date + '\'' +
", store_nbr='" + store_nbr + '\'' +
", state='" + state + '\'' +
", pmp_flag='" + pmp_flag + '\'' +
", zero_flag='" + zero_flag + '\'' +
", submit_ts='" + submit_ts + '\'' +
'}';
}
}
this is the customer Serializer:
public class KafkaJsonSerializer implements Serializer {
private Logger logger = LogManager.getLogger(this.getClass());
#Override
public void configure(Map map, boolean b) {
}
#Override
public byte[] serialize(String s, Object o) {
byte[] retVal = null;
ObjectMapper objectMapper = new ObjectMapper();
try {
retVal = objectMapper.writeValueAsBytes(o);
} catch (Exception e) {
logger.error(e.getMessage());
}
return retVal;
}
#Override
public void close() {
}
}
This is the customer Deserializer:
public class KafkaJsonDeserializer implements Deserializer {
#Override
public void configure(Map map, boolean b) {
}
#Override
public Object deserialize(String s, byte[] bytes) {
ObjectMapper mapper = new ObjectMapper();
pharmacyData pdata = null;
try {
pdata = mapper.readValue(bytes, pharmacyData.class);
} catch (Exception e) {
e.printStackTrace();
}
return pdata;
}
#Override
public void close() {
}
}
This is consumer:
public class MyConsumer {
public static void main(String[] args) {
Properties properties = new Properties();
properties.put("bootstrap.servers", "kafka.kafka-cluster-shared.non-prod-5-az-scus.prod.us.walmart.net:9092");
properties.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
properties.put("value.deserializer", "xxxxxxxx.KafkaJsonDeserializer");
properties.put("group.id", "consumer-group-1");
properties.put("enable.auto.commit", "true");
properties.put("auto.commit.interval.ms", "1000");
properties.put("auto.offset.reset", "earliest");
properties.put("session.timeout.ms", "30000");
KafkaConsumer<String, pharmacyData> consumer = new KafkaConsumer<>(properties);
String topic = "insights";
consumer.subscribe(Collections.singletonList(topic));
while (true) {
ConsumerRecords<String, pharmacyData> consumerRecords = consumer.poll(100);
for (ConsumerRecord<String, pharmacyData> consumerRecord : consumerRecords) {
System.out.println(consumerRecord.key() + "--" + consumerRecord.toString());
//System.out.println(consumerRecord.offset() + "--" + consumerRecord.partition());
}
}
}
}
Can someone please help me with the issues? Thank you very much!
Problem solved:
The solution of this issue just add a default constructor as below:
public pharmacyData() {
}
See this page for more details.
I have an enum type field called Title.
[Serializable]
public enum Title
{
NotSet,
Miss = 4,
Mr = 1,
Mrs = 3,
Ms = 2
}
I want to bind a property of type Title to the Razor View but I don't want it to be a required field. However, on tabbing out or OnBlur, it is showing as required, although I have not specified this as required.
Is there any way I can get around this?
create
namespace YourApplicationName.Helper
{
public class ModelValueListProvider : IEnumerable<SelectListItem>
{
List<KeyValuePair<string, string>> innerList = new List<KeyValuePair<string, string>>();
public static readonly ModelValueListProvider TitleList = new TitleListProvider();
protected void Add(string value, string text)
{
string innerValue = null, innerText = null;
if (value != null)
innerValue = value.ToString();
if (text != null)
innerText = text.ToString();
if (innerList.Exists(kvp => kvp.Key == innerValue))
throw new ArgumentException("Value must be unique", "value");
innerList.Add(new KeyValuePair<string, string>(innerValue, innerText));
}
public IEnumerator<SelectListItem> GetEnumerator()
{
return new ModelValueListProviderEnumerator(innerList.GetEnumerator());
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private struct ModelValueListProviderEnumerator : IEnumerator<SelectListItem>
{
private IEnumerator<KeyValuePair<string, string>> innerEnumerator;
public ModelValueListProviderEnumerator(IEnumerator<KeyValuePair<string, string>> enumerator)
{
innerEnumerator = enumerator;
}
public SelectListItem Current
{
get
{
var current = innerEnumerator.Current;
return new SelectListItem { Value = current.Key, Text = current.Value };
}
}
public void Dispose()
{
try
{
innerEnumerator.Dispose();
}
catch (Exception)
{
}
}
object System.Collections.IEnumerator.Current
{
get
{
return Current;
}
}
public bool MoveNext()
{
return innerEnumerator.MoveNext();
}
public void Reset()
{
innerEnumerator.Reset();
}
}
private class TitleListProvider : ModelValueListProvider
{
public TitleListProvider (string defaultText = null)
{
if (!string.IsNullOrEmpty(defaultText))
Add(string.Empty, defaultText);
Add(Title.NotSet, "NotSet");
Add(Title.Miss , "Miss");
Add(Title.Mr , "Mr");
Add(Title.Mrs , "Mrs");
Add(Title.MS, "MS");
}
public void Add(Title value, string text)
{
Add(value.ToString("d"), text);
}
}
}
}
in your model
public Title? Titleformation { get; set; }
public string[] SelectedTitle { get; set; }
in your view, also add the name space to your view
#using YourApplicationName.Helper;
#Html.ListBoxFor(m => m.SelectedTitle , new SelectList(ModelValueListProvider.TitleList, "Value", "Text"))
hope this help you
Enums require values, and cannot be null (aka not set) despite what someone commented above. What I do for salutations is have a "none" member of the enum, and whenever I print this out, I just check in the code to see if the value of the enum is > 0 (aka, the none option) and don't print it.
public enum Salutation { none,
[Description("Mr.")] Mr,
[Description("Mrs.")] Mrs,
[Description("Ms.")]Ms,
[Description("Miss")] Miss }
Use a class rather than enum ie:
public class Title
{
NotSet;
Miss = 4;
Mr = 1;
Mrs = 3;
Ms = 2;
}
I'm trying to deserialize JSON from Reddit that you can obtain by appending .json to the url. An example would be:
http://www.reddit.com/r/pics/comments/1wvx52/.json?sort=top
However, I am getting the error message:
Exception in thread "main" com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 9765
At line 1 column 9765 in the json there is the following code: "replies": "", whereas normally this would contain an object like this: replies: {
kind: "Listing",
data: {}
},
Does this mean that the json is a String when there is no data, but an object otherwise? How can I deserialize with gson properly if this is the case? I've included my classes below. I still need to figure out how to handle the json starting off with an array of basically two different objects (the first listing in the json is describing the link, while the second listing is describing the comments), but I'll cross that bridge when I get there. Thanks in advance if anyone can shed some light on this issue.
Main Class
public static void main(String[] args)
{
ArrayList<CommentsResults> commentsResults = new ArrayList<CommentsResults>();
String commentsURL = "http://www.reddit.com/r/pics/comments/1wvx52/.json?sort=top";
URL url = null;
try
{
url = new URL(commentsURL);
} catch (MalformedURLException ex)
{
System.out.println(ex.getMessage());
}
try
{
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(url.openStream()));
String jsonText = readAll(bufferedReader);
Gson gson = new GsonBuilder().create();
commentsResults = gson.fromJson(jsonText, new TypeToken<ArrayList<CommentsResults>>(){}.getType());
} catch (IOException ex)
{
System.out.println(ex.getMessage());
}
}
private static String readAll(Reader reader) throws IOException
{
StringBuilder stringBuilder = new StringBuilder();
int cp;
while ((cp = reader.read()) != -1)
{
stringBuilder.append((char) cp);
}
return stringBuilder.toString();
}
CommentsResults Class
public class CommentsResults {
private String kind;
private CommentsData data;
public CommentsResults()
{
}
public CommentsResults(String kind, CommentsData data)
{
this.kind = kind;
this.data = data;
}
public String getKind()
{
return kind;
}
public CommentsData getData()
{
return data;
}
public void setKind(String kind)
{
this.kind = kind;
}
public void setData(CommentsData data)
{
this.data = data;
}
}
CommentsData Class
private String modhash;
private List <CommentsChild> children;
public CommentsData()
{
}
public CommentsData(String modhash, List<CommentsChild> children)
{
this.modhash = modhash;
this.children = children;
}
public String getModhash()
{
return modhash;
}
public List<CommentsChild> getChildren()
{
return children;
}
public void setModhash(String modhash)
{
this.modhash = modhash;
}
public void setChildren(List<CommentsChild> children)
{
this.children = children;
}
CommentsChild Class
private String kind;
private Comment data;
public CommentsChild()
{
}
public CommentsChild(String kind, Comment comment)
{
this.kind = kind;
this.data = comment;
}
public String getKind()
{
return kind;
}
public Comment getComment()
{
return data;
}
public void setKind(String kind)
{
this.kind = kind;
}
public void setComment(Comment comment)
{
this.data = comment;
}
Comment Class
public class Comment {
private CommentsResults replies;
private String id;
private int gilded;
private String author;
private String parent_id;
private String body;
private int downs;
private String link_id;
private boolean score_hidden;
private int created_utc;
private String distinguished;
public Comment()
{
}
public Comment(CommentsResults replies, String id, int gilded, String author, String parent_id, String body, int downs, String link_id, boolean score_hidden, int created_utc, String distinguished)
{
this.replies = replies;
this.id = id;
this.gilded = gilded;
this.author = author;
this.parent_id = parent_id;
this.body = body;
this.downs = downs;
this.link_id = link_id;
this.score_hidden = score_hidden;
this.created_utc = created_utc;
this.distinguished = distinguished;
}
public CommentsResults getReplies()
{
return replies;
}
public String getId()
{
return id;
}
public int getGilded()
{
return gilded;
}
public String getAuthor()
{
return author;
}
public String getParent_id()
{
return parent_id;
}
public String getBody()
{
return body;
}
public int getDowns()
{
return downs;
}
public String getLink_id()
{
return link_id;
}
public boolean isScore_hidden()
{
return score_hidden;
}
public int getCreated_utc()
{
return created_utc;
}
public String getDistinguished()
{
return distinguished;
}
public void setReplies(CommentsResults replies)
{
this.replies = replies;
}
public void setId(String id)
{
this.id = id;
}
public void setGilded(int gilded)
{
this.gilded = gilded;
}
public void setAuthor(String author)
{
this.author = author;
}
public void setParent_id(String parent_id)
{
this.parent_id = parent_id;
}
public void setBody(String body)
{
this.body = body;
}
public void setDowns(int downs)
{
this.downs = downs;
}
public void setLink_id(String link_id)
{
this.link_id = link_id;
}
public void setScore_hidden(boolean score_hidden)
{
this.score_hidden = score_hidden;
}
public void setCreated_utc(int created_utc)
{
this.created_utc = created_utc;
}
public void setDistinguished(String distinguished)
{
this.distinguished = distinguished;
}
}
So in the off chance this helps somebody (which seems dubious at this point) I decided to parse the Json manually using recursion. Here's how I did it:
public static void getCommentsOnLink()
{
String commentsURL= "http://www.reddit.com/r/pics/comments/1wvx52/.json?sort=top";
URL url = null;
try
{
url = new URL(commentsURL);
} catch (MalformedURLException ex)
{
System.out.println(ex.getMessage());
}
String JsonText = readCommentJsonFromURL(url);
RedditCommentResults redditCommentResults = getCommentResults(JsonText);
}
private static String readCommentJsonFromURL(URL url)
{
String JSONText = null;
try
{
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(url.openStream()));
JSONText = readAll(bufferedReader);
} catch (IOException ex)
{
System.out.println(ex.getMessage());
}
return JSONText;
}
private static String readAll(Reader reader) throws IOException
{
StringBuilder stringBuilder = new StringBuilder();
int cp;
while ((cp = reader.read()) != -1)
{
stringBuilder.append((char) cp);
}
return stringBuilder.toString();
}
private static RedditCommentResults getCommentResults(String JsonText)
{
JsonParser parser = new JsonParser();
JsonArray completeJson = (JsonArray) parser.parse(JsonText);
//get link and comment object from the array containing an object for each
JsonObject linkParentJson = (JsonObject) completeJson.get(0);
JsonObject commentParentJson = (JsonObject) completeJson.get(1);
//use automatic deserializer for redditLink
JsonObject linkListingDataJson = linkParentJson.getAsJsonObject("data");
JsonObject linkChildrenJson = linkListingDataJson.getAsJsonArray("children").get(0).getAsJsonObject();
JsonObject linkDataJson = linkChildrenJson.getAsJsonObject("data");
Link commentLink = gson.fromJson(linkDataJson, Link.class);
RedditLink redditCommentLink = new RedditLink(commentLink);
//parse comments manually
JsonObject commentDataJson = commentParentJson.getAsJsonObject("data");
JsonArray commentChildrenJson = commentDataJson.getAsJsonArray("children");
//get all of the comments from the JsonArray
ArrayList<RedditComment> redditComments = getNestedComments(commentChildrenJson);
RedditCommentResults redditCommentResults = new RedditCommentResults(redditComments, redditCommentLink);
return redditCommentResults;
}
private static ArrayList<RedditComment> getNestedComments(JsonArray commentWrapperJsonArray)
{
ArrayList<RedditComment> redditComments = new ArrayList();
for (JsonElement commentWrapperJson : commentWrapperJsonArray)
{
//cast Element to Object so we can search for the primitive "kind". Finally we get it as a String
String kind = commentWrapperJson.getAsJsonObject().getAsJsonPrimitive("kind").getAsString();
//if the comment is of type t1 meaning it is a comment and not a "more" (a "more" is a comment which
//hasn't been loaded yet because it does not have a great deal of upvotes relative to other comments)
if (kind.equals("t1"))
{
JsonObject commentJson = commentWrapperJson.getAsJsonObject().getAsJsonObject("data");
Comment comment = gson.fromJson(commentJson, Comment.class);
RedditComment redditComment = new RedditComment(comment);
JsonElement repliesJson = commentJson.get("replies");
//if the reply is not equal to an empty String (i.e. if there is at least one reply)
if (!repliesJson.isJsonPrimitive())
{
JsonObject dataJson = repliesJson.getAsJsonObject().getAsJsonObject("data");
JsonArray childrenJson = dataJson.getAsJsonArray("children");
ArrayList<RedditComment> nestedComments = getNestedComments(childrenJson);
redditComment.setReplies(nestedComments);
}
redditComments.add(redditComment);
}
}
return redditComments;
}
You have to remove the private CommentsResults replies; from Comment Class and compose replies in CommentsChild class. According to Json fomate you model is
CommentResult -
CommentsData --
List <CommentsChild> children---
CommentsResults replies
recursion/ repeation of comment result
public class CommentsChild {
private String kind;
private Comment data;
//
private CommentsResults replies;
}