Search code examples
androidandroid-room

How to save List<Double> to Room database?


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!


Solution

  • 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