I am working on a Spring Boot project. I just have annotation configuration. I want to include dozer to transform Entities to DTO and DTO to Entities. I see in the dozer website, they explain i have to add the following configuration in spring xml configuration file. Since i have not xml file but annotation configuration Java class, i don't know how to translate this into Java Configuration class.
<bean id="org.dozer.Mapper" class="org.dozer.DozerBeanMapper">
<property name="mappingFiles">
<list>
<value>dozer-global-configuration.xml</value>
<value>dozer-bean-mappings.xml</value>
<value>more-dozer-bean-mappings.xml</value>
</list>
</property>
</bean>
If someone could you give me an example it'll be very useful. Thanks
I think something like this should work:
#Configuration
public class YourConfiguration {
#Bean(name = "org.dozer.Mapper")
public DozerBeanMapper dozerBean() {
List<String> mappingFiles = Arrays.asList(
"dozer-global-configuration.xml",
"dozer-bean-mappings.xml",
"more-dozer-bean-mappings.xml"
);
DozerBeanMapper dozerBean = new DozerBeanMapper();
dozerBean.setMappingFiles(mappingFiles);
return dozerBean;
}
...
}
If you are using DozerBeanMapperFactoryBean instead of DozerBeanMapper you may use something like this.
#Configuration
public class MappingConfiguration {
#Bean
public DozerBeanMapperFactoryBean dozerBeanMapperFactoryBean(#Value("classpath*:mappings/*mappings.xml") Resource[] resources) throws Exception {
final DozerBeanMapperFactoryBean dozerBeanMapperFactoryBean = new DozerBeanMapperFactoryBean();
// Other configurations
dozerBeanMapperFactoryBean.setMappingFiles(resources);
return dozerBeanMapperFactoryBean;
}
}
This way you can import your mappings automatically. Than simple inject your Mapper and use.
#Autowired
private Mapper mapper;
Update with Dozer 5.5.1
In dozer 5.5.1, DozerBeanMapperFactoryBean is removed. So if you want to go with an updated version you need do something like below,
#Bean
public Mapper mapper(#Value(value = "classpath*:mappings/*mappings.xml") Resource[] resourceArray) throws IOException {
List<String> mappingFileUrlList = new ArrayList<>();
for (Resource resource : resourceArray) {
mappingFileUrlList.add(String.valueOf(resource.getURL()));
}
DozerBeanMapper dozerBeanMapper = new DozerBeanMapper();
dozerBeanMapper.setMappingFiles(mappingFileUrlList);
return dozerBeanMapper;
}
Now inject mapper as told above
#Autowired
private Mapper mapper;
And use like below example,
mapper.map(source_object, destination.class);
eg.
mapper.map(admin, UserDTO.class);
Just in case someone wants to avoid xml dozer file. You can use a builder directly in java. For me it's the way to go in a annotation Spring context.
See more information at mapping api dozer
#Bean
public DozerBeanMapper mapper() throws Exception {
DozerBeanMapper mapper = new DozerBeanMapper();
mapper.addMapping(objectMappingBuilder);
return mapper;
}
BeanMappingBuilder objectMappingBuilder = new BeanMappingBuilder() {
#Override
protected void configure() {
mapping(Bean1.class, Bean2.class)
.fields("id", "id").fields("name", "name");
}
};
In my case it was more efficient (At least the first time). Didn't do any benchmark or anything.
Related
These two are Service classes, which makes http calls to other services to retreive the json response. Just wanted to know if this is a proper way to use ResourceConverter which helps to convert json response string to POJO.
bIf you notice, these two seperate classes contains its own ResourceConverter Bean method. I cant create a single method which can be shared by all classes, because we need DummyResponse.class and TestResponse.class as the parameter. So, Is this way acceptable? Is it good practise to have multiple bean methods which return the same type, but in different classes?
#Service
public class TestClient(){
#Bean
private ResourceConverter getTestResponseConverter(){
ObjectMapper mapper = new ObjectMapper();
mapper.setDeserialization(DeserializationFeature.ALLOW_UNKNOWN_PROPERTIES);
ResourceConverter converter = new ResourceConverter(mapper,TestResponse.class);
return converter;
}
private TestResponse getTestResponse(){
//okhttp call to get the json, this is not actual syntax for call, not relevant in this context
String responseBodyString = okhttp.call(request);
JSONAPIDocument<TestResponse> testDocument = getTestResponseConverter().readDocument(responseBodyString, TestResponse.class);
return testDocument.get();
}
}
#Service
public class DummyClient(){
#Bean
private ResourceConverter getDummyResponseConverter(){
ObjectMapper mapper = new ObjectMapper();
mapper.setDeserialization(DeserializationFeature.ALLOW_UNKNOWN_PROPERTIES);
ResourceConverter converter = new ResourceConverter(mapper,DummyResponse.class);
return converter;
}
private Dummy getDummyResponse(){
//okhttp call to get the json, this is not actual syntax for call, not relevant in this context
String responseBodyString = okhttp.call(request);
JSONAPIDocument<DummyResponse> dummyDocument = getDummyResponseConverter().readDocument(responseBodyString, DummyResponse.class);
return testDocument.get();
}
}
You should/can use #Primary to annotate the one that will take precedence if there is an #Autowired ResourceConverter... and Spring can't tell the difference.
You can use #Qualifier to "name" each one and then the same annotation on the #Autowired bean to get the one you want.
So:
#Bean
private ResourceConverter testResponseConverter(){
...
}
and
#Bean
#Primary
private ResourceConverter dummyResponseConverter(){
...
}
You can do:
#Autowired ResourceConverter rc; // Gets the #Primary dummy one
#Autowired #Qualifier("testResponseConverter") ResourceConverter rc2; // Gets the corresponding testResponseConverter
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/annotation/Qualifier.html
I created a mixin for my class. The mixin itself works fine, it's not the issue that most people have where they mix faterxml/codehaus annotations.
I tested it in a unit test, creating the ObjectMapper "by hand" while using the addMixIn method - it worked just fine.
I want to use that mixin to modify the response jsons returned from my REST endpoints.
I've tried to customize Spring Boot's ObjectMapper in many different ways:
BuilderCustomizer:
#Bean
public Jackson2ObjectMapperBuilderCustomizer addMixin(){
return new Jackson2ObjectMapperBuilderCustomizer() {
#Override
public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
jacksonObjectMapperBuilder.mixIn(MyClass.class, MyClassMixin.class);
}
};
}
Builder:
#Bean
Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
return new Jackson2ObjectMapperBuilder().mixIn(MyClass.class, MyClassMixin.class);
}
Converter:
#Bean
public MappingJackson2HttpMessageConverter configureJackson(){
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(MyClass.class, MyClassMixin.class);
converter.setObjectMapper(mapper);
return converter;
}
ObjectMapper:
#Autowired(required = true)
public void configureJackon(ObjectMapper jsonMapper){
jsonMapper.addMixIn(MyClass.class, MyClassMixin.class);
}
None of these work.
As of Spring Boot 2.7, there is built-in support for mixins.
Adding the following annotation:
#JsonMixin(MyClass::class)
class MyClassMixin{
will register mixin in the auto-configured ObjectMapper.
This might depend on Spring Boot version but as per Customize the Jackson ObjectMapper defining a new Jackson2ObjectMapperBuilderCustomizer bean is sufficient
The context’s Jackson2ObjectMapperBuilder can be customized by one or more Jackson2ObjectMapperBuilderCustomizer beans. Such customizer beans can be ordered (Boot’s own customizer has an order of 0), letting additional customization be applied both before and after Boot’s customization.
I had tried the above and it did not work for me either. While debugging, I noticed that the ObjectMapper inside the message converter was null.
Referring to the post get registered message converters, I ended up replacing the default message converter for Jackson, allowing me to customize the object mapper to my needs:
#SpringBootApplication
#RestController
public class MixinTest {
public static void main(String[] args) {
SpringApplication.run(MixinTest.class, args);
}
static class Person {
private String title;
private String name;
private String nullField;
private LocalDate date;
Person(String title, String name) {
this.title = title;
this.name = name;
this.date = LocalDate.now();
}
// getters here...
}
// this will exclude nullField
#JsonInclude(JsonInclude.Include.NON_NULL)
interface PersonMixin {
#JsonProperty("fullName")
String getName();
}
#Bean
public Jackson2ObjectMapperBuilderCustomizer personCustomizer() {
return jacksonObjectMapperBuilder ->
jacksonObjectMapperBuilder.mixIn(Person.class, PersonMixin.class);
}
#Bean
public MappingJackson2HttpMessageConverter myMessageConverter(
// provided by Spring
RequestMappingHandlerAdapter reqAdapter,
Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
ObjectMapper mapper = jacksonObjectMapperBuilder
.featuresToEnable(SerializationFeature.INDENT_OUTPUT)
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.modulesToInstall(new JavaTimeModule())
.build();
// **replace previous MappingJackson converter**
List<HttpMessageConverter<?>> converters =
reqAdapter.getMessageConverters();
converters.removeIf(httpMessageConverter ->
httpMessageConverter.getClass()
.equals(MappingJackson2HttpMessageConverter.class));
MappingJackson2HttpMessageConverter jackson = new
MappingJackson2HttpMessageConverter(mapper);
converters.add(jackson);
reqAdapter.setMessageConverters(converters);
return jackson;
}
#GetMapping("/test")
public Person get() {
return new Person("Mr", "Joe Bloggs");
}
}
Which outputs the following in the browser after hitting http://localhost:8080/test:
{
"title" : "Mr",
"date" : "2019-09-03",
"fullName" : "Joe Bloggs"
}
This way, I should be able to add as many customizers as necessary. I'm sure there's a better way to do this. It seems hacky to replace internals like this...
I need my web service to serve me the messages.properties which contains localized texts in JSON format. I know that I can write my own parser to do that but where should I insert this logic in the Spring framework? Or is there a Spring infrastructure feature that already can do that?
You can use #PropertySource annotation on your class to load your property file into memory.
#Configuration
class MessagesConfiguration {
#Bean(name = "messageProperties")
public static PropertiesFactoryBean mapper() {
PropertiesFactoryBean bean = new PropertiesFactoryBean();
bean.setLocation(new ClassPathResource("messages.properties"));
return bean;
}
#Resource(name="messageProperties")
private Properties messages = new Properties();
public Properties getMessages() {
return messages;
}
}
Properties.class is just a wrapper for Map<String, String> so you can convert it to JSON.
Lets say I have a list of objects like this: LinkedList<JsonAssessment> jsonAssessments....
It is returned to this kind of method:
#RequestMapping(value = "mapping", method = RequestMethod.POST)
public
#ResponseBody
List<JsonAssessment> doSomething(....) {
.....
}
I am making AJAX call to this controller everything is working correctly as expected but I don't like the naming my JSON is returned. In firebug I am seeing:
{"LinkedList":[{"assessmentName":"........
The question is how can I rename that root element LinkedList? Is there any config I have to set?
EDIT
I do not want to use any wrapper objects.
EDIT
My ObjectMapper:
public class JsonObjectMapper extends ObjectMapper {
public JsonObjectMapper() {
super();
this.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
this.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
this.setSerializationInclusion(JsonInclude.Include.NON_NULL);
}
}
Spring: 3.2.4.RELEASE
Jackson: 2.1.2
EDIT
This object mapper is declared in MVC message converters:
<mvc:annotation-driven content-negotiation-manager="contentNegotiationManager">
<mvc:message-converters>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper" ref="jsonObjectMapper"/>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
And that is what I've tried with naming strategy:
public class JsonPropertyNamingStrategy extends PropertyNamingStrategy {
public static final MyNamingStrategy MY_NAMING_STRATEGY = new MyNamingStrategy();
private static final LinkedHashMap<String, String> PROPERTIES = new LinkedHashMap<String, String>() {{
put("LinkedList", "resultList");
}};
public static class MyNamingStrategy extends PropertyNamingStrategyBase {
#Override
public String translate(String propertyName) {
if (propertyName == null) {
return null;
}
if (PROPERTIES.containsKey(propertyName)) {
return PROPERTIES.get(propertyName);
}
return propertyName;
}
}
}
I was debugging it and when it comes to translate method property names comes all except the root element. I have everything that LinkedList contains, but LinkedList is not coming to this method.
Instead of directly returning a the List, wrap it inside a ResponseEntity that will give you a response without a root element
#RequestMapping(value = "mapping", method = RequestMethod.POST)
public
#ResponseBody ResponseEntity<List<JsonAssessment>> doSomething(....) {
.....
return new ResponseEntity(yourList);
}
That way you don't have a root element. If you still want a root element you could add it to a Map. Results in the following JSON "[{"assessmentName":"........]
#RequestMapping(value = "mapping", method = RequestMethod.POST)
public
#ResponseBody ResponseEntity<Map<String, List<JsonAssessment>>> doSomething(....) {
.....
Map results = new HashMap();
result.put("assesments", yourlist);
return new ResponseEntity(results);
}
Should output {"assesments":[{"assessmentName":"........
Although you are stilling wrapping the objects here, it is in objects that are freely available, you don't have to add your own custom classes.
This is what we are using in a couple of our #Controllers, we are using Spring 3.2 and Jackson 2.2.
put the list in a wrapper class is a a commonly implemented strategy
If you are using Jackson Mapper you can use Annotations to define names of properties in classes by
#JsonProperty("foo")
or set the order by
#JsonPropertyOrder({"foo", "bar"})
and so on.
See further Jackson Annotations
edit:
Sorry, just saw the wrapper-comment. The only solution i saw is using a wrapper like this: How to rename root key in JSON serialization with Jackson
I'm developing a REST webservice in spring MVC. I need to change how jackson 2 serialize mongodb objectids. I'm not sure of what to do because I found partial documentation for jackson 2, what I did is to create a custom serializer:
public class ObjectIdSerializer extends JsonSerializer<ObjectId> {
#Override
public void serialize(ObjectId value, JsonGenerator jsonGen,
SerializerProvider provider) throws IOException,
JsonProcessingException {
jsonGen.writeString(value.toString());
}
}
Create a ObjectMapper
public class CustomObjectMapper extends ObjectMapper {
public CustomObjectMapper() {
SimpleModule module = new SimpleModule("ObjectIdmodule");
module.addSerializer(ObjectId.class, new ObjectIdSerializer());
this.registerModule(module);
}
}
and then register the mapper
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean
class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="my.package.CustomObjectMapper"></bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
My CustomConverter is never called. I think the CustomObjectMapper definition is wrong,I adapted it from some code for jackson 1.x
In my controllers I'm using #ResponseBody.
Where am I doing wrong? Thanks
You should annotate corresponding model field with #JsonSerialize annontation. In your case it may be:
public class MyMongoModel{
#JsonSerialize(using=ObjectIdSerializer.class)
private ObjectId id;
}
But in my opinion, it should better don't use entity models as VOs. Better way is to have different models and map between them.
You can find my example project here (I used date serialization with Spring 3 and Jackson 2 as example).
How I would do this is:
Create an annotation to declare your custom serializers:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface MyMessageConverter{
}
Set up component scan for this in your mvcconfiguration file
<context:include-filter expression="package.package.MyMessageConverter"
type="annotation" />
and create a class that implements HttpMessageConverter<T>.
#MyMessageConverter
public MyConverter implements HttpMessageConverter<T>{
//do everything that's required for conversion.
}
Create a class that extends AnnotationMethodHandlerAdapter implements InitializingBean.
public MyAnnotationHandler extends AnnotationMethodHandlerAdapter implements InitializingBean{
//Do the stuffs you need to configure the converters
//Scan for your beans that have your specific annotation
//get the list of already registered message converters
//I think the list may be immutable. So, create a new list, including all of the currently configured message converters and add your own.
//Then, set the list back into the "setMessageConverters" method.
}
I believe this is everything that is required for your goal.
Cheers.
There is no need to create object mapper. Add jackson-core-2.0.0.jar and jackson-annotations-2.0.0.jar to your project.
Now, add the following lines of code to your controller while handing the service:
#RequestMapping(value = "students", method = RequestMethod.POST, headers = "Accept=application/json", consumes = "application/json")
public HashMap<String, String> postStudentForm(
#RequestBody Student student, HttpServletResponse response)
Do not miss any of the annotations.