Deserializar JSON a ArrayList <POJO> usando Jackson

100

Tengo una clase de Java MyPojoque estoy interesado en deserializar de JSON. He configurado una clase MixIn especial MyPojoDeMixIn, para ayudarme con la deserialización. MyPojotiene sólo inty Stringde instancia de variables combinadas con captadores y definidores adecuados. MyPojoDeMixInse parece a esto:

public abstract class MyPojoDeMixIn {
  MyPojoDeMixIn(
      @JsonProperty("JsonName1") int prop1,
      @JsonProperty("JsonName2") int prop2,
      @JsonProperty("JsonName3") String prop3) {}
}

En mi cliente de prueba hago lo siguiente, pero, por supuesto, no funciona en tiempo de compilación porque hay JsonMappingExceptionuna falta de coincidencia de tipos relacionada.

ObjectMapper m = new ObjectMapper();
m.getDeserializationConfig().addMixInAnnotations(MyPojo.class,MyPojoDeMixIn.class);
try { ArrayList<MyPojo> arrayOfPojo = m.readValue(response, MyPojo.class); }
catch (Exception e) { System.out.println(e) }

Soy consciente de que podría solucionar este problema creando un objeto "Respuesta" que solo tenga un elemento ArrayList<MyPojo>, pero luego tendría que crear estos objetos algo inútiles para cada tipo que quiero devolver.

También miré en línea JacksonInFiveMinutes, pero me costó mucho entender el tema Map<A,B>y cómo se relaciona con mi problema. Si no puede decirlo, soy completamente nuevo en Java y tengo experiencia en Obj-C. Mencionan específicamente:

Además de la vinculación a POJO y tipos "simples", hay una variante adicional: la de vinculación a contenedores genéricos (con tipo). Este caso requiere un manejo especial debido al llamado Type Erasure (usado por Java para implementar genéricos de una manera algo compatible con versiones anteriores), que le impide usar algo como Collection.class (que no se compila).

Entonces, si desea vincular datos en un mapa, deberá usar:

Map<String,User> result = mapper.readValue(src, new TypeReference<Map<String,User>>() { });

¿Cómo puedo deserializar directamente a ArrayList?

tacos_tacos_tacos
fuente
Véase también stackoverflow.com/questions/7246157/…
Raedwald

Respuestas:

151

Puede deserializar directamente a una lista mediante el TypeReferencecontenedor. Un método de ejemplo:

public static <T> T fromJSON(final TypeReference<T> type,
      final String jsonPacket) {
   T data = null;

   try {
      data = new ObjectMapper().readValue(jsonPacket, type);
   } catch (Exception e) {
      // Handle the problem
   }
   return data;
}

Y se usa así:

final String json = "";
Set<POJO> properties = fromJSON(new TypeReference<Set<POJO>>() {}, json);

TypeReference Javadoc

Percepción
fuente
Su respuesta parece estar relacionada con su información sobre cómo usar el soporte integrado para TypeReference- simplemente no sé cómo hacerlo ... Consulte mi edición anterior para obtener instrucciones sobre cómo usar genéricos.
tacos_tacos_tacos
Bueno, está relacionado. Pero este es un fragmento de código de trabajo en producción. Olvídese de su mezcla, solo use el código que le he mostrado (pero reemplace POJO, por supuesto, con el nombre de su clase de frijol real).
Percepción
Su código se compiló, pero obtengo una excepción de tiempo de ejecución cuando intento imprimir el archivo arrayList.toString()about a NullPointerException. Supongo que esto podría deberse a que my POJOno se ajusta a las convenciones de nomenclatura correctas para sus propiedades, es decir, todo el problema es que el servicio web regresa Prop1Membery mi objeto sí Prop1. Esta es la única razón real por la que estoy usando mixins para empezar, por lo que no tengo que poner las declaraciones @JsonPropertyen mis objetos puros.
tacos_tacos_tacos
1
Inspeccione visualmente su matriz para asegurarse de haber obtenido al menos una lista. Y si es necesario, vuelva a agregar el mixin, que debería funcionar junto con TypeReference para que todo quede perfectamente deserializado.
Percepción
2
@JsonProperty no es tan malvado como la gente cree. Es difícil alejarse de las anotaciones específicas del proveedor con el estado actual de estandarización (mínimo) en el campo.
Perception
108

Otra forma es usar una matriz como tipo, por ejemplo:

ObjectMapper objectMapper = new ObjectMapper();
MyPojo[] pojos = objectMapper.readValue(json, MyPojo[].class);

De esta manera, evita todas las molestias con el objeto Type, y si realmente necesita una lista, siempre puede convertir la matriz en una lista de la siguiente manera:

List<MyPojo> pojoList = Arrays.asList(pojos);

En mi humilde opinión, esto es mucho más legible.

Y para que sea una lista real (que se puede modificar, consulte las limitaciones de Arrays.asList()), haga lo siguiente:

List<MyPojo> mcList = new ArrayList<>(Arrays.asList(pojos));
DevNG
fuente
1
Elegante, claro, pero no puedo generarlo debido a MyPojo []. Class, que no quiero pasar como parámetro.
Adrian Redgers
Creo que usar TypeFactorycomo se describe en la siguiente respuesta: stackoverflow.com/a/42458104/91497 es la forma de Jackson de especificar el tipo.
Jmini
36

Esta variante parece más simple y elegante.

CollectionType typeReference =
    TypeFactory.defaultInstance().constructCollectionType(List.class, Dto.class);
List<Dto> resultDto = objectMapper.readValue(content, typeReference);
Дмитрий Кулешов
fuente
Gracias por la CollectionTypereferencia explícita .
Erwin Wessels hace
3

Yo también tengo el mismo problema. Tengo un json que se convertirá a ArrayList.

La cuenta se ve así.

Account{
  Person p ;
  Related r ;

}

Person{
    String Name ;
    Address a ;
}

Todas las clases anteriores se han anotado correctamente. Probé TypeReference> () {} pero no funciona.

Me da Arraylist pero ArrayList tiene un LinkedHashMap que contiene algunos hashmaps más vinculados que contienen valores finales.

Mi código es el siguiente:

public T unmarshal(String responseXML,String c)
{
    ObjectMapper mapper = new ObjectMapper();

    AnnotationIntrospector introspector = new JacksonAnnotationIntrospector();

    mapper.getDeserializationConfig().withAnnotationIntrospector(introspector);

    mapper.getSerializationConfig().withAnnotationIntrospector(introspector);
    try
    {
      this.targetclass = (T) mapper.readValue(responseXML,  new TypeReference<ArrayList<T>>() {});
    }
    catch (JsonParseException e)
    {
      e.printStackTrace();
    }
    catch (JsonMappingException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }

    return this.targetclass;
}

Finalmente resolví el problema. Puedo convertir la Lista en Json String directamente a ArrayList de la siguiente manera:

JsonMarshallerUnmarshaller<T>{

     T targetClass ;

     public ArrayList<T> unmarshal(String jsonString)
     {
        ObjectMapper mapper = new ObjectMapper();

        AnnotationIntrospector introspector = new JacksonAnnotationIntrospector();

        mapper.getDeserializationConfig().withAnnotationIntrospector(introspector);

        mapper.getSerializationConfig().withAnnotationIntrospector(introspector);
        JavaType type = mapper.getTypeFactory().
                    constructCollectionType(ArrayList.class, targetclass.getClass()) ;
        try
        {
        Class c1 = this.targetclass.getClass() ;
        Class c2 = this.targetclass1.getClass() ;
            ArrayList<T> temp = (ArrayList<T>) mapper.readValue(jsonString,  type);
        return temp ;
        }
       catch (JsonParseException e)
       {
        e.printStackTrace();
       }
       catch (JsonMappingException e) {
           e.printStackTrace();
       } catch (IOException e) {
          e.printStackTrace();
       }

     return null ;
    }  

}
rushidesai1
fuente
2

Esto funciona para mi.

@Test
public void cloneTest() {
    List<Part> parts = new ArrayList<Part>();
    Part part1 = new Part(1);
    parts.add(part1);
    Part part2 = new Part(2);
    parts.add(part2);
    try {
        ObjectMapper objectMapper = new ObjectMapper();
        String jsonStr = objectMapper.writeValueAsString(parts);

        List<Part> cloneParts = objectMapper.readValue(jsonStr, new TypeReference<ArrayList<Part>>() {});
    } catch (Exception e) {
        //fail("failed.");
        e.printStackTrace();
    }

    //TODO: Assert: compare both list values.
}
Jugal Panchal
fuente