2012-02-14 16 views
14

tengo un modelo interno de datos existente para un Picture, de la siguiente manera:Protocol Buffers y modelos de datos internos

package test.model; 
public class Picture { 

    private int height, width; 
    private Format format; 

    public enum Format { 
    JPEG, BMP, GIF 
    } 

    // Constructor, getters and setters, hashCode, equals, toString etc. 
} 

ahora quiero serializarlo usando protocol buffers. He escrito un archivo Picture.proto que refleja los campos de la clase Picture y compilado el código en el paquete test.model.protobuf con un nombre de clase de PictureProtoBuf:

package test.model.protobuf; 

option java_package = "test.model.protobuf"; 
option java_outer_classname = "PictureProtoBuf"; 

message Picture { 
    enum Format { 
    JPEG = 1; 
    BMP = 2; 
    GIF = 3; 
    } 
    required uint32 width = 1; 
    required uint32 height = 2; 
    required Format format = 3; 
} 

Ahora Ahora estoy asumiendo que si tengo una Picture que Quiero serializar y enviar algún lugar tengo que crear un objeto PictureProtoBuf y mapear todos los campos de ancho, de este modo:

Picture p = new Picture(100, 200, Picture.JPEG); 
PictureProtoBuf.Picture.Builder output = PictureProtoBuf.Picture.newBuilder(); 
output.setHeight(p.getHeight()); 
output.setWidth(p.getWidth()); 

estoy que se despegue cuando tengo una enumeración en mi modelo de datos. La forma más fea que estoy usando en este momento es:

output.setFormat(PictureProtoBuf.Picture.Format.valueOf(p.getFormat().name()); 

Sin embargo, esto es propenso a la rotura y se basa en el nombre de enumeración ser coherente entre mi modelo de datos interno y el modelo de datos del búfer de protocolo (que no es una gran suposición ya que los nombres de enumeración dentro de los archivos .proto deben ser únicos). Puedo ver que tengo que crear manualmente instrucciones de conmutación en enumeraciones si la llamada .name() del modelo interno no coincide con el nombre de enumeración generado por protobuf.

Supongo que mi pregunta es si voy por esto de la manera correcta? ¿Se supone que debo eliminar mi modelo de datos interno (test.model.Picture) a favor del generado por protobuf (test.model.protobuf.PictureProtoBuf)? De ser así, ¿cómo puedo implementar algunas de las sutilezas que he hecho en mi modelo de datos interno (por ejemplo, hashCode(), equals(Object), toString(), etc.)?

+0

No lo he probado (solo porque soy principalmente una persona .NET), pero I * believe * [protostuff] (http://code.google.com/p/protostuff/) le permite seguir trabajando con su modelo existente. –

+0

¡Gracias, lo comprobaré! – Catchwa

+0

@MarcGravell - Gracias por su sugerencia. Tu corazonada era correcta; Protostuff hace exactamente lo que yo quería, pero conserva los búferes de protocolo en el back-end (aunque aún no ha probado su compatibilidad con la biblioteca de protobuf de Google). – Catchwa

Respuesta

3

Aunque las respuestas existentes son buenas, decidí ir un poco más allá con la sugerencia de Marc Gravell de buscar en el material de protección.

Puede utilizar el Protostuff runtime module junto con el OBJECTSCHEMA dinámico para crear esquemas en tiempo de ejecución para su modelo de datos interno

Mi código ahora reduce a:

// Do this once 
private static Schema<Picture> schema = RuntimeSchema.getSchema(Picture.class); 
private static final LinkedBuffer buffer = LinkedBuffer.allocate(DEFAULT_BUFFER_SIZE); 

// For each Picture you want to serialize... 
Picture p = new Picture(100, 200, Picture.JPEG); 
byte[] result = ProtobufIOUtil.toByteArray(p, schema, buffer); 
buffer.clear(); 
return result; 

Esta es una gran mejora con respecto al Google biblioteca de protobuf (vea mi pregunta) cuando tenga muchos y muchos atributos en su modelo de datos internos. Tampoco hay una penalización de velocidad que pueda detectar (¡con mis casos de uso, de todos modos!)

4

Si tiene control sobre su modelo de datos interno, puede modificar test.model.Picture para que los valores enum conozcan su correspondiente equivalente de protobuf, probablemente pasando la correspondencia a sus constructores enum.

Por ejemplo, el uso de Guava'sBiMap (mapa bidireccional con valores únicos), obtenemos algo como

enum ProtoEnum { // we don't control this 
    ENUM1, ENUM2, ENUM3; 
} 

enum MyEnum { 
    ONE(ProtoEnum.ENUM1), TWO(ProtoEnum.ENUM2), THREE(ProtoEnum.ENUM3); 

    static final ImmutableBiMap<MyEnum, ProtoEnum> CORRESPONDENCE; 

    static { 
    ImmutableBiMap.Builder<ProtoEnum, MyEnum> builder = ImmutableBiMap.builder(); 
    for (MyEnum x : MyEnum.values()) { 
     builder.put(x.corresponding, x); 
    } 
    CORRESPONDENCE = builder.build(); 
    } 

    private final ProtoEnum corresponding; 

    private MyEnum(ProtoEnum corresponding) { 
    this.corresponding = corresponding; 
    } 
} 

y luego, si queremos buscar la MyEnum correspondiente a un ProtoEnum, simplemente lo hacemos MyEnum.CORRESPONDENCE.get(protoEnum), y para ir por el otro lado, solo hacemos MyEnum.CORRESPONDENCE.inverse().get(myEnum) o myEnum.getCorresponding().

+0

Gracias por su respuesta. Creo que entiendo el concepto, pero no estoy seguro de cómo lo implementaría. ¿Te importaría anotar algún código? – Catchwa

+2

Hay un trozo para usted; ¿Está lo suficientemente claro? –

+0

Sí, tiene sentido, gracias. – Catchwa

1

Una forma es solamente para mantener la enumeración generada:

package test.model; 
public class Picture { 

    private int height, width; 
    private PictureProtoBuf.Picture.Format format; 

// Constructor, getters and setters, hashCode, equals, toString etc. 
} 

He utilizado este un par de veces, que puede o no tiene sentido en su caso. Sin embargo, nunca se recomienda usar las clases generadas por protobuf como modelo de datos (o extenderlos para agregar funcionalidad).

Cuestiones relacionadas