user1421543
user1421543

Reputation:

Error while trying to cache a HashSet using Android Room Library

I'm willing to try the new Room Library from Android and I met the below error:

Error:(19, 29) error: Cannot figure out how to save this field into database. You can consider adding a type converter for it.

This error refers to the following class member:

private HashSet<String> fruits;

I have the following class:

@Entity(tableName = "SchoolLunches")
public class SchoolLunch {

    @PrimaryKey(autoGenerate = true)
    private int lunchId;

    private boolean isFresh;

    private boolean containsMeat;

    private HashSet<String> fruits;

    public int getLunchId() {
        return lunchId;
    }

    public void setLunchId(int lunchId) {
        this.lunchId = lunchId;
    }

    public boolean isFresh() {
        return isFresh;
    }

    public void setFresh(boolean fresh) {
        isFresh = fresh;
    }

    public boolean isContainsMeat() {
        return containsMeat;
    }

    public void setContainsMeat(boolean containsMeat) {
        this.containsMeat = containsMeat;
    }

    public HashSet<String> getFruits() {
        return fruits;
    }

    public void setFruits(HashSet<String> fruits) {
        this.fruits = fruits;
    }

Also, there is a relative DAO class:

@Dao
public interface SchoolLunchDAO {

    @Query("SELECT * FROM SchoolLunches")
    List<SchoolLunch> getAll();

    @Insert
    void insertAll(SchoolLunch... schoolLunches);

    @Query("DELETE FROM SchoolLunches")
    void deleteAll();
}

Since I'm trying to be a very good developer, I wrote a unit test as follows:

@Test
public void singleEntityTest() {
        HashSet<String> fruitSet = new HashSet<>();
        fruitSet.add("Apple");
        fruitSet.add("Orange");

        SchoolLunch schoolLunch = new SchoolLunch();
        schoolLunch.setContainsMeat(false);
        schoolLunch.setFresh(true);
        schoolLunch.setFruits(fruitSet);

        schoolLunchDAO.insertAll(schoolLunch);

        List<SchoolLunch> schoolLunches = schoolLunchDAO.getAll();
        assertEquals(schoolLunches.size(), 1);

        SchoolLunch extractedSchoolLunch = schoolLunches.get(0);
        assertEquals(false, extractedSchoolLunch.isContainsMeat());
        assertEquals(true, extractedSchoolLunch.isFresh());
        assertEquals(2, extractedSchoolLunch.getFruits().size());
 }

What should I do here?

Upvotes: 2

Views: 1131

Answers (2)

CommonsWare
CommonsWare

Reputation: 1006944

What should I do here?

You could create a type converter, as suggested by the error message. Room does not know how to persist a HashSet<String>, or a Restaurant, or other arbitrary objects.

Step #1: Decide what basic type you want to convert your HashSet<String> into (e.g., a String)

Step #2: Write a class with public static type conversion methods, annotated with @TypeConverter, to do the conversion (e.g., HashSet<String> to String, String to HashSet<String>), in some safe fashion (e.g., use Gson, formatting your String as JSON)

Step #3: Add a @TypeConverters annotation to your RoomDatabase or other scope, to teach Room about your @TypeConverter methods

For example, here are a pair of type converter methods for converting a Set<String> to/from a regular String, using JSON as the format of the String.

  @TypeConverter
  public static String fromStringSet(Set<String> strings) {
    if (strings==null) {
      return(null);
    }

    StringWriter result=new StringWriter();
    JsonWriter json=new JsonWriter(result);

    try {
      json.beginArray();

      for (String s : strings) {
        json.value(s);
      }

      json.endArray();
      json.close();
    }
    catch (IOException e) {
      Log.e(TAG, "Exception creating JSON", e);
    }

    return(result.toString());
  }

  @TypeConverter
  public static Set<String> toStringSet(String strings) {
    if (strings==null) {
      return(null);
    }

    StringReader reader=new StringReader(strings);
    JsonReader json=new JsonReader(reader);
    HashSet<String> result=new HashSet<>();

    try {
      json.beginArray();

      while (json.hasNext()) {
        result.add(json.nextString());
      }

      json.endArray();
    }
    catch (IOException e) {
      Log.e(TAG, "Exception parsing JSON", e);
    }

    return(result);
  }

Upvotes: 3

user1421543
user1421543

Reputation:

I created the following class and now it works. Thank you, CommonsWare!

public class Converters {

    private static final String SEPARATOR = ",";

    @TypeConverter
    public static HashSet<String> fromString(String valueAsString) {
        HashSet<String> hashSet = new HashSet<>();
        if (valueAsString != null && !valueAsString.isEmpty()) {
            String[] values = valueAsString.split(SEPARATOR);
            hashSet.addAll(Arrays.asList(values));
        }
        return hashSet;
    }

    @TypeConverter
    public static String hashSetToString(HashSet<String> hashSet) {
        StringBuilder stringBuilder = new StringBuilder();
        for (String currentElement : hashSet) {
            stringBuilder.append(currentElement);
            stringBuilder.append(SEPARATOR);
        }
        return stringBuilder.toString();
    }

}

Upvotes: 2

Related Questions