Is there an easy way to convert a JSON payload to a Java object using a custom ObjectMapper (Jackson) or do I have to provide a custom type converter. I know that I could use a processor, but somehow it would be nice to use input and output types of the stream definition.
In the second case: Am I even able to provide a custom type converter for application/json to Java?
The documentation states: "The customMessageConverters are added after the standard converters in the order defined. So it is generally easier to add converters for new media types than to replace existing converters."
I bet that there is an existing "application/json" converter - but at a first glance I could not find further information if it is even possible to replace existing converters.
Thanks!
Peter
If you look at streams.xml You can see the relevant configuration. The configured lists are used to construct a CompositeMessageConverter which visits every MessageConverter in list order until it finds one that can do the conversion and returns a non-null result. A CompositeConverter instance is created for each module instance that is configured for conversion (i.e., defines an inputType or outputType value) by filtering the list of candidate message converters, which all inherit AbstractFromMessageConverter. The list is paired down to those which respond true to public boolean supportsTargetMimeType(MimeType mimeType) (where mimeType is the value of the input/outputType). The CompositeMessageConverter is injected into the corresponding MessageChannel and converts the payload.
There are a couple of things you can do. You can override the xd.messageConverters bean definition. For example, you can replace JsonToPojoMessageConverter and PojoToJsonMessageConverter with your own subclasses. You can also insert your own implementations in the list before the above converters and have your implementation match only specific domain objects for which you need a custom JSON mapper.
Another possibility is to define your own mime type and provide converters for that mime type as customMessageConverters. In any case, follow these guidelines forextending Spring XD
Related
My task is to read events from multiple different topics (class of all data in all topics is "Event"). This class contains field "data" (Map) which carries specific for each topic data, that can be deserialized to specific class (e.g. to "DeviceCreateEvent" or smth.). I can create consumers for each topic with #KafkaListener on methods with parameter type "Event". But in this case firstly i have to event.getData() and deserialize it into specific class, so I will get code duplication in all consumer methods. Is there any way to get in annotated consumer method already deserialized object to specific class?
It's not clear what you are asking.
If you have a different #KafkaListener for each topic/event type, and use JSON, the framework will automatically tell the message converter the type the data should be converted to; see the documentation.
Although the Serializer and Deserializer API is quite simple and flexible from the low-level Kafka Consumer and Producer perspective, you might need more flexibility at the Spring Messaging level, when using either #KafkaListener or Spring Integration. To let you easily convert to and from org.springframework.messaging.Message, Spring for Apache Kafka provides a MessageConverter abstraction with the MessagingMessageConverter implementation and its JsonMessageConverter (and subclasses) customization. You can inject the MessageConverter into a KafkaTemplate instance directly and by using AbstractKafkaListenerContainerFactory bean definition for the #KafkaListener.containerFactory() property. The following example shows how to do so: ...
On the consumer side, you can configure a JsonMessageConverter; it can handle ConsumerRecord values of type byte[], Bytes and String so should be used in conjunction with a ByteArrayDeserializer, BytesDeserializer or StringDeserializer. (byte[] and Bytes are more efficient because they avoid an unnecessary byte[] to String conversion). You can also configure the specific subclass of JsonMessageConverter corresponding to the deserializer, if you so wish.
I have a simple persistent pojo like:
public class Peristent {
private String unsafe;
}
I use Spring Data mongoTemplate to persist and fetch the above object. I also need to encrypt the Persistent.unsafe variable and store a complex representation of that in backend, everytime I try to save Persistent object.
Can I annotate Persistent, or provide some sort of hooks where I can make the aforementioned translations without me having to do that in the Pojo code manually. This has to happen automatically during mongoTemplate.insert.
Spring Data currently only support Type based conversions. There is an issue for supporting property based conversion, which you might want to track.
Therefore annotating won't work. What you could do is, create use a separate class for the property, which just wraps the String and register a custom converter for that type. See http://docs.spring.io/spring-data/data-mongo/docs/1.10.4.RELEASE/reference/html/#mongo.custom-converters for details, how to do that.
I have a list of entities which in turn have a field of another (Embeddable) type.
All these entities shall be converted into a single bean which holds a list of these embeddable types.
Prior to using Dozer I have written a conversion method. I have put this into the dozerBeanMapping.xml:
<custom-converters>
<converter type="com.foo.bar.helper.ChargingPoiEntityToPoiConverter" >
<class-a>com.foo.bar.services.charging.repository.ChargingPoiEntity</class-a>
<class-b>com.foo.bar.beans.ChargingPoi</class-b>
</converter>
</custom-converters>
I instantiate Dozer this way:
final Mapper mapper = DozerBeanMapperSingletonWrapper.getInstance();
Which map method do I have to invoke?
Using
mapper.map(cpEntities, Cp.class);
my custom converter is not invoked.
Trying to invoke
mapper.map(cpEntities.get(0), Cp.class);
works well, but I have to convert a List<ChargingPoiEntity> instead of a single ChargingPoiEntity.
how can I achieve this?
mapper.map(cpEntities, Cp.class); is not matching your custom converter because the generic type information in List<ChargingPoiEntity> is lost. Dozer sees the class of cpEntities as java.util.ArrayList, which does not match com.foo.bar.services.charging.repository.ChargingPoiEntity. My understanding is that this is a limitation of Java generics, not an issue in Dozer.
One workaround is to define a custom converter between a ChargingPoiEntity array and a ChargingPoi:
<custom-converters>
<converter type="com.foo.bar.helper.ChargingPoiEntityToPoiConverter" >
<class-a>[Lcom.foo.bar.services.charging.repository.ChargingPoiEntity;</class-a>
<class-b>com.foo.bar.beans.ChargingPoi</class-b>
</converter>
</custom-converters>
When mapping, you can convert the cpEntities list to an array:
ChargingPoiEntity[] entityArray = cpEntities.toArray(
new ChargingPoiEntity[cpEntities.size()]);
ChargingPoi convertedList = mapper.map(entityArray, ChargingPoi.class);
Note that in this case, the custom converter will not be invoked when you do
mapper.map(cpEntities.get(0), ChargingPoi.class);
This problem should only apply when attempting to map generic collections directly via mapper.map(...); entities containing generic collections as fields should map fine.
I'm using an asyncronous message receiver in Spring-AMQP to receive messages. Currently only messages with JSON content are handled, but I have a requirement to also handle messages with XML content. My current implementation of MessageListener has a MessageConverter injected and uses it in onMessage(Message), like this:
MyMessage myMessage = (MyMessage) jsonConverter.fromMessage(message);
In order to support different content types I could obviously use the MessageProperties to interrogate the content-type header and manually select a converter to use. But that seems like a lot of work, like Spring should provide some better support for this scenario. I was hoping to find a generic MessageConverter implementation that would map from content-types to specific converters, but there doesn't seem to be such a thing.
Is my best option to write a delegating converter like that? Or is there a way to configure the ListenerContainer to support both async receiving and multiple converters that are automatically used as needed?
We have an open JIRA issue requesting support for a CompositeMessageConverter.
The listener container doesn't support conversion but we do have the MessageListenerAdapter which does support them (but just one, and has other stuff like handling replies).
Using the adapter means you can use a POJO method on your listener...
public void handleMessage(MyObject foo) {...}
If you put a delegating converter (one that delegates to either the json or marshalling converter) into the MLA, and both converters create the same object type, this will work fine. Otherwise the signature would have to take an Object and you'd have to do instanceof tests.
At some point, I'd like to make the adapter a bit smarter so it can choose the method based on the object type created by the converter...
public void handleMessage(Foo foo) {...}
public void handleMessage(Bar bar) {...}
... but that's really a different issue.
If you come up with a useful converter that you would like to contribute to the framework, the guidelines are on the project wiki.
I use jersey and have a method in my Resource class which has multiple parameters. These parameters are filled using #FormParam but the problem is, the type of the parameters are custom java types, not some primitives or String. I want to convert the value of parameters from json to custom java types. If I use #Cosume(MediaType.APPLICATION_JSON), then I cannot use multiple parameters and if I remove it, parameters cannot be converted from json to their java instances.
#POST #Path("/add")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
#Produces(MediaType.APPLICATION_JSON)
public String add(#FormParam("source") BookEntity source, #FormParam("author") AuthorEntity a) throws JsonGenerationException, JsonMappingException, IOException, TransformationException
{
...
}
If I change the parameter types to String and then use Jackson deserialization, I can deserialize json parameters to java instances but I want to do it for other methods too and get it done automatically.
I tried to use the approach used in Custom Java type for consuming request parameters but I cannot make it work.
You can use a custom type mapper.
See this answer
Anyway by default Jersey tries to map received json object representation using JAXB. Obiously you must annotate your objects.