Como usar Jackson para desserializar uma matriz de objetos

780

A documentação de ligação de dados de Jackson indica que Jackson suporta desserialização de "Matrizes de todos os tipos suportados", mas não consigo descobrir a sintaxe exata para isso.

Para um único objeto, eu faria isso:

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

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

Agora, para uma matriz, quero fazer isso:

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

//Java
List<MyClass> entries = ?

Alguém sabe se existe um comando faltando magia? Se não, então qual é a solução?

Ollie Edwards
fonte
2
Prefiro a biblioteca GSON do Google para lidar com JSON. Vale a pena conferir se você ainda não experimentou ... torna o trabalho muito fácil e intuitivo.
perfil completo de Jesse Webb
11
FWIW As soluções possíveis para esse problema específico com o Gson são quase idênticas às possíveis com a API de vinculação de dados de Jackson.
Programador Bruce
17
Gweebz - talvez você gostaria de explicar por que você acha que o GSON é uma escolha melhor (em comparação com Jackson)?
StaxMan

Respostas:

1657

Primeiro, crie um 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>>(){});

Outra maneira de especificar o tipo de lista:

List<MyClass> myObjects = mapper.readValue(jsonInput, mapper.getTypeFactory().constructCollectionType(List.class, MyClass.class));
Programador Bruce
fonte
43
Uma observação extra, se durante a análise você receber um erro como JsonMappingException: No suitable constructor found for typeesse, significa que você precisará adicionar um construtor padrão à sua classe, adicionando um construtor privado no-arg o corrigiu para mim.
SyntaxRules
12
@SyntaxRules adicionando construtor explícito é necessário se você tiver um construtor explícito - caso contrário, o compilador cria automaticamente o construtor público "vazio". Bom ponto. Outro problema comum é que as classes internas precisam ser static- caso contrário, elas nunca têm construtor de zero-arg.
precisa saber é
244
Por outro lado, List<MyClass> myObjects = Arrays.asList(mapper.readValue(json, MyClass[].class))funciona até 10 vezes mais rápido que o TypeRefence.
5
Estou procurando uma versão do tipo genérico.
Stephane
1
@EugeneTskhovrebov do seu jeito é o mais limpo para inlining. Os outros exigem elenco ou declaração. Sugiro adicioná-lo como sua própria resposta para ajudar a destacá-lo e dar-lhe algum representante.
Erik B
190

De Eugene Tskhovrebov

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

Esta solução parece ser a melhor para mim

Marthym
fonte
Para aqueles que trabalham com agentes em Java, Lotus Domino, este é o caminho a seguir. Eu tentei algumas das outras soluções, mas sempre tem umResourceNotFoundException
John
A adição de SyntaxRules nos comentários para a resposta acima pode ser necessária para esta solução, como nós, era para mim. Eu só queria acrescentar isso para que não se perca.
19417 Rob
2
ouArrays.asList(Json.fromJson(json.get("fieldName"), MyClass[].class))
Al-Mothafar 14/17
3
ouList<MyClass> myObjects = Arrays.asList(mapper.treeToValue(jsonNode.get("fieldName"), MyClass[].class))
Collin Krawll
@CollinKrawll o que faz objectmapper.treetovalue?
Eswar 20/09/18
35

Para implementação 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
fonte
4
Bela construção da Classe <T []>. Nunca vi isso. Onde você encontrou informações sobre isso?
Roeland Van Heddegem
11

Primeiro, crie uma instância do ObjectReader que seja segura para threads.

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

Então use-o:

List<MyClass> result = objectReader.readValue(inputStream);
Greg D
fonte
Isto é exatamente o que estou procurando, obrigado
fique sozinho
obtemos - com.fasterxml.jackson.databind.JsonMappingException: Não é possível desserializar a instância do java.util.ArrayList fora do token START_OBJECT no [Source: java.io.FileInputStream@33fec21; linha: 1, coluna: 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
fonte
3

aqui está um utilitário que pode transformar json2object ou Object2json, qualquer que seja seu pojo (entidade 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
fonte
0

você também pode criar uma classe que se estende ArrayList:

public static class MyList extends ArrayList<Myclass> {}

e depois use-o como:

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

Não consegui usar esta resposta porque meu linter não permitirá lançamentos não verificados.

Aqui está uma alternativa que você pode usar. Eu sinto que é realmente uma solução mais limpa.

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
fonte