Cómo usar Jackson para deserializar una matriz de objetos

780

La documentación de enlace de datos de Jackson indica que Jackson admite la deserialización de "matrices de todos los tipos compatibles", pero no puedo entender la sintaxis exacta para esto.

Para un solo objeto, haría esto:

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

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

Ahora para una matriz, quiero hacer esto:

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

//Java
List<MyClass> entries = ?

Alguien sabe si falta un comando mágico? Si no, ¿cuál es la solución?

Ollie Edwards
fuente
2
Prefiero la biblioteca GSON de Google para tratar con JSON. Vale la pena echarle un vistazo si aún no lo has probado ... hace que trabajar con él sea muy fácil e intuitivo.
Jesse Webb
11
FWIW Las posibles soluciones a este problema específico con Gson son casi idénticas a las posibles con la API de enlace de datos de Jackson.
Programador Bruce
17
Gweebz: ¿tal vez le gustaría explicar por qué cree que GSON es una mejor opción (en comparación con Jackson)?
StaxMan

Respuestas:

1657

Primero crea un mapeador:

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

Como matriz:

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

Como lista:

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

Otra forma de especificar el tipo de Lista:

List<MyClass> myObjects = mapper.readValue(jsonInput, mapper.getTypeFactory().constructCollectionType(List.class, MyClass.class));
Programador Bruce
fuente
43
Una nota adicional, si durante el análisis obtiene un error como JsonMappingException: No suitable constructor found for typeeste, significa que necesita agregar un constructor predeterminado a su clase, agregando un constructor privado sin argumentos lo arregló para mí.
SyntaxRules
12
@SyntaxRules es necesario agregar un constructor explícito si tiene un constructor explícito; de lo contrario, el compilador crea automáticamente un constructor público "vacío". Buen punto. Otro problema común es que las clases internas deben serlo static; de lo contrario, nunca tendrán un constructor de cero argumentos.
StaxMan
244
Por cierto, List<MyClass> myObjects = Arrays.asList(mapper.readValue(json, MyClass[].class))funciona hasta 10 veces más rápido que TypeRefence.
55
Estoy buscando una versión de tipo genérico.
Stephane
1
@EugeneTskhovrebov tu camino es el más limpio para la inserción. Los otros requieren fundición o declaración. Sugiero agregarlo como su propia respuesta para ayudar a resaltarlo y darle un poco de reputación.
Erik B
190

De Eugene Tskhovrebov

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

Esta solución parece ser la mejor para mí.

Marthym
fuente
Para aquellos que trabajan con agentes en Java, Lotus Domino, este es el camino a seguir. Probé algunas de las otras soluciones, pero siempre recibí unResourceNotFoundException
John
La adición de SyntaxRules en los comentarios para la respuesta anterior puede ser necesaria para esta solución, ya que nosotros, fue para mí. Solo quería agregar eso para que no se pierda.
Rob
2
oArrays.asList(Json.fromJson(json.get("fieldName"), MyClass[].class))
Al-Mothafar
3
oList<MyClass> myObjects = Arrays.asList(mapper.treeToValue(jsonNode.get("fieldName"), MyClass[].class))
Collin Krawll
@CollinKrawll ¿qué hace objectmapper.treetovalue?
Eswar
35

Para implementación genérica:

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
fuente
44
Buena construcción de la clase <T []>. Nunca vi esto. ¿Dónde encontraste información sobre esto?
Roeland Van Heddegem
11

Primero cree una instancia de ObjectReader que sea segura para subprocesos.

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

Entonces úsalo:

List<MyClass> result = objectReader.readValue(inputStream);
Greg D
fuente
Esto es exactamente lo que estoy buscando, gracias
solo el
sí obtenemos - com.fasterxml.jackson.databind.JsonMappingException: no se puede deserializar la instancia de java.util.ArrayList del token START_OBJECT en [Fuente: java.io.FileInputStream@33fec21; línea: 1, columna: 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();
}
Jerome
fuente
3

Aquí hay una utilidad que puede transformar json2object u Object2json, sea cual sea su pojo (entidad 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
fuente
0

También podría crear una clase que se extienda ArrayList:

public static class MyList extends ArrayList<Myclass> {}

y luego úsalo como:

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

No pude usar esta respuesta porque mi linter no permitirá lanzamientos no verificados.

Aquí hay una alternativa que puede usar. Siento que en realidad es una solución más limpia.

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
fuente