Skip to content

Instantly share code, notes, and snippets.

@krzysztof-miemiec
Last active March 5, 2019 11:34
Show Gist options
  • Save krzysztof-miemiec/d6b6449b0e0e56e4a57e4ed7bc367aab to your computer and use it in GitHub Desktop.
Save krzysztof-miemiec/d6b6449b0e0e56e4a57e4ed7bc367aab to your computer and use it in GitHub Desktop.
List parsing done with LoganSquare and Realm database.
/**
* Sample class showing how to use type converter in this gist.
*
* @author Krzysztof Miemiec
*/
@JsonObject
public class Album extends RealmObject {
@PrimaryKey
@JsonField
private String id;
@JsonField(typeConverter = StringRealmListConverter.class)
private RealmList<RealmString> imageUrls;
public RealmList<RealmString> getImageUrls() {
return imageUrls;
}
public void setImageUrls(RealmList<RealmString> imageUrls) {
this.imageUrls = imageUrls;
}
}
/**
* The universal converter for RealmList.
* You can use this with RealmObjects:
* 1. Annotated with @JsonObject
* 2. With custom converters, such as RealmString below.
* @author Krzysztof Miemiec
*/
public class RealmListConverter<T extends RealmObject> implements TypeConverter<RealmList<T>> {
/**
* This is useful if you want to use this converter for sending just a simple part of object back to server.
* For example:
* Server gives you this:
* [{"id": 1, "description": "Doxie!", "image": "http://imgur.com/r/Dachshund/OGKuGTR"},
* {"id": 33, "description": "Second doxie!", "image": "http://imgur.com/r/Dachshund/nU3PlyO"}]
* You send back:
* [1, 33]
*/
public interface SingleFieldSerialization<T> {
Object getValue(T object);
}
private Class<T> clazz;
private TypeConverter<T> typeConverter;
private JsonMapper<T> mapper;
private SingleFieldSerialization<T> serializer;
public RealmListConverter(Class<T> clazz) {
this.clazz = clazz;
}
public RealmListConverter(Class<T> clazz, SingleFieldSerialization<T> serializer){
this.clazz = clazz;
this.serializer = serializer;
}
private void init() {
if (typeConverter == null && mapper == null) {
try {
typeConverter = LoganSquare.typeConverterFor(clazz);
} catch (NoSuchTypeConverterException e) {
mapper = LoganSquare.mapperFor(clazz);
}
}
}
@Override
public RealmList<T> parse(JsonParser jsonParser) throws IOException {
init();
RealmList<T> list = new RealmList<>();
if (jsonParser.getCurrentToken() == JsonToken.START_ARRAY) {
while (jsonParser.nextToken() != JsonToken.END_ARRAY) {
T object = typeConverter != null ? typeConverter.parse(jsonParser) : mapper.parse(jsonParser);
if (object != null) {
list.add(object);
}
}
}
return list;
}
@Override
public void serialize(RealmList<T> list, String fieldName, boolean writeFieldNameForObject, JsonGenerator jsonGenerator) throws IOException {
if (writeFieldNameForObject) {
if (list == null) {
return;
}
jsonGenerator.writeFieldName(fieldName);
}
if (list == null) {
jsonGenerator.writeNull();
return;
}
jsonGenerator.writeStartArray();
if (serializer != null) {
JsonMapper<Object> objectMapper = LoganSquare.mapperFor(Object.class);
for (T object : list) {
if (object != null) {
objectMapper.serialize(serializer.getValue(object), jsonGenerator, true);
} else {
jsonGenerator.writeNull();
}
}
} else {
init();
for (T object : list) {
if (object != null) {
if (typeConverter != null) {
typeConverter.serialize(object, fieldName, false, jsonGenerator);
} else {
mapper.serialize(object, jsonGenerator, true);
}
} else {
jsonGenerator.writeNull();
}
}
}
jsonGenerator.writeEndArray();
}
}
/**
* A wrapper for holding Strings in RealmList. Shame.
*
* @author Krzysztof Miemiec
*/
public class RealmString extends RealmObject {
public RealmString(String string) {
this.string = string;
}
public RealmString() {
}
private String string;
public String getString() {
return string;
}
public void setString(String string) {
this.string = string;
}
}
/**
* A simple converter for RealmString wrapper.
*
* @author Krzysztof Miemiec
*/
public class RealmStringConverter extends StringBasedTypeConverter<RealmString> {
@Override
public RealmString getFromString(String string) {
return new RealmString(string);
}
@Override
public String convertToString(RealmString realmString) {
return realmString.getString();
}
}
/**
* All you need to serialize/deserialize RealmList of RealmStrings.
* It can be used thanks to that line:
* LoganSquare.registerTypeConverter(RealmString.class, new RealmStringConverter());
* Put it in place where you initialize LoganSquare.
*
* @author Krzysztof Miemiec
*/
public class StringRealmListConverter extends RealmListConverter<RealmString> {
public StringRealmListConverter() {
super(RealmString.class);
}
}
@peterbetos
Copy link

I'm getting an error "TypeConverter classes must have a non-private zero argument constructor."

Do you know if we need to add these default constructor?
public RealmListConverter(){
}

@peterbetos
Copy link

When I tried a default constructor, it shows this error:
Class io.realm.UserRealmProxy could not be mapped to a JSON object. Perhaps it hasn't been annotated with @JsonObject?

@peterbetos
Copy link

peterbetos commented Jul 29, 2016

Here's what I did (this is to prevent an additional constructor):

  Class<T> clazz;
    TypeConverter<T> typeConverter;
    JsonMapper<T> mapper;
    protected SingleFieldSerialization<T> singleFieldSerialization;


    private void init() {
        if (typeConverter == null && mapper == null) {
            try {
                typeConverter = LoganSquare.typeConverterFor(getPersistentClass());
            } catch (NoSuchTypeConverterException e) {
                mapper = LoganSquare.mapperFor(getPersistentClass());
            }
        }
    }


    public Class<T> getPersistentClass() {
        if (clazz == null) {
            this.clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        }
        return clazz;
    }

@Vincent-Loi
Copy link

@peterbetos Your solution gives me this error: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.Class.getCanonicalName()' on a null object reference

@meierjan
Copy link

@krzysztof-miemiec
Copy link
Author

@peterbetos, @Vincent-Loi, @meierjan: Unfortunately I haven't found any way to get rid of converters extending RealmListConverter<T>. For each type I had to create corresponding RealmListConverter.
Another solution (time-consuming one, but profitable for community that uses both LoganSquare & Realm) is to create a fork of LoganSquare that depends on Realm and generates code that does all of the above without any custom Type Converters.

@peterbetos
Copy link

peterbetos commented Oct 10, 2016

Whoops, sorry forgot about this. I solved mine using the ParameterizedType implementation. You can refer to my implementation below:
https://gist.github.com/peterbetos/fb42ac87c31accd2efc43e33ddaaecc3

But yeah, @krzysztof-miemiec, no solution so far regarding the extensions.
Unless.... someone will do annotation processing for it? :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment