简体   繁体   中英

Avro Map Serialization/Deserialization issue

Currently I am using Avro 1.8.0 to serialize / deserialize objects but facing issue especially for java.util.Map object. Not facing issue with other type of objects.

Sample code here -

class AvroUtils {

    public byte[] serialize(Object payload) {
        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        Schema schema = new ReflectDatumWriter().getData().induce(payload); //---> getting proper map schema as {"type":"map","values":"string"}
        JsonEncoder jsonEncoder = EncoderFactory.get().jsonEncoder(schema, out);
        final GenericDatumWriter<Object> writer = new GenericDatumWriter(schema);
        writer.write(payload, jsonEncoder);
        jsonEncoder.flush();
        return out.toByteArray();
    }

    public <R> R deserialize(Object o, Class<R> aClass) {
        Schema schema = new ReflectDatumWriter().getData().induce(o); //------> getting error - unable to get schema
        final ByteArrayInputStream bin = new ByteArrayInputStream((byte[]) o);
        JsonDecoder jsonDecoder = DecoderFactory.get().jsonDecoder(schema, bin);
        final GenericDatumReader<R> reader = new GenericDatumReader<>(schema);
        return reader.read(null, jsonDecoder);
    }

    public static void main(String[] args) {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("Key1", "Value1");
        map.put("Key2", "Value2");

        // Serialization
        byte[] result = this.serialize(map);
        System.out.println("Serialized Data : " + new String(mapDes, "UTF-8"));

        // Deserialization
        Map<String, Object> mapDes = (Map<String, Object>) this.deserialize(result, Map.class);
        System.out.println("Deserialized Data : " + mapDes);
    }
}

In deserialize method I am trying to get the schema based on input data but avro is throwing error -

`Exception in thread "main" java.lang.ClassCastException: [B cannot be cast to java.util.Collection
at org.apache.avro.reflect.ReflectData.getArrayAsCollection(ReflectData.java:196)
at org.apache.avro.generic.GenericData.induce(GenericData.java:612)`

Note: At the end both the methods will be placed in different libraries (avro-serializer / avro-deserializer).

Please suggest the best way to get schema in deserialization method.

Thanks.

You get java.lang.ClassCastException: [B cannot be cast to java.util.Collection cause you try to call induce() method with object which is byte array but not a Map.

If you want to serialize a map in one place and deserialize it in the other, you can use a better way:

Schema schema = Schema.createMap(Schema.create(Schema.Type.STRING));

If you do so, you will not need any additional parameters in desirialize method.

Also, GenericDatumWriter can be used only with generic records, so you need a ReflectDatumWriter .

Here is an example of your code with changes:

public class AvroUtils {

    public static byte[] serialize(Object payload) throws IOException {
        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        Schema schema = Schema.createMap(Schema.create(Schema.Type.STRING)); //---> getting proper map schema as {"type":"map","values":"string"}
        JsonEncoder jsonEncoder = EncoderFactory.get().jsonEncoder(schema, out);
        final DatumWriter<Object> writer = new ReflectDatumWriter<>(schema);
        writer.write(payload, jsonEncoder);
        jsonEncoder.flush();
        return out.toByteArray();
    }

    public static <R> R deserialize(Object o) throws IOException {
        Schema schema = Schema.createMap(Schema.create(Schema.Type.STRING));
        JsonDecoder jsonDecoder = DecoderFactory.get().jsonDecoder(schema, new ByteArrayInputStream((byte[]) o));
        final DatumReader<R> reader = new ReflectDatumReader<>(schema);
        return reader.read(null, jsonDecoder);
    }

    public static void main(String[] args) throws IOException {
        Map<String, Object> map = new HashMap<>();
        map.put("Key1", "Value1");
        map.put("Key2", "Value2");

        // Serialization
        byte[] result = serialize(map);

        // Deserialization
        Map<String, Object> mapDes = deserialize(result);
        System.out.println("Deserialized Data : " + mapDes);
    }
}

As a result, you'll get something like this:

Deserialized Data : {Key2=Value2, Key1=Value1}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM