java/scala: faster type-aware serialization of only basic types?
in scala, i have a need to serialize objects that are limited to a small set of basic types: array, list, map, set, int, boolean, etc. i want to be able to serialize and deserialize those in a way that preserves the type information in the serialized format. specifically, if i have serialized an Array[Any], i want to be able to deserialize it and only specify that the resulting object is Array[Any]. that is, i don't want to specify a structure definition for every single thing i'm going to serialize. at the same time it needs to be able to distinguish between int and long, tuple and array, etc.
for example:
val obj = Array[Any](...) // can have any basic开发者_开发技巧 types in here
val ser = serialize(obj)
val newObj = deserialize[Array[Any]](ser) // recovers the exact types from the original obj
json is not appropriate for this case because it has a many-to-one mapping of scala types to json types. i'm currently using java serialization but it's extremely slow. since i don't need to serialize any arbitrary object type, is there a faster alternative for my narrower use case?
I don't about speed or indeed availability of library support, but have you looked at ASN.1?
I'd use a simple interface like this:
public interface Serializer{
public <T> T deserialize(String serializedData);
public String serialize(Object data);
}
And an enum to implement it:
public enum StandardSerializer implements Serializer{
INTEGER("I", Integer.class, int.class){
@Override
protected Integer doDeserialize(final String stripped){
return Integer.valueOf(stripped);
}
},
STRING("I", String.class){
@Override
protected Object doDeserialize(final String stripped){
return stripped;
}
},
LIST("L", List.class){
@Override
protected String doSerialize(final Object data){
final Iterator<?> it = ((List<?>) ((List<?>) data)).iterator();
final StringBuilder sb = new StringBuilder();
if(it.hasNext()){
Object next = it.next();
sb.append(StandardSerializer
.forType(next.getClass())
.serialize(next));
while(it.hasNext()){
sb.append(',');
next = it.next();
sb.append(StandardSerializer
.forType(next.getClass())
.serialize(next));
}
}
return sb.toString();
}
@Override
protected Object doDeserialize(final String stripped){
final List<Object> list = new ArrayList<Object>();
for(final String item : stripped.split(",")){
list.add(StandardSerializer.forData(item).deserialize(item));
}
return list;
}
}
/* feel free to implement more enum entries */
;
private static final String DELIMITER = ":";
public static StandardSerializer forType(final Class<?> type){
for(final StandardSerializer candidate : values()){
for(final Class<?> supportedType : candidate.supportedClasses){
if(supportedType.isAssignableFrom(type)) return candidate;
}
}
throw new IllegalArgumentException("Unmapped type: " + type);
}
private final String prefix;
private final Class<?>[] supportedClasses;
private StandardSerializer(final String prefix,
final Class<?>... supportedClasses){
this.prefix = prefix;
this.supportedClasses = supportedClasses;
}
private String base64decode(final String removePrefix){
// TODO call one of the many base64 libraries here
return null;
}
private String base64encode(final String data){
// TODO call one of the many base64 libraries here
return null;
}
@SuppressWarnings("unchecked")
@Override
public final <T> T deserialize(final String serializedData){
return (T) doDeserialize(base64decode(removePrefix(serializedData)));
}
public static StandardSerializer forData(final String serializedData){
final String prefix =
serializedData.substring(0, serializedData.indexOf(DELIMITER));
for(final StandardSerializer candidate : values()){
if(candidate.prefix.equals(prefix)) return candidate;
}
throw new IllegalArgumentException("Unknown prefix: " + prefix);
}
protected abstract Object doDeserialize(String strippedData);
private String removePrefix(final String serializedData){
return serializedData.substring(prefix.length() + DELIMITER.length());
}
// default implementation calles toString()
protected String doSerialize(final Object data){
return data.toString();
}
@Override
public String serialize(final Object data){
return new StringBuilder()
.append(prefix)
.append(DELIMITER)
.append(base64encode(doSerialize(data)))
.toString();
}
}
Now here's how you can code against that:
List<?> list = Arrays.asList("abc",123);
String serialized = StandardSerializer.forType(list.getClass()).serialize(list);
List<?> unserialized = StandardSerializer.forData(serialized)
.deserialize(serialized);
(While you might choose a different format for serialization, using an enum strategy pattern is probably still a good idea)
精彩评论