2013-05-08 9 views
13

Ho un qualcosa di JSON oggetto come:Come ignorare i campi enum nella mappatura Jackson JSON-to-Object?

{"name":"John", "grade":"A"} 

o

{"name":"Mike", "grade":"B"} 

o

{"name":"Simon", "grade":"C"} 

ecc

Sto cercando di mappare il JSON sopra per:

@JsonIgnoreProperties(ignoreUnknown = true) 
public class Employee{ 
     @JsonIgnoreProperties(ignoreUnknown = true) 
     public enum Grade{ A, B, C } 
     Grade grade; 
     String name; 

    public Grade getGrade() { 
    return grade; 
    } 

    public void setGrade(Grade grade) { 
    this.grade = grade; 
    } 

    public String getName() { 
    return name; 
    } 

    public void setName(String name) { 
    this.name = name; 
    } 
} 

la mappatura di cui sopra funziona bene, ma in futuro ci saranno più tipi "Grade" diciamo D, E, ecc, che rompe il mapping esistente e lancia la seguente eccezione

05-08 09:56:28.130: W/System.err(21309): org.codehaus.jackson.map.JsonMappingException: Can not construct instance of Employee from String value 'D': value not one of declared Enum instance names 

C'è un modo per ignora i campi sconosciuti con i tipi enum?

Grazie

+2

L'annotazione '@ JsonIgnoreProperties' non funziona come previsto per i tipi' Enum'; solo (al momento) consente di ignorare le proprietà POJO sconosciute. Ma penso che abbia senso come idea di miglioramento. Quindi potresti presentare un problema al progetto [Jackson databind] (https://github.com/FasterXML/jackson-databind)? Se è così, forse potrebbe essere aggiunto in Jackson 2.3? – StaxMan

+3

Le risposte sono un po 'prolisse. Per coloro che cercano la soluzione più rapida e semplice: impostare 'READ_UNKNOWN_ENUM_VALUES_AS_NULL' [funzione di deserializzazione] (https://github.com/FasterXML/jackson-databind/wiki/Deserialization-Features#value-conversions) su true. – Jonik

risposta

19

penso che si dovrebbe definire esterno deserializer per Grade enum.

ho aggiunto ulteriore campo di enum - UNKNOWN:

enum Grade { 
    A, B, C, UNKNOWN; 

    public static Grade fromString(String value) { 
     for (Grade grade : values()) { 
      if (grade.name().equalsIgnoreCase(value)) { 
       return grade; 
      } 
     } 

     return UNKNOWN; 
    } 
} 
class Employee { 

    @JsonDeserialize(using = GradeDeserializer.class) 
    private Grade grade; 
    private String name; 

    public Grade getGrade() { 
     return grade; 
    } 

    public void setGrade(Grade grade) { 
     this.grade = grade; 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    @Override 
    public String toString() { 
     return "Employee [grade=" + grade + ", name=" + name + "]"; 
    } 
} 

Ora, parser potrebbe apparire così:

class GradeDeserializer extends JsonDeserializer<Grade> { 
    @Override 
    public Grade deserialize(JsonParser parser, DeserializationContext context) 
      throws IOException, JsonProcessingException { 
     return Grade.fromString(parser.getValueAsString()); 
    } 
} 

Esempio di utilizzo:

public class JacksonProgram { 

    public static void main(String[] args) throws Exception { 
     ObjectMapper objectMapper = new ObjectMapper(); 
     JsonFactory jsonFactory = new JsonFactory(); 
     JsonParser parser = jsonFactory 
       .createJsonParser("{\"name\":\"John\", \"grade\":\"D\"}"); 
     Employee employee = objectMapper.readValue(parser, Employee.class); 
     System.out.println(employee); 
    } 

} 

uscita:

Employee [grade=UNKNOWN, name=John] 

Se non si desidera aggiungere altro campo, si restituirà null ad esempio.

+0

Grazie, ha funzionato. – Saqib

+1

Avere un'istanza SCONOSCIUTA dell'enumerazione mi sembra una scelta sbagliata. Preferirei la soluzione di Stefaan con la funzione READ_UNKNOWN_ENUM_VALUES_AS_NULL, che produrrà null. – Tom

+3

UNKNOWN significa che il valore non è noto in questo momento. null significa - il valore non è impostato. Può essere fonte di confusione in alcune situazioni e potrebbe causare NPE. Inoltre, se i valori enum contengono metodi, possiamo implementare la gestione nulla speciale in modo più elegante. Anche per me la soluzione con la caratteristica READ_UNKNOWN_ENUM_VALUES_AS_NULL è buona. Forse, è meglio del mio ... –

19

ho trovato un modo per farlo come segue:

public static void main(String[] args) throws JsonParseException, JsonMappingException, UnsupportedEncodingException, IOException { 
    String json = "{\"name\":\"John\", \"grade\":\"D\"}"; 

    ObjectMapper mapper = new ObjectMapper(); 
    mapper.configure(DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_AS_NULL, true); 
    Employee employee = mapper.readValue(new ByteArrayInputStream(json.getBytes("UTF-8")), Employee.class); 

    System.out.println(employee.getGrade()); 
} 

This uscite:

nulli

altre classi:

import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 

@JsonIgnoreProperties(ignoreUnknown = true) 
public class Employee { 
    private String name; 
    private Grade grade; 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public Grade getGrade() { 
     return grade; 
    } 

    public void setGrade(Grade grade) { 
     this.grade = grade; 
    } 
} 



import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 

@JsonIgnoreProperties(ignoreUnknown = true) 
public enum Grade {A, B, C} 

I Haven sono com e attraverso un modo per farlo con un'annotazione ancora.

Spero che questo aiuti.

+0

Grazie, ha funzionato – Saqib

+0

@stefaan: questa risposta ha funzionato per me! Tutto quello che mi mancava era la DeserializationFeature .READ_UNKNOWN_ENUM_VALUES_AS_NULL, ottima scoperta! – Arthulia

+0

A partire da Jackson 2.8, c'è anche READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, che consente di impostare un valore predefinito anziché null. https://fasterxml.github.io/jackson-databind/javadoc/2.8/com/fasterxml/jackson/databind/DeserializationFeature.html#READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE – Rick

1

@JsonCreator fornisce una soluzione più concisa rispetto a @JsonDeserialize.

L'idea è di annotare la tua sostituzione valueOf() (chiamata safeValueOf() in questo esempio) con @JsonCreator e poi Jackson deserializza le stringhe utilizzando la tua implementazione.

Si noti che l'implementazione è all'interno dell'enumerazione, è possibile utilizzarlo come campo in altri oggetti senza modifiche.

La soluzione di seguito è racchiusa in un test di unità in modo da poter eseguire direttamente.

import static junit.framework.TestCase.assertEquals; 

import java.io.IOException; 

import org.junit.Test; 

import com.fasterxml.jackson.annotation.JsonCreator; 
import com.fasterxml.jackson.databind.ObjectMapper; 

public class EmployeeGradeTest { 

    public enum Grade { 
     A, B, C, OTHER; 

     @JsonCreator 
     public static Grade safeValueOf(String string) { 
      try { 
       return Grade.valueOf(string); 
      } catch (IllegalArgumentException e) { 
       return OTHER; 
      } 
     } 
    } 

    @Test 
    public void deserialize() throws IOException { 
     assertEquals(Grade.A, new ObjectMapper().readValue("\"A\"", Grade.class)); 
    } 

    @Test 
    public void deserializeNewValue() throws IOException { 
     assertEquals(Grade.OTHER, new ObjectMapper().readValue("\"D\"", Grade.class)); 
    } 
} 
Problemi correlati