145 votes

Référence aux types Jackson et génériques

Je veux utiliser la bibliothèque jackson json pour une méthode générique comme suit :

public MyRequest<T> tester() {
    TypeReference<MyWrapper<T>> typeRef = new TypeReference<MyWrapper<T>>();  
    MyWrapper<T> requestWrapper = (MyWrapper<T>) JsonConverter.fromJson(jsonRequest, typeRef);
    return requestWrapper.getRequest();
}

public class MyWrapper<T> {

    private MyRequest<T> request;

    public MyRequest<T> getRequest() {
        return request;
    }

    public void setRequest(MyRequest<T> request) {
        this.request = request;
    }
}

public class MyRequest<T> {
     private List<T> myobjects;

     public void setMyObjects(List<T> ets) {
         this.myobjects = ets;
     }

     @NotNull
     @JsonIgnore
     public T getMyObject() {
         return myobjects.get(0);
     }
}

Le problème est que lorsque j'appelle getMyObject(), qui se trouve dans l'objet de requête, Jackson renvoie l'objet personnalisé imbriqué sous la forme d'un LinkedHashMap. Existe-t-il un moyen de spécifier que l'objet T doit être retourné ? Par exemple : si j'ai envoyé un objet de type Customer, alors Customer doit être retourné à partir de cette liste ?

0 votes

Veuillez ajouter l'implémentation de getT()

0 votes

Cette question est similaire à stackoverflow.com/questions/6062011/ mais ils ont suggéré de spécifier le type en utilisant TypeFactory. Cependant, je ne connais pas le type au moment de la compilation...

0 votes

TypeFactory possède des méthodes qui ne nécessitent pas de classe statique ; createCollectionType et ainsi de suite.

255voto

StaxMan Points 34626

C'est un problème bien connu de l'effacement des types en Java : T n'est qu'une variable de type, et vous devez indiquer la classe réelle, généralement en tant qu'argument Class. Sans cette information, le mieux que l'on puisse faire est d'utiliser des bornes ; et T simple est à peu près identique à 'T extends Object'. Et Jackson liera alors les objets JSON comme des cartes.

Dans ce cas, la méthode du testeur doit avoir accès à Class, et vous pouvez construire

JavaType type = mapper.getTypeFactory().
  constructCollectionType(List.class, Foo.class)

et ensuite

List<Foo> list = mapper.readValue(new File("input.json"), type);

25 votes

Cela fonctionne : j'ai fait ce qui suit : JavaType topMost = mapper.getTypeFactory().constructParametricType(MyWrapper.class, ActualClassRuntime.class) ; et puis j'ai fait le readValue et ça a enfin marché :)

0 votes

Oui, cela fonctionne -- merci d'avoir signalé la méthode permettant de créer un type générique autre que le type Map/Collection !

1 votes

@StaxMan serait-il préférable d'utiliser ClassMate pour ce genre de choses à partir de maintenant ?

9voto

rushidesai1 Points 31

JavaType fonctionne ! ! J'essayais de démarshaller (désérialiser) une liste de chaînes json en objets java ArrayList et je me battais pour trouver une solution depuis des jours.
Voici le code qui m'a finalement donné la solution. Code :

JsonMarshallerUnmarshaller<T> {
    T targetClass;

    public ArrayList<T> unmarshal(String jsonString) {
        ObjectMapper mapper = new ObjectMapper();

        AnnotationIntrospector introspector = new JacksonAnnotationIntrospector();
        mapper.getDeserializationConfig()
            .withAnnotationIntrospector(introspector);

        mapper.getSerializationConfig()
            .withAnnotationIntrospector(introspector);
        JavaType type = mapper.getTypeFactory().
            constructCollectionType(
                ArrayList.class, 
                targetclass.getClass());

        try {
            Class c1 = this.targetclass.getClass();
            Class c2 = this.targetclass1.getClass();
            ArrayList<T> temp = (ArrayList<T>) 
                mapper.readValue(jsonString,  type);
            return temp ;
        } catch (JsonParseException e) {
            e.printStackTrace();
        } catch (JsonMappingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return null ;
    }  
}

2 votes

Comment initialiser la TargetClass ?

0 votes

Veuillez me montrer un petit exemple. Je passe une classe<?> cible et je récupère target.getClassName().

1 votes

Ajoutez un constructeur comme suit : JsonMarshallerUnmarshaller<T>{ private Class<T> targetClass ; JsonMarshallerUnmarshaller(Class<T> c){ targetClass = c ; }. } Apportez maintenant les modifications appropriées à la fonction 'unmarshal' pour utiliser cette classe au lieu de faire getClass partout.

0voto

Mr. Polywhirl Points 3677

J'ai modifié La réponse de rushidesai1 pour inclure un exemple concret.

JsonMarshaller.java

import java.io.*;
import java.util.*;

public class JsonMarshaller<T> {
    private static ClassLoader loader = JsonMarshaller.class.getClassLoader();

    public static void main(String[] args) {
        try {
            JsonMarshallerUnmarshaller<Station> marshaller = new JsonMarshallerUnmarshaller<>(Station.class);
            String jsonString = read(loader.getResourceAsStream("data.json"));
            List<Station> stations = marshaller.unmarshal(jsonString);
            stations.forEach(System.out::println);
            System.out.println(marshaller.marshal(stations));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @SuppressWarnings("resource")
    public static String read(InputStream ios) {
        return new Scanner(ios).useDelimiter("\\A").next(); // Read the entire file
    }
}

Sortie

Station [id=123, title=my title, name=my name]
Station [id=456, title=my title 2, name=my name 2]
[{"id":123,"title":"my title","name":"my name"},{"id":456,"title":"my title 2","name":"my name 2"}]

JsonMarshallerUnmarshaller.java

import java.io.*;
import java.util.List;

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;

public class JsonMarshallerUnmarshaller<T> {
    private ObjectMapper mapper;
    private Class<T> targetClass;

    public JsonMarshallerUnmarshaller(Class<T> targetClass) {
        AnnotationIntrospector introspector = new JacksonAnnotationIntrospector();

        mapper = new ObjectMapper();
        mapper.getDeserializationConfig().with(introspector);
        mapper.getSerializationConfig().with(introspector);

        this.targetClass = targetClass;
    }

    public List<T> unmarshal(String jsonString) throws JsonParseException, JsonMappingException, IOException {
        return parseList(jsonString, mapper, targetClass);
    }

    public String marshal(List<T> list) throws JsonProcessingException {
        return mapper.writeValueAsString(list);
    }

    public static <E> List<E> parseList(String str, ObjectMapper mapper, Class<E> clazz)
            throws JsonParseException, JsonMappingException, IOException {
        return mapper.readValue(str, listType(mapper, clazz));
    }

    public static <E> List<E> parseList(InputStream is, ObjectMapper mapper, Class<E> clazz)
            throws JsonParseException, JsonMappingException, IOException {
        return mapper.readValue(is, listType(mapper, clazz));
    }

    public static <E> JavaType listType(ObjectMapper mapper, Class<E> clazz) {
        return mapper.getTypeFactory().constructCollectionType(List.class, clazz);
    }
}

Station.java

public class Station {
    private long id;
    private String title;
    private String name;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return String.format("Station [id=%s, title=%s, name=%s]", id, title, name);
    }
}

données.json

[{
  "id": 123,
  "title": "my title",
  "name": "my name"
}, {
  "id": 456,
  "title": "my title 2",
  "name": "my name 2"
}]

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X