3
votes

Comment faire en sorte que Jackson injecte la classe générique dans un haricot?

Supposons que nous ayons un bean comme celui-ci:

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

La désérialisation se déroule comme ceci:

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;
    }
}

Puis-je faire Jackson pour injecter la classe "SomeClass" dans mon bean? L'injection de la référence de type elle-même serait également acceptable, je pense.


10 commentaires

pouvez-vous vérifier cela une fois -> stackoverflow.com/questions/ 11664894 /…


@VinayVeluri en json je n'ai aucune référence sur la "dataClass". La seule façon dont Jackson sait cela est la référence de type. C'est pourquoi je me suis demandé si jackson pouvait injecter cette information.


Il ne peut pas faire cela, vous voudrez peut-être le faire vous-même en lisant une valeur avec class comme chaîne, puis en chargeant la classe avec Class.forName. Non seulement Jackson, mais vous-même ne pouvez pas supposer que vous connaissez cette classe (la classe peut être chargée en classe). Et puis l'analyser à nouveau avec une classe reconnue


@maslan Si je donne la référence de type pour la désérialisation, jackson pourrait vraiment simplement faire l'injection.


pourquoi ne pas appeler data.getClass () ?


@Nolequen si la réponse est vide, alors la classe serait Void et les données sont nulles. Si j'appelle .getClass sur un null, j'obtiens NullPointerException.


vous pouvez ajouter une vérification de null dans la méthode getDataClass : data == null? data.getClass (): Void.class


@ buræquete no. Aucune des réponses ne répond à ma question.


@ GáborLipták ne sais pas pourquoi ma réponse n'est pas du tout utile, pouvez-vous s'il vous plaît commenter?


@ buræquete si la valeur n'est pas présente, votre solution ne peut pas dire quel type a été donné dans le mappeur d'objets.


3 Réponses :


1
votes

cela a fonctionné pour moi;

public Class<T> getType() {
    return (Class<T>) data.getClass();
}

et

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());
    }
}

et

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.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];
    }
}

Mais, peut-être que ce sera mieux de ne pas enregistrer le type de classe, mais d'utiliser une méthode pour obtenir le type à partir des données?

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;
}


0 commentaires

3
votes

S'il n'est pas souhaitable d'enregistrer les informations de classe dans json et d'utiliser @JsonTypeInfo , je suggérerais d'utiliser @JacksonInject:

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>>() { });

La désérialisation ressemblerait à:

  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;
    }
  }


3 commentaires

Merci. La question intéressante est de savoir comment injecter la classe automatiquement en fonction de la référence de type?


N'est-ce pas possible en raison de l ' effacement de type ?


@ AustinSchäfer - Jackson a le TypeReference, il doit donc savoir pendant la désérialisation quelle classe de paramètre se trouve dans la classe Response paramétrée.



0
votes
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);
    }
}
Super simple, no need to tinker with Jackson, NPE safe... 

0 commentaires