Comment utiliser Jackson pour désérialiser un tableau d'objets

781

La documentation de liaison de données Jackson indique que Jackson prend en charge la désérialisation des «tableaux de tous les types pris en charge», mais je ne peux pas comprendre la syntaxe exacte pour cela.

Pour un seul objet, je ferais ceci:

//json input
{
    "id" : "junk",
    "stuff" : "things"
}

//Java
MyClass instance = objectMapper.readValue(json, MyClass.class);

Maintenant, pour un tableau, je veux faire ceci:

//json input
[{
    "id" : "junk",
    "stuff" : "things"
},
{
    "id" : "spam",
    "stuff" : "eggs"
}]

//Java
List<MyClass> entries = ?

Quelqu'un sait s'il y a une commande magique manquante? Sinon, quelle est la solution?

Ollie Edwards
la source
2
Je préfère la bibliothèque GSON de Google pour traiter avec JSON. Cela vaut la peine de vérifier si vous ne l'avez pas encore essayé ... rend son utilisation très simple et intuitive.
Jesse Webb
11
FWIW Les solutions possibles à ce problème spécifique avec Gson sont presque identiques à celles possibles avec l'API de liaison de données de Jackson.
Programmeur Bruce
17
Gweebz - vous aimeriez peut-être expliquer pourquoi vous pensez que GSON est un meilleur choix (par rapport à Jackson)?
StaxMan

Réponses:

1658

Créez d'abord un mappeur:

import com.fasterxml.jackson.databind.ObjectMapper;// in play 2.3
ObjectMapper mapper = new ObjectMapper();

En tant que tableau:

MyClass[] myObjects = mapper.readValue(json, MyClass[].class);

Comme liste:

List<MyClass> myObjects = mapper.readValue(jsonInput, new TypeReference<List<MyClass>>(){});

Une autre façon de spécifier le type de liste:

List<MyClass> myObjects = mapper.readValue(jsonInput, mapper.getTypeFactory().constructCollectionType(List.class, MyClass.class));
Programmeur Bruce
la source
43
Une note supplémentaire, si lors de l'analyse, vous obtenez une erreur comme celle- JsonMappingException: No suitable constructor found for typeci, cela signifie que vous devez ajouter un constructeur par défaut à votre classe en ajoutant un constructeur privé sans argument qui l'a corrigé pour moi.
SyntaxRules
12
@SyntaxRules l'ajout d'un constructeur explicite est nécessaire si vous avez un constructeur explicite - sinon, le compilateur crée automatiquement un constructeur public "vide". Bon point. Un autre problème commun est que les classes internes doivent l'être static- sinon elles n'ont jamais de constructeur à arg zéro.
StaxMan
245
Btw, List<MyClass> myObjects = Arrays.asList(mapper.readValue(json, MyClass[].class))fonctionne jusqu'à 10 fois plus vite que TypeRefence.
5
Je recherche une version de type générique.
Stephane
1
@EugeneTskhovrebov votre chemin est le plus propre pour l'inline. Les autres nécessitent un casting ou une déclaration. Je suggère de l'ajouter comme sa propre réponse pour l'aider à le mettre en évidence et à vous donner une représentation.
Erik B
190

De Eugene Tskhovrebov

List<MyClass> myObjects = Arrays.asList(mapper.readValue(json, MyClass[].class))

Cette solution semble être la meilleure pour moi

Marthym
la source
Pour ceux qui travaillent avec des agents en Java, Lotus Domino, c'est la voie à suivre. J'ai essayé certaines des autres solutions, mais j'obtenais toujours unResourceNotFoundException
John
Un ajout de SyntaxRules dans les commentaires pour la réponse ci-dessus peut être nécessaire pour cette solution car nous, c'était pour moi. Je voulais juste ajouter cela pour qu'il ne soit pas perdu.
Rob
2
ouArrays.asList(Json.fromJson(json.get("fieldName"), MyClass[].class))
Al-Mothafar
3
ouList<MyClass> myObjects = Arrays.asList(mapper.treeToValue(jsonNode.get("fieldName"), MyClass[].class))
Collin Krawll
@CollinKrawll que fait objectmapper.treetovalue?
Eswar
35

Pour la mise en œuvre générique:

public static <T> List<T> parseJsonArray(String json,
                                         Class<T> classOnWhichArrayIsDefined) 
                                         throws IOException, ClassNotFoundException {
   ObjectMapper mapper = new ObjectMapper();
   Class<T[]> arrayClass = (Class<T[]>) Class.forName("[L" + classOnWhichArrayIsDefined.getName() + ";");
   T[] objects = mapper.readValue(json, arrayClass);
   return Arrays.asList(objects);
}
Obi Wan - PallavJha
la source
4
Belle construction de classe <T []>. Je n'ai jamais vu ça. Où avez-vous trouvé des informations à ce sujet?
Roeland Van Heddegem
11

Créez d'abord une instance d'ObjectReader qui est thread-safe.

ObjectMapper objectMapper = new ObjectMapper();
ObjectReader objectReader = objectMapper.reader().forType(new TypeReference<List<MyClass>>(){});

Ensuite, utilisez-le:

List<MyClass> result = objectReader.readValue(inputStream);
Greg D
la source
C'est exactement ce que je recherche, merci
stand alone
nous obtenons - com.fasterxml.jackson.databind.JsonMappingException: impossible de désérialiser l'instance de java.util.ArrayList du jeton START_OBJECT à [Source: java.io.FileInputStream@33fec21; ligne: 1, colonne: 1]
Dinesh Kumar
8
try {
    ObjectMapper mapper = new ObjectMapper();
    JsonFactory f = new JsonFactory();
    List<User> lstUser = null;
    JsonParser jp = f.createJsonParser(new File("C:\\maven\\user.json"));
    TypeReference<List<User>> tRef = new TypeReference<List<User>>() {};
    lstUser = mapper.readValue(jp, tRef);
    for (User user : lstUser) {
        System.out.println(user.toString());
    }

} catch (JsonGenerationException e) {
    e.printStackTrace();
} catch (JsonMappingException e) {
    e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
}
Jérôme
la source
3

voici un utilitaire qui permet de transformer json2object ou Object2json, quel que soit votre pojo (entité T)

import java.io.IOException;
import java.io.StringWriter;
import java.util.List;

import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

/**
 * 
 * @author TIAGO.MEDICI
 * 
 */
public class JsonUtils {

    public static boolean isJSONValid(String jsonInString) {
        try {
            final ObjectMapper mapper = new ObjectMapper();
            mapper.readTree(jsonInString);
            return true;
        } catch (IOException e) {
            return false;
        }
    }

    public static String serializeAsJsonString(Object object) throws JsonGenerationException, JsonMappingException, IOException {
        ObjectMapper objMapper = new ObjectMapper();
        objMapper.enable(SerializationFeature.INDENT_OUTPUT);
        objMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        StringWriter sw = new StringWriter();
        objMapper.writeValue(sw, object);
        return sw.toString();
    }

    public static String serializeAsJsonString(Object object, boolean indent) throws JsonGenerationException, JsonMappingException, IOException {
        ObjectMapper objMapper = new ObjectMapper();
        if (indent == true) {
            objMapper.enable(SerializationFeature.INDENT_OUTPUT);
            objMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
        }

        StringWriter stringWriter = new StringWriter();
        objMapper.writeValue(stringWriter, object);
        return stringWriter.toString();
    }

    public static <T> T jsonStringToObject(String content, Class<T> clazz) throws JsonParseException, JsonMappingException, IOException {
        T obj = null;
        ObjectMapper objMapper = new ObjectMapper();
        obj = objMapper.readValue(content, clazz);
        return obj;
    }

    @SuppressWarnings("rawtypes")
    public static <T> T jsonStringToObjectArray(String content) throws JsonParseException, JsonMappingException, IOException {
        T obj = null;
        ObjectMapper mapper = new ObjectMapper();
        obj = mapper.readValue(content, new TypeReference<List>() {
        });
        return obj;
    }

    public static <T> T jsonStringToObjectArray(String content, Class<T> clazz) throws JsonParseException, JsonMappingException, IOException {
        T obj = null;
        ObjectMapper mapper = new ObjectMapper();
        mapper = new ObjectMapper().configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
        obj = mapper.readValue(content, mapper.getTypeFactory().constructCollectionType(List.class, clazz));
        return obj;
    }
Tiago Medici
la source
0

vous pouvez également créer une classe qui étend ArrayList:

public static class MyList extends ArrayList<Myclass> {}

puis l'utiliser comme:

List<MyClass> list = objectMapper.readValue(json, MyList.class);
pero_hero
la source
0

Je n'ai pas pu utiliser cette réponse car mon linter n'autorisera pas les lancers non contrôlés.

Voici une alternative que vous pouvez utiliser. Je pense que c'est en fait une solution plus propre.

public <T> List<T> parseJsonArray(String json, Class<T> clazz) throws JsonProcessingException {
  var tree = objectMapper.readTree(json);
  var list = new ArrayList<T>();
  for (JsonNode jsonNode : tree) {
    list.add(objectMapper.treeToValue(jsonNode, clazz));
  }
  return list;
}
lbenedetto
la source