2012-01-20 14 views
7

Estoy tratando de escribir un deserializador personalizado para Jackson y quiero hacerlo genérico (genérico en el sentido de que funciona en cualquier tipo, no como en "genéricos").Deserializador personalizado de Jackson Obtener acceso a la clase de campo actual

Sin embargo, parece que no puedo entender cómo manejar el tipo de campo deserializado.

Por ejemplo, estoy buscando hacer algo como lo siguiente:

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

Es específicamente el tipo get del campo actual parte que he luchado con.

Editar: Es el tipo del campo java Estoy interesado en

+0

Por "tipo de campo actual", ¿quiere decir el tipo del valor JSON (objeto, conjunto de cuerdas, int, etc.)? ¿O el tipo de un campo Java con el mismo nombre en 'MyObject' al que está asignando el valor JSON? –

+0

Este último, el tipo de campo de Java. – monkjack

Respuesta

2

He resuelto mi problema particular al agregar una implementación de Deserializers al ObjectMapper. Por ejemplo,

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

Esto devolverá mi EnumDeserializer personalizado instanciado para cada tipo de Enum.

+0

me salvó algo de tiempo, pero no use su enum deserializer. Todo tipo de desaprobación horrible, más simple de escribir el tuyo. – MikePatel

+0

Solo quería agregar que configurar estos Deserializadores en la nueva api de jackson es tan impío. ejemplo proporcionado a través de gist: https://gist.github.com/patelm5/9268866 – MikePatel

0

En términos generales, y sin excepción la captura y verificación de errores ...

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 
} 

... y bucle hasta tok. es END_OBJECT. Para encontrar una de la clase actual por su nombre:

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

¿No encontrará findField en la clase del deserializador y no en la clase en la que se creó el deserializador para deshacer la clasificación? – monkjack

3

No hacer - deserializadores están registrados por tipo, por lo que necesita para construir deserializer saber qué tipo se espera que deserializar.

Si desea registrar un deserializador genérico, puede hacer las cosas más dinámicas implementando ContextualDeserializer. Su método createContextual() se llama con el argumento BeanProperty, y puede verificar cosas como el nombre de la propiedad (que puede ser nula, en el caso de valores raíz a los que no hace referencia una propiedad) y el tipo (que es el tipo declarado). Este método puede devolver una nueva instancia (NO modifique el deserializador original, ya que es compartido por todas las propiedades), configurado con toda la información adicional que necesita.

+0

Esta fue una buena respuesta, desafortunadamente parece que si registras un Deserializer con la clase Enum, entonces Jackson no habla con tu clase personalizada, sino que está integrado en el deserializador de enumeración. Esto significa que tendré que registrar mi deserializador enum de cusom con cada tipo de enumeración en mis entidades. – monkjack

+2

Correcto, al registrar deserializadores, se aplican al tipo específico para el que lo registra.Si quiere/necesita crear algo para manejar todos los tipos de enumeración, debería implementar 'Deserializadores', registrar eso: su devolución de llamada 'findEnumDeserializer()' invoca cada vez que se necesita un nuevo deserializador para un tipo de enumeración. – StaxMan

+0

¿Cómo creo un Objeto BeanProperty para la clase que quiero deserializar? –

0

Lo resolví así.

Obtener actual tipo de campo de 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; 
} 

Cuestiones relacionadas