#jsonRootName not working with spring boot starter hateoas - java

I am developing an rest application using spring-boot and using spring-Hateoas . And the DTO that i have written is:
Bill.java
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonRootName("bills")
public class Bill{
Depedencies:
dependencies {
compile "org.springframework.boot:spring-boot-starter-hateoas"
compile "org.springframework.boot:spring-boot-starter-ws"
compile "org.springframework.boot:spring-boot-starter-actuator"
compile "org.springframework.cloud:spring-cloud-starter-eureka:${springCloudVersion}"
testCompile("org.springframework.boot:spring-boot-starter-test")
}
Application.java:
#Configuration
#Import(BillServiceConfig.class)
#EnableAutoConfiguration
#EnableEurekaClient
#ComponentScan({"com.billing"})
#EnableWebMvc
#EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL)
public class Application {
BillController.java:
#RequestMapping(method = RequestMethod.GET, value = "")
public ResponseEntity<Resources<Resource<Bill>>> getBills(#PathVariable String billUid)
And the spring-boot version I am using is 1.2.2. The output that I am getting is
`_embedded: {
BillList:
{`
The json Root name here is BillList. But I need it as "bills" instead of "BillList". Can anybody help out in this issue. Thanks in advance.

The keys within an _embedded clause are actually relation names. Hence, they're obtained though a RelProvider abstraction in Spring HATEOAS. The easiest way to customize them is by annotating the domain type with #Relation and define the relation names that you expect for item and collection relations.
An easy way to get correct plurals used in the _embedded clause is by adding the Evo Inflector JAR to your classpath as documented here.

Related

Mongo Index not created using #Indexed in Spring Boot

I know there are similar questions like this one: Spring boot / mongo wont create index with the index annotation
Also issues in Github like 'spring.data.mongodb.auto-index-creation=true' not working
And also I've tried this Baeldung guide: Spring Data MongoDB – Indexes, Annotations and Converters
So the problem is I'm trying to add an index to an existing collection using #Indexed anotation like this:
#CreatedDate
#Indexed(name="timestamp_index", expireAfterSeconds=100))
private Date timestamp;
The field is a timestamp which is created automatically when an element is inserted into the DB.
Also the class has #Document tag.
So, what have I tried?
Following other answers first thing I did is to add spring.data.mongodb.auto-index-creation: true in this way:
spring:
data:
mongodb:
uri: ${env.mongo-database.url}
auto-index-creation: true
This not works... but I've also read that the problem can be if I have a AbstractMongoClientConfiguration class.
Currently the project doesn't have that class, but exists a MongoConfiguration class with the #Configuration tag. I don't know if this can interfere or something.
The class is like this:
#Configuration
public class MongoConfiguration { /*creates some beans*/ }
This class create a #Bean named mongodb so I've tried to add manually auto-index to true here:
#Primary
#Bean(name = "mongodb")
#ConfigurationProperties(prefix = "spring.data.mongodb")
public MongoProperties getMongodbProperties() {
MongoProperties mongoProperties = new MongoProperties();
mongoProperties.setAutoIndexCreation(true);
return mongoProperties;
}
But this doesn't work either. The index is not created.
Also this class doesn't extends from AbstractMongoClientConfiguration so I can't override the method autoIndexCreation
Also I can create the index programantically with this code:
mongoTemplate
.indexOps(PagoDto.class)
.ensureIndex(new Index().on("timestamp", Sort.Direction.ASC).expire(100));
But for clearer implementation I'd like to do using only annotations in the model.
So the point is: Neither use auto-index-creation in application properties nor trying to add the value into properties works.
Maybe the configuration class doesn't allow to create automatically? It interferes in any way?
I'm not supposed to be authorized to modify the configuration class, if is a little change like call setAutoIndexCreation method there is no problem but I can't change the logic and extends from AbstractMongoClientConfiguration. So the ideal scenario is to get it to work with the annotation #Indexed.
Thanks in advance

How to pass multiple consumer names in pact provider JUnit test

Using the pact provider JUnit 5/Spring Boot support annotations, perhaps I am not searching well for the answer... I'm wondering if it's possible to annotate a pact provider verification test with multiple consumers using the #Consumer annotation.
Like I would want to be able to do something like the following
#Provider("provider-name")
#Consumer("consumer-1, consumer-2")
#PactBroker
#ActiveProfiles("test")
public class PactVerificationTest {
#Test
//test methods
//...
}
The annotation takes a String as a value so unfortunately something like #Consumer({"consumer-1", "consumer-2"}) does not work either.
Like this:
#PactBroker(consumerVersionSelectors = {
#VersionSelector(consumer = "my-consumer-1"),
#VersionSelector(consumer = "my-consumer-2")
})
Use the latest library version and see documentation for more

Creating and using annotation in java

I am reviewing open source spring projects. I am confused about the use of annotations around here. I want to ask to clarify this.
#Target(ElementType.METHOD)
#Retention(RUNTIME)
#Bean
public #interface Merge {
#AliasFor("targetRef")
String value() default "";
#AliasFor("value")
String targetRef() default "";
Placement placement() default Placement.APPEND;
int position() default 0;
Class<MergeBeanStatusProvider> statusProvider() default MergeBeanStatusProvider.class;
boolean early() default false;
}
An annotation has been created here named Merge. It has different parameters and default values.
#Configuration
public class LocalConfiguration {
#Merge(targetRef = "mergedList", early = true)
public List<String> blLocalMerge() {
return Arrays.asList("local-config1", "local-config2");
}
}
And this is usage of #Merge annotation in any class I choosed randomly.
When I examined the code, I could not find any class related to the implementation of Merge annotation. By the way, this problem I'm having isn't just about this annotation. Almost all the annotations I have examined are used without being implemented in any way.
I think I will understand the others if we start from this annotation.
What does this anotation do? What kind of message does it give to the place where it is used. How does the application understand what that annotation does in runtime without being implemented anywhere.
Thanks.
Annotations don't have implementations. They are processed by external classes or tools depending on the RetentionPolicy. In this case, the Merge annotation has Runtime retention so it will be available via reflection once the class is loaded. At runtime any interested party (in this case I assume the Spring Framework) can use getAnnotations on your LocalConfiguration class to detect the Merge annotation and take whatever action that needs to be taken. The possibilities are really up to the framework that defined the annotation. A lot of Spring injection works like this with annotations but they are also used by many other frameworks such as Hibernate, Jersey, etc. The main idea is that annotations act as markers on specific code points to be used by an external entity at a later point.

Jackson serializes using #JsonProperty name in constructor

I have been working on upgrading the Spring Boot version of one of my microservices, and I stumbled upon a strange behaviour. I have class like this:
public class FilteredData {
private final List<ShipmentData> shipments;
public FilteredData(#JsonProperty("listShipments") List<ShipmentData> shipments) {
this.shipments = shipments;
}
public List<ShipmentData> getShipments() {
return shipments;
}
}
The behaviour that I had before doing the upgrade was that, when deserialising, the name listShipments was used in the JSON object to map to the shipments property of the Java class. However, when serialising, it would write the shipments property with the name shipments, not listShipments.
The problem is that now it is using the name listShipments when both deserialising and serialising. I am not sure at what point this issue started happening, as my initial Spring Boot version was 1.5.7 and I am slowly upgrading all the way to 2.3.4. But I believe it started happening after version 2.0.0.
I don't know if this is being caused by some internal change in Spring Boot's Jackson autoconfiguration, or a change in the actual Jackson library, but I am having a hard time tracking what caused this and how to fix it.
EDIT: I noticed from the latest Spring Boot 1 version (1.5.22) to Spring Boot 2.0.0, the Jackson minor version was bumped (from 2.8 to 2.9). Could this have caused the issue?
I was able to reproduce this exact behaviour while switching from Spring Boot 1.5.7.RELEASE to 2.0.0.RELEASE. First, I added the parent and the spring-boot-starter-web dependency. Then, I created a simple #RestController, used the POJO you provided and replaced ShipmentData with String. When I manually create a Jackson ObjectMapper, I could not reproduce the issue across the two releases.
The most interesting part: When you rename the parameter name of the constructor to something other than shipments, it seems to work fine. This could possibly be a bug in Jackson or somewhere in the Spring Framework which manifests only when the getter matches the parameter name in the constructor.
I suggest to use #JsonAlias to define the alternative name that should be accepted during deserialization:
#JsonCreator
public FilteredData(
#JsonProperty("shipments")
#JsonAlias("listShipments")
List<ShipmentData> shipments) {
this.shipments = shipments;
}
With this, you can deserialize with both listShipments and shipments while for serialization only shipments is used (defined by getter).
For reference, a working implementation with 2.3.3.RELEASE:
#RestController
public class FilteredDataController {
#PostMapping("/data")
public FilteredData postFilteredData(#RequestBody FilteredData data) {
return data;
}
}
public class FilteredData {
private final List<String> shipments;
#JsonCreator
public FilteredData(
#JsonProperty("shipments") #JsonAlias("listShipments")
List<String> shipments) {
this.shipments = shipments;
}
public List<String> getShipments() {
return shipments;
}
}
Request:
curl -d '{"listShipments":["a","b","c"]}' -H 'Content-Type: application/json' http://localhost:8080/data
Result:
{"shipments":["a","b","c"]}
I managed to find the issue! It was caused by a dependency being used by spring-boot-starter-web called jackson-module-parameter-names. This library provides a Jackson module named ParameterNamesModule, which, as per Spring Boot's Jackson Autoconfiguration, any Jackson module found in the classpath is automatically imported.
The description for this library in https://github.com/FasterXML/jackson-modules-java8 says:
Parameter names: support for detecting constructor and factory method ("creator") parameters without having to use #JsonProperty annotation. Provides com.fasterxml.jackson.module.paramnames.ParameterNamesModule
I am not 100% sure still why this created the problem that it did, but removing it through Maven solved the issue:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.module</groupId>
<artifactId>jackson-module-parameter-names</artifactId>
</exclusion>
</exclusions>
</dependency>
There were breaking changes between Spring Boot 1 and Spring Boot 2. Have you tried anything to fix it already? E.g. attempted to move the JsonProperty annotation to field rather than constructor? or using #JsonCreator
online resource: https://www.baeldung.com/jackson-deserialize-immutable-objects

Mixing in Spring Data Annotations

As I am gradually trying to remove Dependencies on Spring in the domain part of my library without minimal extra effort, I now turn to Spring Data and the Repositories
Originally we annotated our domain entities to look like this:
#Document
public void MyEntity {
#Id
#Getter private final EntityIdentifier identifier;
#PersistenceConstructor
public MyEntity( ... ) {}
...
}
and so on.
where #Document, #PersistenceConstructor and #Id originate from the Spring Project and some are for a specific database backend (MongoDB).
I would like to cut this dependency and use my own annotations, that make sense in my domain - #Document is definitly nothing my domain experts would understand when appearing on e.g an clas Chair or a Desk.
For de/serialization with Jackson, I can create mixins to add specific annotations to classes without modifying them in their origin.
Maybe there is a similar technique for Spring or some other way to achive this that is more elegant than creating a wrapping class?
Apparently I need some clarification:
Lets suppose we try to write a clean architecture application which consists out of the following modules: domain, adapters, application. In the domain module, I have my domain logic and domain entities and everything domainy. I do not have anything springy - no dependency on spring whatsoever, not even by having a dependency that somehow depends on spring.
In the adapters and application module, I do have dependencies on spring. I might use spring-data to implement the Repository-Adapters. I will use Spring to configure and glue together the application.
Now, in my domain module I have the following classes:
#AllArgsConstructor
#HashAndEquals(of="identifier")
#DomainEntity // <-- This is an Annotation that has no dependency on Spring!
public class DomainEntity {
#DomainId // <-- This is an Annotation that has no dependency on Spring!
#Getter private final DomainEntityIdentifier identifier;
#Getter #Setter private String someValue;
...
}
#HashAndEquals
#AllArgsConstructor
public class DomainEntityIdentifiers {
#Getter private final String name;
}
public void interface DomainEntityRepository {
DomainEntity findById(DomainEntityIdentifier identifier);
void save(DomainEntity domainEntity)
void deleteById(DomainEntityIdentifier identifier);
}
Now the task is, to provide the implementation of that interface in the adapters module, using Spring Data - e.g. spring-data-mongo and inject this adapter to the domain in the application module.
Now, surly I can create an class, lets say DomainEntityMongo which is basically the same as the DomainEntity just with the spring-data-mongo-annotations, then a public interface MyEntityRepository extends CrudRepository<EntityIdentifier, MyEntityMongo> and implement the interface DomainRepository by using MyEntityRepository and converting there and back again between DomainEntityMongo <=> DomainEntity.
What I am looking for is a more magical/generical solution. E.g.
Having jackson-style mixin-classes, which provide Spring with the necessary/missing meta-data to do the work
Configuring Spring to use non-spring-annotations to do the work (just as it is possible with the ComponentScan for non-component-inheriting Annotations)
Or - if the data guys have crafted another innovative solution - this innovative solution.
you can use
#JsonDeserialize(using = yourCustomizedDeserializer.class)
have a look here
https://www.baeldung.com/jackson-deserialization
you can customize your persistence strategy with #Persister. You may, for example, specify your own subclass of org.hibernate.persister.EntityPersister or you might even provide a completely new implementation of the interface org.hibernate.persister.ClassPersister that implements persistence via, for example, stored procedure calls, serialization to flat files or LDAP.
is that what you are looking for?

Categories