Как заставить Джексона вводить Дженерик в боб? - PullRequest
2 голосов
/ 11 июня 2019

Предположим, что у нас есть такой боб:

public class Response<T> {

    private T data;
    private double executionDuration;
    private boolean success;
    private String version;

    //HOW TO Make Jackson to inject this?
    private Class<T> dataClass;

    public Optional<T> getData() {
        return Optional.ofNullable(data);
    }

    public double getExecutionDuration() {
        return executionDuration;
    }

    public Class<T> getDataClass() {
        return dataClass;
    }

    public String getVersion() {
        return version;
    }

    public boolean isSuccess() {
        return success;
    }
}

Десериализация происходит так:

objectMapper.readValue(json, new TypeReference<Response<SomeClass>>() {});

Могу ли я как-то заставить Джексона добавить класс "SomeClass" в мой бин? Думаю, что введение самой ссылки на тип также было бы нормальным.

Ответы [ 3 ]

1 голос
/ 16 июня 2019

это сработало для меня;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
public class Entity<T> {
    private T data;
    @JsonSerialize(converter = ClassToStringConverter.class)
    @JsonDeserialize(converter = StringToClassConverter.class)
    private Class<T> dataClass;
}

и

import com.fasterxml.jackson.databind.util.StdConverter;

public class ClassToStringConverter extends StdConverter<Class<?>, String> {

    public String convert(Class<?> aClass) {
//        class java.lang.Integer
        return aClass.toString().split("\\s")[1];
    }
}

и

import com.fasterxml.jackson.databind.util.StdConverter;

public class StringToClassConverter extends StdConverter<String, Class<?>> {

    public Class<?> convert(String s) {
        try {
            return Class.forName(s);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

Main;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;

public class Main {

    public static void main(String[] args) throws IOException {
        Entity<Integer> data = new Entity<Integer>();
        data.setData(5);
        data.setDataClass(Integer.class);

        ObjectMapper mapper = new ObjectMapper();
        String json = mapper.writeValueAsString(data);

        Entity<Integer> jsonData = mapper.readValue(json, new TypeReference<Entity<Integer>>() {});
        System.out.println(jsonData.getData());
        System.out.println(jsonData.getDataClass().getCanonicalName());
    }
}

Но,может быть, лучше не сохранять тип класса, а использовать метод для получения типа из данных?

public Class<T> getType() {
    return (Class<T>) data.getClass();
}
0 голосов
/ 21 июня 2019
public class Response<T> {
    private T data;

    // other fields & methods

    public Class getType() {
        return Optional.ofNullable(data).map(Object::getClass).orElse(Void.class);
    }

    public Optional<Class> getSafeType() {
        return Optional.ofNullable(data).map(Object::getClass);
    }
}

Супер просто, не нужно возиться с Джексоном, NPE безопасно ...

0 голосов
/ 16 июня 2019

Если нежелательно сохранять информацию о классе в json и использовать @JsonTypeInfo, я бы предложил использовать @JacksonInject:

  public class Response<T> {
    private T data;
    private double executionDuration;
    private boolean success;
    private String version;

    @JacksonInject("dataClass")
    private Class<T> dataClass;

    public Optional<T> getData() {
      return Optional.ofNullable(data);
    }

    public double getExecutionDuration() {
      return executionDuration;
    }

    public Class<T> getDataClass() {
      return dataClass;
    }

    public String getVersion() {
      return version;
    }

    public boolean isSuccess() {
      return success;
    }
  }

Десериализация будет выглядеть так:

ObjectMapper mapper = new ObjectMapper();
InjectableValues.Std injectable = new InjectableValues.Std();
injectable.addValue("dataClass", SomeClass.class);
mapper.setInjectableValues(injectable);
final Response<Integer> response = mapper.readValue(json, new TypeReference<Response<SomeClass>>() { });
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...