Nick7 Nick7 - 5 months ago 79
JSON Question

How to specify object custom serialization in ORMLite?

I would like to store some field of type ParentClass as json string into my database. I don't want to use Serializable interface and DataType.SERIALIZABLE cause it ties with full class name of serialized class.

So I'm using the following code:

class ParentClass {

@DatabaseField(persisterClass = MyFieldClassPersister.class)
private MyFieldClass myField;
}


where persister class a kind of:

public class MyFieldClassPersister extends StringType {

private static final MyFieldClassPersister singleTon = new MyFieldClassPersister();

public static MyFieldClassPersister getSingleton() {
return singleTon;
}

protected MyFieldClassPersister() {
super(SqlType.STRING, new Class<?>[0]);
}

@Override
public Object parseDefaultString(FieldType fieldType, String defaultStr) {
return jsonStringToObject(defaultStr);
}

@Override
public Object resultToSqlArg(FieldType fieldType, DatabaseResults results, int columnPos) throws SQLException {
String string = results.getString(columnPos);
return jsonStringToObject(string);
}

private static MyFieldClass jsonStringToObject(String string) {
// json to object conversion logic
}

}


Here are two issues I've met:


  1. I didn't get how to specify custom convertion from object to string. Seems that ORMLite calls Object.toString() in order to get string representation of the object. It would be great to have some method in Persister in which I could specify how to convert Object to string (json in my case). Yes, I can override toString() method in MyFieldClass, but it is more convenient to perform conversion in Persister. Is there any method I could override in order to specify convertion from model object to db-object?

  2. If I mark my custom field type as String type:

    class ParentClass {

    @DatabaseField(dataType = DataType.STRING, persisterClass = MyFieldClassPersister.class)
    private MyFieldClass myField;
    }



then ormlite crashes when saving object with the following message:

java.lang.IllegalArgumentException: Field class com.myapp.venue.MyFieldClass for
field FieldType:name=myField,class=ParentClass is not valid for type
com.j256.ormlite.field.types.StringType@272ed83b, maybe should be
class java.lang.String


It doesn't crash if I omit dataType specification. Can I avoid this crash in some way? It seems to me that it's better to specify types explicitly.

Answer

So basically your persister should be implemented in the next way:

public class MyFieldClassPersister extends StringType {

    private static final MyFieldClassPersister INSTANCE = new MyFieldClassPersister();

    private MyFieldClassPersister() {
        super(SqlType.STRING, new Class<?>[] { MyFieldClass.class });
    }

    public static MyFieldClassPersister getSingleton() {
        return INSTANCE;
    }

    @Override
    public Object javaToSqlArg(FieldType fieldType, Object javaObject) {
        MyFieldClass myFieldClass = (MyFieldClass) javaObject;
        return myFieldClass != null ? getJsonFromMyFieldClass(myFieldClass) : null;
    }

    @Override
    public Object sqlArgToJava(FieldType fieldType, Object sqlArg, int columnPos) {
        return sqlArg != null ? getMyFieldClassFromJson((String) sqlArg) : null;
    }

    private String getJsonFromMyFieldClass(MyFieldClass myFieldClass) {
        // logic here
    }

    private MyFieldClass getMyFieldClassFromJson(String json) {
        // logic here
    }
}

You should register it in onCreate method of your OrmLiteSqliteOpenHelper class

    @Override
    public void onCreate(SQLiteDatabaseHolder holder, ConnectionSource connectionSource) {
            try {
                //...
                DataPersisterManager
                 .registerDataPersisters(MyFieldClassPersister.getSingleton());
            } catch (SQLException e) {
                // log exception
            }
    }

And then you can use it in your model like this:

@DatabaseField(persisterClass = MyFieldClassPersister.class, columnName = "column_name")
protected MyFieldClass myFieldClass;