Verwendung von Jackson zum Deserialisieren einer Reihe von Objekten

779

Aus der Jackson-Datenbindungsdokumentation geht hervor , dass Jackson das Deserialisieren von "Arrays aller unterstützten Typen" unterstützt, aber ich kann die genaue Syntax dafür nicht herausfinden.

Für ein einzelnes Objekt würde ich Folgendes tun:

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

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

Nun möchte ich für ein Array Folgendes tun:

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

//Java
List<MyClass> entries = ?

Weiß jemand, ob ein magischer Befehl fehlt? Wenn nicht, was ist dann die Lösung?

Ollie Edwards
quelle
2
Ich bevorzuge die GSON-Bibliothek von Google für den Umgang mit JSON. Es lohnt sich zu prüfen, ob Sie es noch nicht ausprobiert haben ... macht die Arbeit damit sehr einfach und intuitiv.
Jesse Webb
11
FWIW Die möglichen Lösungen für dieses spezielle Problem mit Gson sind fast identisch mit denen, die mit Jacksons Datenbindungs-API möglich sind.
Programmierer Bruce
17
Gweebz - vielleicht möchten Sie erklären, warum GSON Ihrer Meinung nach die bessere Wahl ist (im Vergleich zu Jackson)?
StaxMan

Antworten:

1656

Erstellen Sie zuerst einen Mapper:

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

Als Array:

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

Als Liste:

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

Eine andere Möglichkeit, den Listentyp anzugeben:

List<MyClass> myObjects = mapper.readValue(jsonInput, mapper.getTypeFactory().constructCollectionType(List.class, MyClass.class));
Programmierer Bruce
quelle
43
Ein zusätzlicher Hinweis: Wenn beim Parsen ein Fehler auftritt, JsonMappingException: No suitable constructor found for typebedeutet dies, dass Sie Ihrer Klasse einen Standardkonstruktor hinzufügen müssen, indem Sie einen privaten Konstruktor ohne Argumente hinzufügen, der ihn für mich behoben hat.
SyntaxRules
12
@SyntaxRules Hinzufügen eines expliziten Konstruktors ist erforderlich, wenn Sie einen expliziten Konstruktor haben. Andernfalls erstellt der Compiler automatisch einen öffentlichen "leeren" Konstruktor. Guter Punkt. Ein weiteres häufiges Problem ist, dass innere Klassen sein müssen static- andernfalls haben sie niemals einen Null-Arg-Konstruktor.
StaxMan
244
Übrigens, List<MyClass> myObjects = Arrays.asList(mapper.readValue(json, MyClass[].class))arbeitet bis zu 10 Mal schneller als TypeRefence.
5
Ich suche eine generische Version.
Stephane
1
@EugeneTskhovrebov Ihr Weg ist der sauberste zum Inlining. Die anderen müssen gegossen oder deklariert werden. Ich schlage vor, es als eigene Antwort hinzuzufügen, um es hervorzuheben, und Ihnen einige Wiederholungen zu geben.
Erik B
190

Von Eugene Tskhovrebov

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

Diese Lösung scheint für mich die beste zu sein

Marthym
quelle
Für diejenigen, die mit Agenten in Java, Lotus Domino, arbeiten, ist dies der richtige Weg. Ich habe einige der anderen Lösungen ausprobiert, aber immer eineResourceNotFoundException
John
Die Hinzufügung von SyntaxRules in den Kommentaren für die obige Antwort kann für diese Lösung erforderlich sein, so wie wir es für mich waren. Ich wollte das nur hinzufügen, damit es nicht verloren geht.
Rob
2
oderArrays.asList(Json.fromJson(json.get("fieldName"), MyClass[].class))
Al-Mothafar
3
oderList<MyClass> myObjects = Arrays.asList(mapper.treeToValue(jsonNode.get("fieldName"), MyClass[].class))
Collin Krawll
@CollinKrawll Was macht objectmapper.treetovalue?
Eswar
35

Für die generische Implementierung:

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
quelle
4
Schönes Konstrukt der Klasse <T []>. Ich habe das nie gesehen. Wo haben Sie Informationen dazu gefunden?
Roeland Van Heddegem
11

Erstellen Sie zunächst eine Instanz von ObjectReader, die threadsicher ist.

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

Dann benutze es:

List<MyClass> result = objectReader.readValue(inputStream);
Greg D.
quelle
Dies ist genau das, wonach ich suche, danke
Standalone
wir bekommen - com.fasterxml.jackson.databind.JsonMappingException: Instanz von java.util.ArrayList kann nicht aus dem START_OBJECT-Token unter [Quelle: java.io.FileInputStream@33fec21; Zeile: 1, Spalte: 1]
Dinesh Kumar
7
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();
}
Jerome
quelle
3

Hier ist ein Dienstprogramm, mit dem Sie json2object oder Object2json transformieren können, unabhängig von Ihrem Pojo (Entität 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
quelle
0

Sie können auch eine Klasse erstellen, die Folgendes erweitert ArrayList:

public static class MyList extends ArrayList<Myclass> {}

und dann benutze es wie:

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

Ich konnte diese Antwort nicht verwenden da mein Linter keine ungeprüften Würfe zulässt.

Hier ist eine Alternative, die Sie verwenden können. Ich denke, es ist tatsächlich eine sauberere Lösung.

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
quelle