Come usare Jackson per deserializzare una serie di oggetti


780

La documentazione relativa all'associazione dei dati di Jackson indica che Jackson supporta la deserializzazione di "array di tutti i tipi supportati", ma non riesco a capire l'esatta sintassi per questo.

Per un singolo oggetto farei questo:

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

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

Ora per un array voglio fare questo:

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

//Java
List<MyClass> entries = ?

Qualcuno sa se c'è un comando magico mancante? In caso contrario, qual è la soluzione?


2
Preferisco la libreria GSON di Google per gestire JSON. Vale la pena dare un'occhiata se non l'hai ancora provato ... rende il lavoro molto semplice e intuitivo.
Jesse Webb,

11
FWIW Le possibili soluzioni a questo specifico problema con Gson sono quasi identiche a ciò che è possibile con l'API Data Binding di Jackson.
Programmatore Bruce,

17
Gweebz - forse ti piacerebbe spiegare perché ritieni che GSON sia una scelta migliore (rispetto a Jackson)?
StaxMan,

Risposte:


1657

Per prima cosa crea un mapper:

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

Come matrice:

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

Come elenco:

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

Un altro modo per specificare il tipo di elenco:

List<MyClass> myObjects = mapper.readValue(jsonInput, mapper.getTypeFactory().constructCollectionType(List.class, MyClass.class));

43
Una nota in più, se durante l'analisi si ottiene un errore del genere JsonMappingException: No suitable constructor found for type, significa che è necessario aggiungere un costruttore predefinito alla classe aggiungendo un costruttore privato no-arg risolto per me.
Sintassi Regole

12
@SyntaxRules è necessario aggiungere un costruttore esplicito se si dispone di un costruttore esplicito; in caso contrario, il compilatore crea automaticamente un costruttore "vuoto" pubblico. Buon punto. Un altro problema comune è che le classi interne devono essere static, altrimenti non hanno mai un costruttore zero-arg.
StaxMan,

244
A proposito, List<MyClass> myObjects = Arrays.asList(mapper.readValue(json, MyClass[].class))funziona fino a 10 volte più veloce di TypeRefence.

5
Sto cercando una versione di tipo generico.
Stephane,

1
@EugeneTskhovrebov la tua strada è la più pulita per allineare. Gli altri richiedono casting o dichiarazioni. Suggerisco di aggiungerlo come propria risposta per evidenziarlo e darvi un po 'di rappresentante.
Erik B,

190

Da Eugene Tskhovrebov

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

Questa soluzione sembra essere la migliore per me


Per coloro che lavorano con agenti in Java, Lotus Domino, questa è la strada da percorrere. Ho provato alcune delle altre soluzioni, ma ho sempre ricevuto unResourceNotFoundException
John

L'aggiunta di SyntaxRules nei commenti per la risposta sopra può essere richiesta per questa soluzione come noi, è stata per me. Volevo solo aggiungerlo per non perderlo.
Rob,

2
oppureArrays.asList(Json.fromJson(json.get("fieldName"), MyClass[].class))
Al-Mothafar,

3
oppureList<MyClass> myObjects = Arrays.asList(mapper.treeToValue(jsonNode.get("fieldName"), MyClass[].class))
Collin Krawll

@CollinKrawll cosa fa objectmapper.treetovalue?
Eswar,

35

Per implementazione generica:

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

4
Bel costrutto di classe <T []>. Non l'ho mai visto. Dove hai trovato informazioni su questo?
Roeland Van Heddegem,

11

Innanzitutto creare un'istanza di ObjectReader che sia thread-safe.

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

Quindi usalo:

List<MyClass> result = objectReader.readValue(inputStream);

Questo è esattamente quello che sto cercando, grazie
stand alone

otteniamo - com.fasterxml.jackson.databind.JsonMappingException: impossibile deserializzare l'istanza di java.util.ArrayList dal token START_OBJECT su [Fonte: java.io.FileInputStream@33fec21; riga: 1, colonna: 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();
}

3

ecco un'utilità che sta trasformando json2object o Object2json, qualunque sia il tuo 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;
    }

0

potresti anche creare una classe che si estende ArrayList:

public static class MyList extends ArrayList<Myclass> {}

e poi usalo come:

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

0

Non sono stato in grado di utilizzare questa risposta perché la mia linter non consentirà cast non controllati.

Ecco un'alternativa che puoi usare. Sento che in realtà è una soluzione più pulita.

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;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.