java json jackson flickr

java - Deserialización JSON personalizada con Jackson



flickr (4)

Estoy usando la API de Flickr . Al llamar al método flickr.test.login , el resultado JSON predeterminado es:

{ "user": { "id": "21207597@N07", "username": { "_content": "jamalfanaian" } }, "stat": "ok" }

Me gustaría analizar esta respuesta en un objeto Java:

public class FlickrAccount { private String id; private String username; // ... getter & setter ... }

Las propiedades JSON se deben asignar de esta manera:

"user" -> "id" ==> FlickrAccount.id "user" -> "username" -> "_content" ==> FlickrAccount.username

Desafortunadamente, no puedo encontrar una forma agradable y elegante de hacer esto usando Anotaciones. Mi enfoque hasta ahora es leer la Cadena JSON en un Map<String, Object> y obtener los valores desde allí.

Map<String, Object> value = new ObjectMapper().readValue(response.getStream(), new TypeReference<HashMap<String, Object>>() { }); @SuppressWarnings( "unchecked" ) Map<String, Object> user = (Map<String, Object>) value.get("user"); String id = (String) user.get("id"); @SuppressWarnings( "unchecked" ) String username = (String) ((Map<String, Object>) user.get("username")).get("_content"); FlickrAccount account = new FlickrAccount(); account.setId(id); account.setUsername(username);

Pero creo que esta es la forma más no elegante, nunca. ¿Hay alguna forma simple, ya sea utilizando Anotaciones o un Deserializador personalizado?

Esto sería muy obvio para mí, pero por supuesto no funciona:

public class FlickrAccount { @JsonProperty( "user.id" ) private String id; @JsonProperty( "user.username._content" ) private String username; // ... getter and setter ... }


Como no quiero implementar una clase personalizada (nombre de Username ) solo para asignar el nombre de usuario, opté por un enfoque un poco más elegante, pero aún muy feo:

ObjectMapper mapper = new ObjectMapper(); JsonNode node = mapper.readTree(in); JsonNode user = node.get("user"); FlickrAccount account = new FlickrAccount(); account.setId(user.get("id").asText()); account.setUsername(user.get("username").get("_content").asText());

Todavía no es tan elegante como esperaba, pero al menos me deshice de todo el reparto feo. Otra ventaja de esta solución es que mi clase de dominio ( FlickrAccount ) no está contaminada con ninguna anotación de Jackson.

Basándome en la respuesta de @ Michał Ziober , decidí utilizar la solución más sencilla en mi opinión. Usando una anotación @JsonDeserialize con un deserializador personalizado:

@JsonDeserialize( using = FlickrAccountDeserializer.class ) public class FlickrAccount { ... }

Pero el deserializador no usa ninguna clase interna, solo el JsonNode como anteriormente:

class FlickrAccountDeserializer extends JsonDeserializer<FlickrAccount> { @Override public FlickrAccount deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { FlickrAccount account = new FlickrAccount(); JsonNode node = jp.readValueAsTree(); JsonNode user = node.get("user"); account.setId(user.get("id").asText()); account.setUsername(user.get("username").get("_content").asText()); return account; } }


Debes convertir el nombre de usuario en una clase dentro de FlickrAccount y asignarle un campo de contenido


Puede escribir deserializador personalizado para esta clase. Podría verse así:

class FlickrAccountJsonDeserializer extends JsonDeserializer<FlickrAccount> { @Override public FlickrAccount deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException { Root root = jp.readValueAs(Root.class); FlickrAccount account = new FlickrAccount(); if (root != null && root.user != null) { account.setId(root.user.id); if (root.user.username != null) { account.setUsername(root.user.username.content); } } return account; } private static class Root { public User user; public String stat; } private static class User { public String id; public UserName username; } private static class UserName { @JsonProperty("_content") public String content; } }

Después de eso, debes definir un deserializador para tu clase. Puede hacerlo de la siguiente manera:

@JsonDeserialize(using = FlickrAccountJsonDeserializer.class) class FlickrAccount { ... }


También puedes usar SimpleModule.

SimpleModule module = new SimpleModule(); module.setDeserializerModifier(new BeanDeserializerModifier() { @Override public JsonDeserializer<?> modifyDeserializer( DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) { if (beanDesc.getBeanClass() == YourClass.class) { return new YourClassDeserializer(deserializer); } return deserializer; }}); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(module); objectMapper.readValue(json, classType);