2012-01-20 26 views
7

Sto cercando di scrivere un deserializzatore personalizzato per Jackson e voglio renderlo generico (generico nel senso di lavorare su qualsiasi tipo, non come in "generici").Custom Deserializer Jackson Accesso alla classe Field corrente

Tuttavia, non riesco a capire come ottenere un handle per il tipo di campo che viene deserializzato.

Ad esempio, sto cercando di fare qualcosa di simile al seguente:

@Override 
public MyObject deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { 

     Class c = <get type of current field> 
     // do something with that type 
     return new SubclassOfC(somedata based on c); 
} 

E 'in particolare il tipo get di campo corrente parte che ho lottato con.

Edit: E 'il tipo di campo java Sono interessato a

+0

Per "tipo di campo attuale", vuoi dire il tipo del valore JSON (oggetto, array, string, int, ecc)? O il tipo di un campo Java con lo stesso nome in 'MyObject' in cui stai mappando il valore JSON? –

+0

Quest'ultimo, il tipo del campo Java. – monkjack

risposta

2

Ho risolto il mio problema aggiungendo un'implementazione di Deserializer all'ObjectMapper. Es.

Deserializers d = new Deserializers.Base() { 

    @Override 
    public JsonDeserializer<?> findEnumDeserializer(Class<?> type, DeserializationConfig config, BeanDescription beanDesc, BeanProperty property) 
        throws JsonMappingException { 
       if (property.getType().getContentType() != null) 
        return new EnumDeserializer(property.getType().getContentType().getRawClass()); 
       return new EnumDeserializer(property.getType().getRawClass()); 
      } 

     }; 
     mapper.setDeserializerProvider(mapper.getDeserializerProvider().withAdditionalDeserializers(d)); 

Ciò restituirà il mio EnumDeserializer personalizzato istanziato per ogni tipo Enum separato.

+0

mi ha salvato un po 'di tempo ma non uso il deserializzatore enum. Tutti i tipi di orribile deprecazione, più semplice scrivere il tuo. – MikePatel

+0

Volevo solo aggiungere che l'impostazione di questo deserializzatore sul nuovo jackson api è oh così empia. esempio fornito tramite sintesi: https://gist.github.com/patelm5/9268866 – MikePatel

0

In parole povere, e senza eccezioni la cattura e il controllo degli errori ...

JsonToken tok = jp.nextValue(); 

Field field = findField(jp.getCurrentName()); 

Class<?> fc = field.getType(); 

if(fc == int.class) { 
    field.setInt(this, jp.getIntValue()); 
} // handle all the primitive types and String in the same way, then... 
} ... else if(tok == JsonToken.START_ARRAY) { 
    if(fc.isArray()) { 
     // Load into an array 
    } else if(Collection.class.isAssignableFrom(fc)) { 
     // Load into a collection 
    } else { 
     // throw 
    } 
} else if(tok == JsonToken.START_OBJECT) { 
    // Recursively create that object from the JSON stream 
} 

... e ciclo fino a quando tok. è END_OBJECT. Per trovare un della classe corrente per nome:

Field findField(String name) { 
    for(Class<?> c = getClass(); c != null; c = c.getSuperclass()) { 
     for(Field field : c.getDeclaredFields()) { 
      if(field.getName().equals(name)) { 
       return field; 
      } 
     } 
    } 
} 
+0

Non troveràIl campo guarda all'interno della classe del deserializzatore e non della classe che il deserializzatore è stato creato per unmarshall? – monkjack

3

Non farlo - deserializzatore sono registrate per tipologia, quindi è necessario costruire deserializzatore di sapere che tipo ci si aspetta per deserializzare.

Se si desidera registrare un deserializzatore generico, è tuttavia possibile rendere le cose più dinamiche implementando ContextualDeserializer. Il suo metodo createContextual() viene chiamato con l'argomento BeanProperty e puoi controllare cose come il nome della proprietà (che può essere nullo, nel caso di valori radice a cui non fa riferimento una proprietà) e il tipo (che è il tipo dichiarato). Questo metodo può quindi restituire una nuova istanza (NON modificare il deserializzatore originale, poiché è condivisa da tutte le proprietà), configurata con tutte le informazioni aggiuntive necessarie.

+0

Questa è stata una buona risposta, sfortunatamente sembra che se registri un Deserializer con la classe Enum, allora Jackson non parla con la tua classe personalizzata, ma è costruito in enum deserializer. Ciò significa che dovrei registrare il mio cusom ener deserializer con ogni tipo di enum nelle mie entità. – monkjack

+2

A destra, quando si registrano i deserializzatori, si applicano al tipo specifico per cui lo si registra.Se vuoi/devi creare qualcosa per gestire tutti i tipi di enum, dovresti invece implementare 'Deserializers', registralo: il suo' findEnumDeserializer() 'callback chiamato ogni volta che è necessario un nuovo deserializzatore per un tipo enum. – StaxMan

+0

Come si crea un oggetto BeanProperty per la classe che si desidera deserializzare? –

0

Ho risolto il problema in questo modo.

Get attuale tipo di campo java ...

@Override 
public Enum deserialize(JsonParser jsonparser, DeserializationContext context) throws IOException, JsonProcessingException { 
    System.out.println("EnumDeserializer ...."); 
    Field field = findField(jsonparser.getCurrentName(), jsonparser.getCurrentValue().getClass()); 
    Class<?> javaType = field.getType(); 
    return null; 
} 

public Field findField(String name, Class<?> c) { 
    for (; c != null; c = c.getSuperclass()) { 
     for (Field field : c.getDeclaredFields()) { 
      if (Modifier.isStatic(field.getModifiers())) { 
       continue; 
      } 
      if (field.getName().equals(name)) { 
       return field; 
      } 
     } 
    } 
    return null; 
} 

Problemi correlati