I need to use two arraylists in my database. Here is the entity class:
@Entity
public class User {
public int getId() {
return id;
}
@NonNull
public ArrayList<String> getValues(){
return values;
}
@NonNull
public ArrayList<String> getDates(){
return dates;
}
@NonNull
public String getType_counter() {
return type_counter;
}
@NonNull
public String getWhere_counter() {
return where_counter;
}
@PrimaryKey(autoGenerate = true)
public int id;
@ColumnInfo(name = "d")
@TypeConverters({Converters.class})
public ArrayList<String> dates; // arraylist here
@ColumnInfo(name = "value")
@TypeConverters({Converters.class})
public ArrayList<String> values; // arraylist here
@ColumnInfo(name = "type")
public String type_counter;
@ColumnInfo(name = "location")
public String where_counter;
}
I have created a TypeConverter class as described in this answer:
public class Converters {
@TypeConverter
public static ArrayList<String> fromString(String value) {
Type listType = new TypeToken<ArrayList<String>>() {}.getType();
return new Gson().fromJson(value, listType);
}
@TypeConverter
public static String fromArrayList(ArrayList<String> list) {
Gson gson = new Gson();
return gson.toJson(list);
}
}
I insert the values in the database this way:
private void saveNewValue(String value, String type, String location){
// Getting current date
Date c = Calendar.getInstance().getTime();
SimpleDateFormat df = new SimpleDateFormat("dd-MMM-yyyy", Locale.getDefault());
String formattedDate = df.format(c);
//Saving new User
userViewModel = ViewModelProviders.of(this).get(UserViewModel.class);
User user = new User();
user.values.add(value);
user.dates.add(formattedDate);
user.type_counter = type;
user.where_counter = location;
userViewModel.insert(user);
Toast.makeText(this, "Successfully saved!", Toast.LENGTH_SHORT).show();
}
I've got one activity with RecyclerView
which has only type and location and when you click on a specific item you go to another activity where another RecyclerView
shows all the values and dates of this specific user (by id).
This is my DAO class:
@Dao
public interface UserDao {
@Query("SELECT * FROM User ")
LiveData<List<User>> getAllValues();
@Insert
void insertValue(User ... users);
@Delete
void deleteValue(User user);
@Query("SELECT location FROM User")
LiveData<String[]> getAllLocations();
@Query("SELECT * from User WHERE id=:id")
LiveData<User> getUserWithId(int id);
}
This is the error I'm getting:
E/AndroidRuntime: FATAL EXCEPTION: arch_disk_io_0
Process: org.tensorflow.lite.examples.detection, PID: 14774
java.lang.RuntimeException: Exception while computing database live data.
at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:92)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 1 path $
at com.google.gson.Gson.fromJson(Gson.java:944)
at com.google.gson.Gson.fromJson(Gson.java:897)
at com.google.gson.Gson.fromJson(Gson.java:846)
at org.tensorflow.lite.examples.detection.Converters.fromString(Converters.java:15)
at org.tensorflow.lite.examples.detection.db.UserDao_Impl$3.call(UserDao_Impl.java:126)
at org.tensorflow.lite.examples.detection.db.UserDao_Impl$3.call(UserDao_Impl.java:109)
at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:90)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
Caused by: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 1 path $
at com.google.gson.stream.JsonReader.beginArray(JsonReader.java:351)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:80)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)
at com.google.gson.Gson.fromJson(Gson.java:932)
at com.google.gson.Gson.fromJson(Gson.java:897)
at com.google.gson.Gson.fromJson(Gson.java:846)
at org.tensorflow.lite.examples.detection.Converters.fromString(Converters.java:15)
at org.tensorflow.lite.examples.detection.db.UserDao_Impl$3.call(UserDao_Impl.java:126)
at org.tensorflow.lite.examples.detection.db.UserDao_Impl$3.call(UserDao_Impl.java:109)
at androidx.room.RoomTrackingLiveData$1.run(RoomTrackingLiveData.java:90)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
D/OpenGLRenderer: endAllActiveAnimators on 0x72aa705400 (RippleDrawable) with handle 0x72a70ea8e0
Could somebody please help me out? Thanks in advance.
I can provide extra info if necessary.
Somehow you are storing data that does not contain the enclosing square brackets [
and ]
around the list (in columns d and or value).
For example, the Type converter should convert that value fields/members to be along the lines of ["26-Jul-2022"]
or for multiple dates ["26-Jul-2022","25-Jul-2022"]
.
The Type Converter fromArrayList
does this as it stands. The following based upon your code (first 5 rows) shows, using App Inspection that is the case:-
However, if the fromArrayList
is modified to instead be :-
@TypeConverter
public static String fromArrayList(ArrayList<String> list) {
Log.d("TYPCONVERTER","Type Converter >>fromArrayList<< invoked. ArrayList had " + list.size() + " elements.");
Gson gson = new Gson();
return gson.toJson(list).replace("[","").replace("]","");
}
then running a test extraction, results in the exception that you are encountering (for the data):-
2022-07-26 10:27:15.328 28959-28959/a.a.so73110961javaroomlisttypeconverters E/AndroidRuntime: FATAL EXCEPTION: main
Process: a.a.so73110961javaroomlisttypeconverters, PID: 28959
java.lang.RuntimeException: Unable to start activity ComponentInfo{a.a.so73110961javaroomlisttypeconverters/a.a.so73110961javaroomlisttypeconverters.MainActivity}: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 2 path $
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3449)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 2 path $
The fix is to ascertain where you are saving a string without the enclosing square brackets (and it is very unlikely the Type Converter)
Changing to use:-
@Query("SELECT * FROM User " +
"WHERE " +
" substr(value,1,1) = '[' " +
"AND substr(value,length(value),1) = ']' " +
"AND substr(d,1,1) = '[' " +
"AND substr(d,length(d),1) = ']' " +
"AND id=:id " +
";")
LiveData<User> getValidUser(int id);
circumvents the failure BUT would return nothing for a User that has the incorrect data stored.
Using App Inspection you could run the following query to ascertain which rows would cause and issue :-
SELECT * FROM user WHERE substr(d,1,1) <> '[' OR substr(d,length(d),1) <> ']' OR substr(value,1,1) <> '[' OR substr(value,length(value),1) <> ']';
e.g.