-
Notifications
You must be signed in to change notification settings - Fork 823
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ability to Tag and Bind specific serializers to fields via annotations #180
Comments
If we allow fields to be annoted to specify the serializer for that field, probably it should be done for FieldSerializer and all similar classes. We'd probably accept a PR for this. I unfortunately don't have a lot of time to work on Kryo. :( |
I agree. It is FieldSerializer and classes derived from it, who are affected. For collection classes we already have APIs to set serializers for their elements and keys/values. So, adding this possibility to the FieldSerializer would make this approach more uniform and symmetric. We may also think about adding special annotations for fields that are collections or maps. Internally they would be mapped to the mentioned existing APIs. E.g. something like
|
In case someone works on this, what name do we want for the annotation? BindSerializer is ok I suppose. Serializer, FieldSerializer, DefaultSerializer are taken. Well, we could allow DefaultSerializer to also target fields, would that make more sense? Or maybe FieldSerializer.BindSerializer (or FieldSerializer.DefaultSerializer?) and MapSerializer.BindSerializer? They aren't the same since MapSerializer needs two. |
I think Field/MapSerializer.BindSerializer is fine, I just want to throw in the idea to make the relation to kryo more clear in the name - thinking of |
Yeah I agree it should be part of the FieldSerializer. FWIW below is what we ended up doing in our code since we are on Kryo 2.22. we needed backward compatibility support as well. The class is TagBindFieldSerializer. open to better name(s) or other updates. Thanks, import java.lang.reflect.Field;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.KryoException;
import com.esotericsoftware.kryo.Serializer;
import com.esotericsoftware.kryo.factories.ReflectionSerializerFactory;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.serializers.FieldSerializer;
import com.esotericsoftware.kryo.serializers.TaggedFieldSerializer;
/**
* The Class TagBindFieldSerializer. Ported from {@link TaggedFieldSerializer}.
* An alternative default serializer for Kryo that also processes {@link Tag}
* and {@link BindKryoSerializer} annotations.
*
* @see FieldSerializer
* @see TaggedFieldSerializer
*
* @param <T>
* the generic type being processed
*/
@SuppressWarnings("rawtypes")
public class TagBindFieldSerializer<T> extends FieldSerializer<T> {
/** The tag indexes of this type T. */
private int[] tags;
/**
* Instantiates a new tag bind field serializer.
*
* @param kryo
* the kryo instance used
* @param type
* the type being processed
*/
public TagBindFieldSerializer(Kryo kryo, Class type) {
super(kryo, type);
}
/**
* Handle {@link Tag} and {@link BindKryoSerializer} annotations. Modifies
* backing fields by removing unwanted fields and setting custom serializers
* if set by {@link BindKryoSerializer}
*
* @see com.esotericsoftware.kryo.serializers.FieldSerializer#initializeCachedFields
* ()
*/
protected void initializeCachedFields() {
CachedField[] fields = getFields();
// Remove unwanted fields.
for (int i = 0, n = fields.length; i < n; i++) {
Field field = fields[i].getField();
Tag tag = field.getAnnotation(Tag.class);
Deprecated deprecated = field.getAnnotation(Deprecated.class);
if (tag == null || deprecated != null) {
super.removeField(field.getName());
continue;
}
// here we capture a specific serializer for a particular field
if (field.isAnnotationPresent(BindKryoSerializer.class)) {
Class<? extends Serializer> serializerClass = field.getAnnotation(BindKryoSerializer.class).value();
Serializer s = ReflectionSerializerFactory.makeSerializer(super.getKryo(), serializerClass,
field.getClass());
fields[i].setSerializer(s);
}
}
// Cache tags.
fields = getFields();
tags = new int[fields.length];
for (int i = 0, n = fields.length; i < n; i++)
tags[i] = fields[i].getField().getAnnotation(Tag.class).value();
}
/**
* Writes the object by inspected modified backing fields. write tag index
* in the beginning.
*
* @see com.esotericsoftware.kryo.serializers.FieldSerializer#write(com.
* esotericsoftware.kryo.Kryo, com.esotericsoftware.kryo.io.Output,
* java.lang.Object)
*/
public void write(Kryo kryo, Output output, T object) {
CachedField[] fields = getFields();
output.writeInt(fields.length, true);
for (int i = 0, n = fields.length; i < n; i++) {
output.writeInt(tags[i], true);
fields[i].write(output, object);
}
}
/**
* Process reads based on modified backing fields. Read tag index and match
* with field tag.
*
* @see com.esotericsoftware.kryo.serializers.FieldSerializer#read(com.
* esotericsoftware.kryo.Kryo, com.esotericsoftware.kryo.io.Input,
* java.lang.Class)
*/
public T read(Kryo kryo, Input input, Class<T> type) {
T object = kryo.newInstance(type);
kryo.reference(object);
int fieldCount = input.readInt(true);
int[] tags = this.tags;
CachedField[] fields = getFields();
for (int i = 0, n = fieldCount; i < n; i++) {
int tag = input.readInt(true);
CachedField cachedField = null;
for (int ii = 0, nn = tags.length; ii < nn; ii++) {
if (tags[ii] == tag) {
cachedField = fields[ii];
break;
}
}
if (cachedField == null)
throw new KryoException("Unknown field tag: " + tag + " (" + getType().getName() + ")");
cachedField.read(input, object);
}
return object;
}
/*
* (non-Javadoc)
*
* @see
* com.esotericsoftware.kryo.serializers.FieldSerializer#removeField(java
* .lang.String)
*/
public void removeField(String fieldName) {
super.removeField(fieldName);
initializeCachedFields();
}
} and the annotations import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.esotericsoftware.kryo.Serializer;
/**
* The Annotation BindKryoSerializer. Used to annotate fields with a specific
* Kryo serializer.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface BindKryoSerializer {
/**
* Value.
*
* @return the class<? extends serializer> used for this field
*/
@SuppressWarnings("rawtypes")
Class<? extends Serializer> value();
} import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* The Interface Tag. Used to annotate fields with an integer index.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Tag {
/**
* Value.
*
* @return the index of this field
*/
int value();
} |
Like TaggedFieldSerializer, but instead also allow an annotation to set a specific serializer annotation.
Something like a change to TaggedFieldSerializer#initializeCachedFields:
Use:
@DefaultSerializer(TaggedBindableFieldSerializer.class)
public class Foo {
@tag(0)
@BindSerializer(UriSerializer.class)
private URI sourceLabel;
}
Also, it would be nice to actually annotate an interface with @DefaultSerializer. Basically the Kryo#getDefaultSerializer can be updated to traverse up the interfaces. I'd be happy to help with this. let me know your thoughts.
The text was updated successfully, but these errors were encountered: