java

Ben Sless 2024-04-28T16:42:07.807649Z

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.

emccue 2024-04-29T17:45:53.842179Z

I don't think i have deep knowledge

emccue 2024-04-29T17:46:03.462039Z

but can you give a little context

emccue 2024-04-29T17:46:14.699309Z

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

emccue 2024-04-29T17:46:40.734839Z

> 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?

emccue 2024-04-29T17:47:04.563579Z

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

Ben Sless 2024-04-29T18:34:08.258009Z

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

emccue 2024-04-29T18:38:24.699799Z

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

Ben Sless 2024-04-29T18:46:45.504069Z

Ideally merging into jsonista

emccue 2024-04-29T18:49:52.665689Z

okay

emccue 2024-04-29T18:50:01.825879Z

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

Ben Sless 2024-04-29T18:53:06.192849Z

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 Sless 2024-04-29T18:54:02.800699Z

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

Ben Sless 2024-05-01T10:36:35.130759Z

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

emccue 2024-05-01T17:52:25.218259Z

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

emccue 2024-05-01T17:52:57.688139Z

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

Ben Sless 2024-05-23T07:32:23.780759Z

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