Java сериализатор Джексона, включая FQCN - PullRequest
1 голос
/ 21 февраля 2020

Я пытаюсь создать универсальный сериализатор c Jackon polymorphi c, который может сериализовать и десериализовать в JSON и обратно с этим форматом, включая fqcn класса объекта:

{
  "fqcn": "full qualified class name of the object",
  "data": "serialized object"
}

Эта оболочка должна применяться к любому объекту, например, это будет JSON представление объекта HashMap>:

{
  "fqcn": "java.util.HashMap",
  "data": {
    "key1": {
      "fqcn": "java.util.ArrayList",
      "data": [
        {
          "fqcn": "java.lang.String",
          "data": "value1"
        },
        {
          "fqcn": "java.lang.String",
          "data": "value2"
        }
      ]
    },
    "key2": { 
      ... 
    }
  }
}

Я мог бы использовать аннотацию MixIn для всех объектов с @JsonTypeInfo

@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.WRAPPER_OBJECT)
public interface ObjMixin {

}

---

ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(Object.class, ObjMixin.class);

Однако формат не соответствует требуемому формату: {"fqcn": ..., "data": ...}

Я также попытался зарегистрировать StdConverter для преобразования любых объектов в оболочку, подобную этой :

public class ObjectWrapper {

    private String fqcn;
    private Object data;

    public ObjectWrapper(Object obj) {
        this.fqcn = obj.getClass.getCanonicalName();
        this.data = obj;
    }

}

Однако невозможно создать StdDelegatingSerializer для Object.class.

С пользовательским StdSerializer, как показано ниже, я получаю StackOverflowError:

@Override
public void serialize(Object obj, JsonGenerator jsonGen, SerializerProvider serializerProvider) throws IOException {
    jsonGen.writeStartObject();
    jsonGen.writeStringField("fqcn", obj.getClass().getCanonicalName());
    jsonGen.writeFieldName("data");
    if (obj instanceof Iterable) {
        jsonGen.writeStartArray();
        // Recursive serialization of all elements in the iterable
        jsonGen.writeEndArray();
    } else if (obj instanceof Map) {
        jsonGen.writeStartObject();
        // Recursive serialization of all elements in the map
        jsonGen.writeEndObject();
    } else {
        // Infinite recursion here because I'm defining this serializer for Object.class
        serializerProvider.defaultSerializeValue(obj, jsonGen); 
    }
}

Кто-нибудь знает какое-либо другое решение, чтобы достичь этого?

1 Ответ

0 голосов
/ 26 февраля 2020

Вы можете использовать настраиваемый сериализатор и настраиваемый поставщик сериализатора, чтобы обернуть каждый объект, который вы хотите сериализовать, в этот объект-оболочку (РЕДАКТИРОВАТЬ: он не работал рекурсивно, обновил код, чтобы не использовать объект-оболочку, а вместо этого записывать поля):

public class FQCNTest {

    @Test
    public void doTest() throws JsonProcessingException {
        final ObjectMapper om = getObjectMapper();

        final Object obj = getTestObject();

        final String json = om.writeValueAsString(obj);

        System.out.println(json); // {"fqcn":"java.util.HashMap","data":{"k":{"fqcn":"java.lang.String","data":"v"}}}

        final Object obj2 = getTestValue();

        final String json2 = om.writeValueAsString(obj2);

        System.out.println(json2); // {"fcqn":"java.lang.String","data":"hello"}

        final Object obj3 = null;

        final String json3 = om.writeValueAsString(obj3);

        System.out.println(json3); // null
    }

    private ObjectMapper getObjectMapper() {
        final ObjectMapper mapper = new ObjectMapper();

        final SerializerProvider sp = mapper.getSerializerProviderInstance();
        mapper.setSerializerProvider(new CustomSerializerProvider(sp, mapper.getSerializerFactory()));
        return mapper;
    }

    private Object getTestObject() {
        final HashMap<Object, Object> hashMap = new HashMap<>();
        hashMap.put("k", "v");
        return hashMap;
    }

    private Object getTestValue() {
        return "hello";
    }

}

class CustomSerializerProvider extends DefaultSerializerProvider {

    private final SerializerProvider defaultInstance;

    protected CustomSerializerProvider(final SerializerProvider defaultInstance, final SerializerFactory f) {
        super(defaultInstance, defaultInstance.getConfig(), f);
        this.defaultInstance = defaultInstance;

    }

    @Override
    public WritableObjectId findObjectId(final Object forPojo, final ObjectIdGenerator<?> generatorType) {
        return defaultInstance.findObjectId(forPojo, generatorType);
    }

    @Override
    public JsonSerializer<Object> serializerInstance(final Annotated annotated, final Object serDef) throws JsonMappingException {
        return new CustomSerializer();
    }

    @Override
    public Object includeFilterInstance(final BeanPropertyDefinition forProperty, final Class<?> filterClass) {
        try {
            return defaultInstance.includeFilterInstance(forProperty, filterClass);
        } catch (final JsonMappingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean includeFilterSuppressNulls(final Object filter) throws JsonMappingException {
        return defaultInstance.includeFilterSuppressNulls(filter);
    }

    @Override
    public DefaultSerializerProvider createInstance(final SerializationConfig config, final SerializerFactory jsf) {
        return this;
    }

    @Override
    public void serializeValue(final JsonGenerator gen, final Object value) throws IOException {
        new CustomSerializer().serialize(value, gen, this);
    }

    @Override
    public void serializeValue(final JsonGenerator gen, final Object value, final JavaType rootType) throws IOException {
        super.serializeValue(gen, value, rootType);
    }

    @Override
    public void serializeValue(final JsonGenerator gen, final Object value, final JavaType rootType, final JsonSerializer<Object> ser) throws IOException {
        super.serializeValue(gen, value, rootType, ser);

    }

}

class CustomSerializer extends StdSerializer<Object> {

    protected CustomSerializer() {
        super(Object.class);
    }

    @Override
    public void serialize(final Object value, final JsonGenerator gen, final SerializerProvider provider) throws IOException {
        if (value == null) {
            provider.defaultSerializeValue(value, gen);
            return;
        }

        final Class<?> clazz = value.getClass();
        final JsonSerializer<Object> serForClazz = provider.findValueSerializer(clazz);

        gen.writeStartObject();
        gen.writeStringField("fqcn", clazz.getCanonicalName());
        gen.writeFieldName("data");

        if (value instanceof Iterable) {
            gen.writeStartArray();
            for (final Object e : ((Iterable<?>) value)) {
                final JsonSerializer<Object> ser = new CustomSerializer();
                ser.serialize(e, gen, provider);
            }
            gen.writeEndArray();
        } else if (value instanceof Map) {
            gen.writeStartObject();
            // Recursive serialization of all elements in the map
            for (final Map.Entry<?, ?> e : ((Map<?, ?>) value).entrySet()) {
                final String key = e.getKey().toString(); // need to handle keys better
                final Object mapValue = e.getValue();
                gen.writeFieldName(key);
                final JsonSerializer<Object> ser = new CustomSerializer();
                ser.serialize(mapValue, gen, provider);
            }
            gen.writeEndObject();
        } else {
            serForClazz.serialize(value, gen, provider);
        }

        gen.writeEndObject();
    }

}

Примечание: этот код может содержать слишком много ненужного материала, я просто взял его достаточно далеко, чтобы он работал для конкретного примера c. (и не тестировал десериализацию, это может быть совсем другое)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...