Fork me on GitHub
#java
<
2024-04-28
>
Ben Sless16:04:07

Anyone here have deep knowledge of Jackson and defining custom serialization? I'm trying to write a serializer that will use the IKVReduce interface but I have no idea how to approach the problem correctly and efficiently. For example, I tried extending StdSerializer<IKVReduce>, and tried to have private members

private JsonSerializer<Object> _keySer;
    private JsonSerializer<Object> _valSer;
But I have no idea how to lazily initialize them at runtime or do it correctly. I want to avoid calls to find the serializer at runtime.

emccue17:04:53

I don't think i have deep knowledge

emccue17:04:03

but can you give a little context

emccue17:04:14

are you writing this in Java, clojure with gen-class, or...

emccue17:04:40

> But I have no idea how to lazily initialize them at runtime or do it correctly. I want to avoid calls to find the serializer at runtime. Why do you want lazy initialization exactly?

emccue17:04:04

and the mechanism to use here is jackson modules - making a serializer is part of that though

Ben Sless18:04:08

For bit more context - I'm trying to add a custom serializer for IKVReduce to Jsonista, so it'll be a serializer registered on an ObjectMapper

emccue18:04:24

merging into Jsonista proper, implementing in your own project, or making a new side library?

Ben Sless18:04:45

Ideally merging into jsonista

emccue18:04:01

so what is the reason for lazy initialization? + what do you think should be lazily initialized

Ben Sless18:04:06

I'll just paste my implementation so it'll be easier to discuss:

package jsonista.jackson;

import clojure.lang.AFn;
import clojure.lang.IKVReduce;
import clojure.lang.Util;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import java.io.IOException;
import java.util.Map;

public class KVReduceSerializer extends StdSerializer<IKVReduce> implements ContextualSerializer {

    private JsonSerializer<Object> _keySer;
    private JsonSerializer<Object> _valSer;


    public KVReduceSerializer(JsonSerializer<Object> keySer, JsonSerializer<Object> valSer) {
        this();
        _keySer = keySer;
        _valSer = valSer;
    }

    public KVReduceSerializer() {super (IKVReduce.class);}

    protected KVReduceSerializer(Class<IKVReduce> t) {
        super(t);
    }

    protected KVReduceSerializer withResolved(JsonSerializer<Object> keySer, JsonSerializer<Object> valSer) {
        return this._keySer == keySer && this._valSer == valSer ? this : new KVReduceSerializer(keySer, valSer);

    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
        JavaType object = serializerProvider.constructType(Object.class);
        JsonSerializer<Object> keySer = serializerProvider.findKeySerializer(object, null);
        JsonSerializer<Object> valSer = serializerProvider.findValueSerializer(object);
        return this.withResolved(keySer, valSer);
    }

    private class RFn extends AFn {
        // public boolean first;
        private final JsonGenerator jsonGenerator;
        private final SerializerProvider serializerProvider;
        private RFn(JsonGenerator jsonGenerator, SerializerProvider serializerProvider) {
            this.jsonGenerator = jsonGenerator;
            this.serializerProvider = serializerProvider;
        }
        public Object invoke(Object b, Object k, Object v) {
            try {
                _keySer.serialize(k, jsonGenerator, serializerProvider);
                _valSer.serialize(v, jsonGenerator, serializerProvider);
            } catch (IOException e) {
                Util.sneakyThrow(e);
            }
            return null;
        };
    }

    private void serializeFields(
            IKVReduce kv,
            JsonGenerator jsonGenerator,
            SerializerProvider serializerProvider) {
        RFn kvrf = new RFn(jsonGenerator, serializerProvider);
        kv.kvreduce(kvrf, null);
    }


    @Override
    public void serialize(IKVReduce stringObjectMap, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeStartObject();
        serializeFields(stringObjectMap, jsonGenerator, serializerProvider);
        jsonGenerator.writeEndObject();

    }
}

Ben Sless18:04:02

I was trying to implement the "inverse" of the persistent hash map deserializer, also drew inspiration from the MapSerializer in Jackson Databind

Ben Sless10:05:35

with a debugger on, I can see I go through the createContextual method, but I'm not initializing the value serializer correctly. Ideas?

emccue17:05:25

sorry i've been dragged into stuff; havent had time yet

emccue17:05:57

the one time i've done a jackson module is this

Ben Sless07:05:23

This is as far as I got. I know my code goes through resolve and createContextual (checked with debugger), but I can't figure out how to initialize the serializers correctly. Eternal glory to whoever solves this