Deserializacja JSON do ArrayList <POJO> przy użyciu Jacksona

100

Mam klasę Java MyPojo, którą chcę deserializować z formatu JSON. Skonfigurowałem specjalną klasę MixIn MyPojoDeMixIn, aby pomóc mi w deserializacji. MyPojoma zmienne tylko inti Stringinstancji połączone z odpowiednimi metodami pobierającymi i ustawiającymi. MyPojoDeMixInwygląda mniej więcej tak:

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

W moim kliencie testowym wykonuję następujące czynności, ale oczywiście nie działa to w czasie kompilacji, ponieważ istnieje JsonMappingExceptionpowiązanie z niezgodnością typów.

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

Zdaję sobie sprawę, że mógłbym rozwiązać ten problem, tworząc obiekt „Response”, który zawiera tylko element ArrayList<MyPojo>, ale wtedy musiałbym utworzyć te nieco bezużyteczne obiekty dla każdego typu, który chcę zwrócić.

Spojrzałem również na JacksonInFiveMinutes w Internecie, ale miałem okropny czas, aby zrozumieć, o co chodzi Map<A,B>i jaki ma to związek z moim problemem. Jeśli nie możesz powiedzieć, jestem całkowicie nowy w Javie i pochodzę z tła Obj-C. W szczególności wspominają o:

Oprócz tworzenia powiązań z obiektami POJO i typami „prostymi” istnieje jeszcze jeden wariant: powiązanie z kontenerami rodzajowymi (typowymi). Ten przypadek wymaga specjalnej obsługi ze względu na tak zwany typ wymazywania (używany przez Javę do implementacji typów generycznych w sposób nieco wstecznie zgodny), który uniemożliwia użycie czegoś takiego jak Collection.class (który nie kompiluje się).

Więc jeśli chcesz powiązać dane z mapą, będziesz musiał użyć:

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

Jak mogę deserializować bezpośrednio do ArrayList?

tacos_tacos_tacos
źródło

Odpowiedzi:

153

Możesz deserializować bezpośrednio do listy przy użyciu TypeReferenceopakowania. Przykładowa metoda:

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

I jest używany w ten sposób:

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

TypeReference Javadoc

Postrzeganie
źródło
Twoja odpowiedź wydaje się związana z ich informacjami o tym, jak używać wbudowanej obsługi TypeReference- po prostu nie rozumiem, jak to zrobić ... Zobacz moją edycję powyżej, aby uzyskać instrukcje dotyczące używania typów ogólnych.
tacos_tacos_tacos
Cóż, to jest powiązane. Ale to jest fragment kodu działającego w środowisku produkcyjnym. Zapomnij o swoim miksie, po prostu użyj kodu, który pokazałem (ale oczywiście zamień POJO na nazwę twojej aktualnej klasy fasoli).
Percepcja
Twój kod został skompilowany, ale podczas próby wydrukowania arrayList.toString()pliku about a NullPointerException. Domyślam się, że może to być spowodowane tym, że my POJOnie dostosowuje się do odpowiednich konwencji nazewnictwa swoich właściwości, to znaczy cały problem polega na tym, że usługa internetowa zwraca, Prop1Membera mój obiekt ma Prop1. To jest jedyny prawdziwy powód, dla którego używam mixinów na początku, więc nie muszę umieszczać deklaracji @JsonPropertyw moich czystych obiektach.
tacos_tacos_tacos
1
Wizualnie sprawdź swoją tablicę, aby upewnić się, że otrzymałeś przynajmniej listę. W razie potrzeby dodaj z powrotem mikser , który powinien współpracować z TypeReference, aby uzyskać porządną deserializację.
Percepcja
2
@JsonProperty nie jest tak zła, jak się ludziom wydaje. Trudno jest uciec od adnotacji specyficznych dla dostawcy, co przy obecnym stanie standaryzacji (minimalnym) w tej dziedzinie.
Percepcja
109

Innym sposobem jest użycie tablicy jako typu, np:

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

W ten sposób unikniesz wszystkich kłopotów z obiektem Type, a jeśli naprawdę potrzebujesz listy, zawsze możesz przekonwertować tablicę na listę przez:

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

IMHO to jest znacznie bardziej czytelne.

Aby była to rzeczywista lista (którą można modyfikować, zobacz ograniczenia Arrays.asList()), wykonaj następujące czynności:

List<MyPojo> mcList = new ArrayList<>(Arrays.asList(pojos));
DevNG
źródło
1
Z pewnością eleganckie, ale nie mogę go wygenerować z powodu klasy MyPojo []., Której nie chcę podawać jako parametru.
Adrian Redgers
Myślę, że użycie sposobu TypeFactoryopisanego w następnej odpowiedzi: stackoverflow.com/a/42458104/91497 jest sposobem Jacksona na określenie typu.
Jmini
36

Ten wariant wygląda na prostszy i bardziej elegancki.

CollectionType typeReference =
    TypeFactory.defaultInstance().constructCollectionType(List.class, Dto.class);
List<Dto> resultDto = objectMapper.readValue(content, typeReference);
Дмитрий Кулешов
źródło
Dzięki za wyraźne CollectionTypeodniesienie.
Erwin Wessels
3

Ja też mam ten sam problem. Mam plik json, który ma zostać przekonwertowany na ArrayList.

Konto wygląda tak.

Account{
  Person p ;
  Related r ;

}

Person{
    String Name ;
    Address a ;
}

Wszystkie powyższe klasy zostały odpowiednio opisane. Próbowałem TypeReference> () {}, ale nie działa.

Daje mi Arraylist, ale ArrayList ma link HashMap, który zawiera więcej połączonych hashmap zawierających ostateczne wartości.

Mój kod jest następujący:

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

W końcu rozwiązałem problem. Jestem w stanie przekonwertować List w Json String bezpośrednio do ArrayList w następujący sposób:

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
źródło
2

To działa dla mnie.

@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
źródło