how to use Jackson to deserialize a map property without the Annotation #JsonDeserialize - jackson

I have to deserialize a Class in third-party jar that contains a Map<String,Object> Collection property, so that the Annotation #JsonDeserialize cannot be used to defined at the property level,
I find that ObjectMapper can be registered with the custom deserializer, like
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(X.class, new XDeserializer());
mapper.registerModule(module);
X x = mapper.readValue(json, X.class);
but this is for class, is there any way to register a custom deserializer for property?

Related

Why jackson is not serializing this?

#Data
public class IdentificacaoBiometricaDto {
private Integer cdIdentifBiom;
private String nrMatricula;
private String deImpressaoDigital;
private Integer cdFilialAtualizacao;
}
I am using retrofit 2.6.1, jackson 2.9.9 and lombok 1.8.10.
The exception is:
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class br.com.clamed.modelo.loja.dto.central.IdentificacaoBiometricaDto and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:77)
at com.fasterxml.jackson.databind.SerializerProvider.reportBadDefinition(SerializerProvider.java:1191)
at com.fasterxml.jackson.databind.DatabindContext.reportBadDefinition(DatabindContext.java:313)
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:71)
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:33)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider._serialize(DefaultSerializerProvider.java:480)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:400)
at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1392)
at com.fasterxml.jackson.databind.ObjectWriter._configAndWriteValue(ObjectWriter.java:1120)
at com.fasterxml.jackson.databind.ObjectWriter.writeValueAsBytes(ObjectWriter.java:1017)
at retrofit2.converter.jackson.JacksonRequestBodyConverter.convert(JacksonRequestBodyConverter.java:34)
at retrofit2.converter.jackson.JacksonRequestBodyConverter.convert(JacksonRequestBodyConverter.java:24)
at retrofit2.ParameterHandler$Body.apply(ParameterHandler.java:355)
... 14 more
The object mapper:
return new ObjectMapper().registerModule(new ParameterNamesModule())
.registerModule(new Jdk8Module())
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
I am setting all fields, when passing it to a request body, retrofit fails because jackson could not serialize the object.
Retrofit call:
#POST("/usuario/v1.0/cadastraBiometria")
Call<IdentificacaoBiometricaDto> cadastraBiometria(#Body IdentificacaoBiometricaDto identificacaoBiometricaDto);
Rest service:
#RestController
#RequestMapping("/usuario")
public class UsuarioController {
#PostMapping(value = "/v1.0/cadastraBiometria")
public ResponseEntity<IdentificacaoBiometricaDto> cadastraBiometria(#RequestBody IdentificacaoBiometricaDto identificacaoBiometricaDto) {
}
}
Update:
If I change the retrofit converter to Gson it works;
If I serialize it using Jackson directly, it works;
Removing lombok makes no difference;
Found the problem. The biometric reader library was causing this. For some reason it's incompatible with openjdk-11 and is causing all sort of unrelated problems.
Yes, very weird. But the lib is very poorly done.

How to manually set a single bean property with Jackson?

I'm trying to manually update a bean (it is an argument to a JAX-RS resource method). The value of the field to be set in the bean is to be deserialized from JSON, contextually.
I want to do:
ObjectMapper objectMapper = new ObjectMapper();
// .... <configured, ClassIntrospector obtained for type> ...
BeanProperty prop;
// ... <bean property resolved through ClassIntrospector> ...
AnnotatedMember mutator = prop.getMutator();
JsonFactory jf = new JsonFactory();
JsonParser parser = jf.createParser(textProp);
Object value = objectMapper.getDeserializationContext().readValue(parser, mutator.getRawType());
mutator.setValue(beanInstance, value);
The problem is that Jackson is throwing a NullPointerException:
at com.fasterxml.jackson.databind.DeserializationContext.getTypeFactory(DeserializationContext.java:251)
at com.fasterxml.jackson.databind.DeserializationContext.readValue(DeserializationContext.java:758)
I confirmed with the debugger that the _config field of my DeserializationContext is null, and that this is what is being accessed during my code sequence.
So, what gives? How can I configure this properly so that this works? (Or is there some other way to manually deserialize a JSON fragment to a given type, respecting the JAX-RS resource context / classes?)

Register JodaModule in Jax-RS Application

I'm writing a Jax-RS application using Jersey, and Jackson2 under the hood to facilitate JSON i/o. The service itself works fine, but I'd like to improve it by having the Jackson mapper automagically serialize/deserialize date and date-times to JodaTime objects.
I'm following the documentation here and have added the relevant jars, but I'm lost on this instruction:
Registering module
To use Joda datatypes with Jackson, you will first need to register the module first (same as with all Jackson datatype modules):
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JodaModule());
I've tried to do this in the custom class that extends jax.ws.rs.core.Application, but I'm not at all confident in that solution. I'm currently getting this error:
Can not instantiate value of type [simple type, class org.joda.time.DateTime] from String value ('2014-10-22'); no single-String constructor/factory method
at [Source: org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream#3471b6d5; line: 7, column: 25]
Other than the general impression that this module registration needs to happen at application (servlet?) startup, I have no idea what to do with this information. Do I need to annotate a custom class with something in particular to have it picked up ? Should I be extending some class ?
The examples I find on StackOverflow usually stick it in main() and call the mapper directly, but I'm relying on Jackson Databinding so the examples aren't relevant. Any direction is appreciated.
You'll basically want to create/configure/return the ObjectMapper in a ContextResolver. Something like
#Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper> {
final ObjectMapper mapper = new ObjectMapper();
public ObjectMapperContextResolver() {
mapper.registerModule(new JodaModule());
}
#Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
}
If you are using package scanning to discover your resources, then the #Provider annotation should allow this class to be discovered and registered also.
Basically what happens, is the the MessageBodyReader and MessageBodyWriter provided by Jackson, used for unmarshalling and marshalling, respectively, will call the getContext method in the ContextResolver, to determine the ObjectMapper to use. The reader/writer will pass in the class (in a reader it will be the type expected in a method param, in a writer it will be the type returned as-a/in-a response), meaning we are allowed to use differently configured ObjectMapper for different classes, as seen here. In the above solution, it is used for all classes.

Moxy: marshall without JAXB annotations like #XmlRootElement

Is it possible to marshall all Java POJOs to XML without using JAXB annotations without configuring every POJO class independently?
PS: the context is a Rest Resource with Jersey 2.
JAXB (JSR-222) implementations including MOXy do not require any annotations. In the absence of an #XmlRootElement annotation you need to wrap your object in an instance of JAXBElement.
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Foo foo = new Foo();
JAXBElement<Foo> je = new JAXBElement(new QName("root-element"), Foo.class, foo);
Marshaller marshaller = jc.createMarshaller();
marshaller.marshal(je, System.out);
For More Information
You can refer to the following article from my blog for a full example:
http://blog.bdoughan.com/2012/07/jaxb-no-annotations-required.html

Jackson unrecognized field exception but field is in JSON

I am using Spring Integration to consume a message with a JSON Payload.
In my spring context I have
<integration:channel id="jsonToMyMessageConverterChannel"/>
<integration:json-to-object-transformer
type="com.acme.messaging.message.MyMessage"
input-channel="jsonToMyMessageConverterChannel"
output-channel="myMessageUpdateChannel"/>
My message related objects are:
MyMessage.java
#JsonIgnoreProperties(ignoreUnknown=true)
public class MyMessage {
#JsonProperty
private String timestamp;
#JsonProperty("msgs")
private List<Message> messages;
// Getters and Setters...
}
Message.java
#JsonIgnoreProperties(ignoreUnknown=true)
public class Message {
#JsonProperty
private Integer msgId;
#JsonProperty("msgText")
private String text;
// Getters and Setters...
}
When the json transformer attempts to convert the message to an object it fails with
Caused by: org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "msgs" (Class com.acme.messaging.message.MyMessage), not marked as ignorable
The JSON payload definitely has msgs which is an array that has objects which represent the Message.java class.
Can any one suggest reasons why the exception occurs given that the JSON has the field that is being complained about and the class itself is also annotated to ignore unknown fields?
Update
After some debugging it looks like the #JsonProperty("msgs") annotations aren't being use, for some reason.
This works fine for me...
#Test
public void test() throws Exception {
MyMessages mm = new MyMessages();
MyMessage m = new MyMessage();
m.setMsgId(1);
m.setText("foo");
mm.setMessages(Arrays.asList(m));
mm.setTimestamp("123");
#SuppressWarnings("deprecation")
ObjectToJsonTransformer otjt = new ObjectToJsonTransformer(new ObjectMapper());
Message<?> message = new GenericMessage<MyMessages>(mm);
message = otjt.transform(message);
System.out.println(message);
#SuppressWarnings("deprecation")
JsonToObjectTransformer<MyMessages> jtot = new JsonToObjectTransformer<MyMessages>(MyMessages.class, new ObjectMapper());
message = jtot.transform(message);
mm = (MyMessages) message.getPayload();
System.out.println(mm.getTimestamp());
System.out.println(mm.getMessages().get(0).getText());
}
(I changed your classnames slightly to avoid colliding with Message<?>)
Resulting in...
[Payload={"timestamp":"123","msgs":[{"msgId":1,"msgText":"foo"}]}][Headers={timestamp=1373997151738, id=f2425f36-a500-4aee-93a4-e7e0240ce0f1, content-type=application/json}]
123
foo
Do you have both jackson 1.x (codehaus) and 2.x (fasterxml) on the classpath, and using Spring Integration 3.0.0?
If they're both on the classpath, SI will use Jackson 2.x, by default, (which won't understand 1.x annotations).
Or, I guess - are you using Jackson2 (fasterxml) annotations? Spring Integration 2.x uses Jackson 1 (codehaus).
EDIT:
In order to support both versions of Jackson, you can annotate the class with both annotations...
#JsonProperty("msgs")
#com.fasterxml.jackson.annotation.JsonProperty("msgs")
public List<MyMessage> messages;