2015-01-12 15 views
17

Ho un pezzo di JSON, che assomiglia a questo:Deserialize array nidificato come ArrayList con Jackson

{ 
    "authors": { 
    "author": [ 
     { 
     "given-name": "Adrienne H.", 
     "surname": "Kovacs" 
     }, 
     { 
     "given-name": "Philip", 
     "surname": "Moons" 
     } 
    ] 
    } 
} 

Ho creato una classe per memorizzare le informazioni Autore:

public class Author { 
    @JsonProperty("given-name") 
    public String givenName; 
    public String surname; 
} 

E due classi wrapper :

public class Authors { 
    public List<Author> author; 
} 

public class Response { 
    public Authors authors; 
} 

Questo funziona, ma non è necessario disporre di due classi wrapper. Voglio trovare un modo per rimuovere la classe Authors e avere un elenco come proprietà della classe Entry. È qualcosa del genere è possibile con Jackson?

Aggiornamento

risolto che con deserializzatore personalizzato:

public class AuthorArrayDeserializer extends JsonDeserializer<List<Author>> { 

    private static final String AUTHOR = "author"; 
    private static final ObjectMapper mapper = new ObjectMapper(); 
    private static final CollectionType collectionType = 
      TypeFactory 
      .defaultInstance() 
      .constructCollectionType(List.class, Author.class); 

    @Override 
    public List<Author> deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) 
      throws IOException, JsonProcessingException { 

     ObjectNode objectNode = mapper.readTree(jsonParser); 
     JsonNode nodeAuthors = objectNode.get(AUTHOR); 

     if (null == nodeAuthors      // if no author node could be found 
       || !nodeAuthors.isArray()   // or author node is not an array 
       || !nodeAuthors.elements().hasNext()) // or author node doesn't contain any authors 
      return null; 

     return mapper.reader(collectionType).readValue(nodeAuthors); 
    } 
} 

e di utilizzarlo in questo modo:

@JsonDeserialize(using = AuthorArrayDeserializer.class) 
public void setAuthors(List<Author> authors) { 
    this.authors = authors; 
} 

@wassgren Grazie per l'idea.

+0

la risposta ti aiuta –

risposta

7

Vedo almeno due approcci per eseguire questa operazione se si desidera eliminare le classi wrapper. Il primo è utilizzare il Jackson Tree Model (JsonNode) e il secondo è utilizzare una funzione di deserializzazione denominata UNWRAP_ROOT_VALUE.


Alternativa 1: Usa JsonNode

Quando deserializzazione JSON utilizza Jackson vi sono diversi modi per controllare che tipo di oggetti che devono essere creati. Lo ObjectMapper può deserializzare il JSON ad es. a Map, JsonNode (tramite il metodo readTree) o un POJO.

Se si combina il metodo readTree con la conversione POJO, è possibile rimuovere completamente i wrapper. Esempio:

// The author class (a bit cleaned up) 
public class Author { 
    private final String givenName; 
    private final String surname; 

    @JsonCreator 
    public Author(
      @JsonProperty("given-name") final String givenName, 
      @JsonProperty("surname") final String surname) { 

     this.givenName = givenName; 
     this.surname = surname; 
    } 

    public String getGivenName() { 
     return givenName; 
    } 

    public String getSurname() { 
     return surname; 
    } 
} 

La deserializzazione può quindi essere simile a questa:

// The JSON 
final String json = "{\"authors\":{\"author\":[{\"given-name\":\"AdrienneH.\",\"surname\":\"Kovacs\"},{\"given-name\":\"Philip\",\"surname\":\"Moons\"}]}}"; 

ObjectMapper mapper = new ObjectMapper(); 

// Read the response as a tree model 
final JsonNode response = mapper.readTree(json).path("authors").path("author"); 

// Create the collection type (since it is a collection of Authors) 
final CollectionType collectionType = 
     TypeFactory 
       .defaultInstance() 
       .constructCollectionType(List.class, Author.class); 

// Convert the tree model to the collection (of Author-objects) 
List<Author> authors = mapper.reader(collectionType).readValue(response); 

// Now the authors-list is ready to use... 

Se si utilizza questo albero Model approssimano alle classi wrapper possono essere completamente rimossi.


Alternativa 2: rimuovere uno dei involucri e scartare il valore radice Il secondo approccio è quello di rimuovere solo uno degli involucri. Si supponga di rimuovere la classe Authors ma di conservare il wrapper Response. Se aggiungi un @JsonRootName -annotazione, puoi in seguito scartare il nome di livello superiore.

@JsonRootName("authors") // This is new compared to your example 
public class Response { 
    private final List<Author> authors; 

    @JsonCreator 
    public Response(@JsonProperty("author") final List<Author> authors) { 
     this.authors = authors; 
    } 

    @JsonProperty("author") 
    public List<Author> getAuthors() { 
     return authors; 
    } 
} 

Poi, per il mapper utilizzare semplicemente:

ObjectMapper mapper = new ObjectMapper(); 

// Unwrap the root value i.e. the "authors" 
mapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true); 
final Response responsePojo = mapper.readValue(json, Response.class); 

Il secondo approccio rimuove solo una delle classi wrapper, ma invece la funzione di analisi è piuttosto bella.

+0

grazie, proverò questo approccio. – Dmitry