1

I'm trying to save a List of Double to Room database using this Converter class:

public class Converters {

    @TypeConverter
    public static String fromDoubleList(List<Double> doubleList){

        Gson gson = new Gson();
        String json = gson.toJson(doubleList);
        return json;
    }

    public static List<Double> fromString(String value){

        Type listType = new TypeToken<List<Double>>(){}.getType();
        return new Gson().fromJson(value, listType);
    }
}

but I keep getting this message:

error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.

Why isn't the converter class working?

Edit - This is my database:

@Database(entities = {MarketUnit.class}, version = 1)
@TypeConverters({Converters.class})
public abstract class MarketDatabase extends RoomDatabase {

    public static final String DATABASE_NAME = "market_db";

    private static MarketDatabase instance;

    public static MarketDatabase getInstance(final Context context){
        if(instance == null){
            instance = Room.databaseBuilder(
                    context.getApplicationContext(),
                    MarketDatabase.class,
                    DATABASE_NAME
            ).build();
        }
        return instance;
    }

    public abstract MarketDao getMarketDao();

}

I've tried the suggestion from the duplicate question, but it's not working!

AtomicallyBeyond
  • 348
  • 1
  • 15
  • String json = gson.toJson(doubleList); where doubleList is doubleList; You should try something like: String str=doubleList.toString(); String json = gson.toJson(str); – Mark Ezberg Nov 12 '21 at 02:57
  • How are you creating the DB? – Nestor Perez Nov 12 '21 at 02:59
  • You need to add the `@TypeConverters` annotation to the room database class. [Referencing complex data using Room](https://developer.android.com/training/data-storage/room/referencing-data) – Bishan Nov 12 '21 at 03:02
  • Does this answer your question? [Android room persistent library - TypeConverter error of error: Cannot figure out how to save field to database"](https://stackoverflow.com/questions/44582397/android-room-persistent-library-typeconverter-error-of-error-cannot-figure-ou) – Bishan Nov 12 '21 at 03:02
  • I tried the links but they didn't help. – AtomicallyBeyond Nov 12 '21 at 05:35

1 Answers1

2

In short you cannot have an Array/List directly as a column.

What you can do is have a POJO as a holder class and then have the column use that type.

e.g.

class Holder() {
   List<Double> doubleList;
}

and in the Entity class have something like

Holder theColumn;

Noting that the Type Converter would then be from/to Holder

e.g. :-

class Converters {

    @TypeConverter
    public String fromHolder(Holder h) {
        Gson gson = new Gson();
        String json = gson.toJson(h);
        return json;
    }
    @TypeConverter
    public Holder toHolder(String s) {
        return new Gson().fromJson(s, Holder.class);
    }
}

Working Example:

Holder :-

class Holder {
    public List<Double> doubles;
}

MarketUnit :-

@Entity
class MarketUnit {
    @PrimaryKey
    Long id = null;
    Holder doubleList;
}

Converters :-

class Converters {

    @TypeConverter
    public static String fromHolder(Holder h) {
        Gson gson = new Gson();
        String json = gson.toJson(h);
        return json;
    }
    @TypeConverter
    public static Holder toHolder(String s) {
        return new Gson().fromJson(s, Holder.class);
    }
}
  • made static so can be called

MarketDao :-

@Dao
abstract class MarketDao {

    @Insert
    abstract long insert(MarketUnit marketUnit);
    @Query("SELECT * FROM marketunit")
    abstract List<MarketUnit> getAllMarketUnits();

}

MarketDatabase :-

@Database(entities = {MarketUnit.class}, version = 1)
@TypeConverters({Converters.class})
public abstract class MarketDatabase extends RoomDatabase {

    public static final String DATABASE_NAME = "market_db";

    private static MarketDatabase instance;

    public static MarketDatabase getInstance(final Context context){
        if(instance == null){
            instance = Room.databaseBuilder(
                    context.getApplicationContext(),
                    MarketDatabase.class,
                    DATABASE_NAME
            ).allowMainThreadQueries().build();
        }
        return instance;
    }

    public abstract MarketDao getMarketDao();

}
  • .allowMainThreadQueries added for brevity and convenience

Finally an Activity MainActivity for test:-

public class MainActivity extends AppCompatActivity {

    MarketDatabase db;
    MarketDao dao;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        db = MarketDatabase.getInstance(this);
        dao = db.getMarketDao();

        MarketUnit mu1 = new MarketUnit();
        Holder h = new Holder();
        ArrayList<Double> dbls = new ArrayList<>();
        dbls.add(200.01);
        dbls.add(111.111);
        h.doubles = dbls;
        mu1.doubleList = h;
        dao.insert(mu1);

        for(MarketUnit mu: dao.getAllMarketUnits()) {
            Log.d("DBINFO","MarketUnit ID = " + mu.id);
            Log.d("DBINFO","Converted Holder :-\n\t" + Converters.fromHolder(mu.doubleList));
            for(Double d: mu.doubleList.doubles) {
                Log.d("DBINFO","\tDouble is " + d);
            }
        }
    }
}

Result from the Log :-

2021-11-12 16:34:37.776  D/DBINFO: MarketUnit ID = 1
2021-11-12 16:34:37.777  D/DBINFO: Converted Holder :-
        {"doubles":[200.01,111.111]}
2021-11-12 16:34:37.777  D/DBINFO:  Double is 200.01
2021-11-12 16:34:37.777  D/DBINFO:  Double is 111.111

The MarketUnit table using App Inspection :-

enter image description here

MikeT
  • 51,415
  • 16
  • 49
  • 68
  • Tried it, but doesn't work. Still the same message: error: Cannot figure out how to save this field into database. You can consider adding a type converter for it. private DoubleListHolder doubleListHolder = new DoubleListHolder(); – AtomicallyBeyond Nov 12 '21 at 03:36
  • @jamesfields well you must be doing something wrong. Added a working example that shows that it works. – MikeT Nov 12 '21 at 05:50
  • how come your "entity" doesn't have a table name, and shouldn't each field have @ColumnInfo(name = "someName") ? AND shouldn't the MarketDao be an intereface? – AtomicallyBeyond Nov 12 '21 at 06:21
  • I duplicated your working example, it's showing that it's working for me as well, but when I extract the database file from the emulator's director and check with DB Browser (SQLite) there is not table in there? I've even edited your working example to include table name and column names. – AtomicallyBeyond Nov 12 '21 at 07:06
  • @jamesfields table name will be the class name by default, likewise column name will be the variable name. Interface or abstract class for Dao's I prefer the latter as it's more flexible. – MikeT Nov 12 '21 at 07:07
  • 1
    @jamesfields if you look there is likely a file same name as the database but suffixed with -wal. If the -wal file is not 0 bytes and exists then some of the database will be in the -wal file. You could copy both files (also -shm if it exists). More at https://sqlite.org/wal.html – MikeT Nov 12 '21 at 07:09
  • I set @PrimaryKey(autoGenerate = true) to autogenerate in your working example, and it finally populated the table in the emulator directory.... now I have to try and figure out what I'm doing wrong. – AtomicallyBeyond Nov 12 '21 at 07:22