Ignore xml tags while serializing pojo fields to xml - spring-boot

I am using jackson library to map POJO to XML.
compile ('com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.0')
While serializing I need to ignore some of the fields. This is my POJO class. For example, the field lineNumber should be ignored.
#NoArgsConstructor
#AllArgsConstructor
#Getter
#XmlAccessorType(XmlAccessType.FIELD)
public class InvoiceLineItem {
#JacksonXmlProperty(localName = "LineNumber")
#XmlTransient
private Integer lineNumber;
#JacksonXmlProperty(localName = "ProductCode")
#XmlTransient
private String productCode;
#JacksonXmlProperty(localName = "ProductDescription")
#XmlTransient
private String productDescription;
}
I am using #XmlTransient with XmlAccessorType to ignore the fields. But the lineNumber field annotated with XmlTransient is not ignored while serializing.

Try adding the #JsonProperty(access = Access.WRITE_ONLY)
annotation to the lineNumber field.
Even thought it looks like a JSON thing,
the Jackson XmlMapper identifies the annotation and reacts accordingly.
Edit
The conclusion XmlMapper should support JSON serizlization is an example of the following, incorrect attempt at reasoning:
All men are mortal.
Socrates was mortal.
Therefore, all men are Socrates.
The XmlMapper is not a wrapper class around ObjectMapper.
It came after ObjectMapper and appears to share many features,
like the handling of some JSON annotation.

Related

OpenApi add example for request body

Am working with Spring boot and I am using springdoc-openapi-ui to generate spec files using swagger editor
The issue Is, Am trying to avoid creating model classes just to use them in the request body to show them with swagger UI.
For example :
#RequestMapping(value = "/update/project/{id}", method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> projectUpdate(#RequestBody ObjectNode json, #PathVariable int id)
{ ... }
If I use it like this, the example will be empty on the Swagger UI page.
So as a solution, I have to do it like the following
public class CustomerCreateRequest {
#JsonProperty
private String ProjectId;
#JsonProperty
private String ProjectName;
#JsonProperty
private String ProjectDescription;
#JsonProperty
private String CustomerId;
#JsonProperty
private String CustomerName;
#JsonProperty
private String CustomerDescription;
#JsonProperty
private String BucketId;
#JsonProperty
private String API_KEY;
#JsonProperty
private String Name;
#JsonProperty
private String RedmineId;
And then I can use the model class I just created like the following.
#PostMapping(value = "/createUser")
public ResponseEntity createCustomer(#RequestBody CustomerCreateRequest requestBody)
{ ... }
Question
Is it ok to do a model class just for this purpose?
Is there a way to add an example so the UI team will have an idea of how to use it.
I know that a model class can be helpful in generating a client for UI ( like JSClient ) But is it really necessary? I mean can't we overcome this issue?
Any Answer, Suggestion, Links are appreciated, the swagger docs was not helpful in my case.
my two cents:
Is it ok to do a model class just for this purpose?
Yes, you should use a model class for your #RequestBody becouse every endpoint must have a contract to communicate the payload necessary to be consumed.
It's a good practice add the annotations like
#Parameter(description="Some description", example="{\"foo\":\"bar\"}")
#RequestBody CustomerCreateRequest requestBody
Is there a way to add an example so the UI team will have an idea of how to use it.
No, Swagger will map a POJO class with decorators such as #Schema and others. ObjectNode has not a valid representation for the use case
I know that a model class can be helpful in generating a client for UI ( like JSClient ) But is it really necessary? I mean can't we overcome this issue?
Well, in my experience use tools as Swagger have more benefits than cons. It's necessary take care about the constraints related? I think so

Spring property binding with multiple separation

I have an application.property like this:
somevalue.api.test=something
somevalue.anotherproperty=stuff
I have made a configuration bean like this:
#Configuration
#ConfigurationProperties("somevalue")
public class SomeProperties {
#NotNull
private String apiTest;
#NotNull
private String anotherproperty;
}
Is it possible to refer to api.test like apiTest?
Mainly my issue is that I want to use the somevalue starting point for both property. I know if I don't separate with a dot the apiTest and I use it in this way somevalue.api-test I can refer to that with apiTest in my bean, but in my case it's not possible the renaming. So with dot separation can I achieve the same result or I should create two separate config bean, one refering to somevalue.api and the another only to somevalue?
If you can't rename the property then no, you can't reference it using String apiTest. You need an additional class as follows:
#Configuration
#ConfigurationProperties("somevalue")
public class GcssProperties {
#NotNull
private GcssApiProperties api;
#NotNull
private String anotherproperty;
}
public class GcssApiProperties {
#NotNull
private String test;
}
This should work.

Throw error when properties marked with #JsonIgnore are passed

I have a requirement to mark certain properties in my REST beans as ignored using #JsonIgnore. (I am using Spring Boot). This helps in avoiding these properties in my Swagger REST documentation.
I also would like to ensure that if the client passes these properties, an error is sent back. I tried setting spring.jackson.deserialization.fail-on-unknown-properties=true, but that works only for properties that are truly unknown. The properties marked with #JsonIgnore passes through this check.
Is there any way to achieve this?
I think I found a solution -
If I add #JsonProperty(access = Access.READ_ONLY) to the field that is marked as #JsonIgnore, I get back a validation error. (I have also marked the property with #Null annotation. Here is the complete solution:
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Employee {
#Null(message = "Id must not be passed in request")
private String id;
private String name;
//getters and setters
}
#JsonInclude(JsonInclude.Include.NON_NULL)
public class EmployeeRequest extends Employee {
#Override
#JsonIgnore
#JsonProperty(access = Access.READ_ONLY)
public void setId(String id) {
super.setId(id);
}
}
PS: By adding #JsonProperty(access = Access.READ_ONLY), the property started showing up in Swagger model I had to add #ApiModelProperty(hidden = true) to hide it again.
The create method takes EmployeeRequest as input (deserialization), and the get method returns Employee as response (serialization). If I pass id in create request, with the above solution, it gives me back a ConstraintViolation.
PS PS: Bummer. None of these solutions worked end-to-end. I ended up creating separate request and response beans - with no hierarchical relationship between them.

Spring Data Rest - sort by nested property

I have a database service using Spring Boot 1.5.1 and Spring Data Rest. I am storing my entities in a MySQL database, and accessing them over REST using Spring's PagingAndSortingRepository. I found this which states that sorting by nested parameters is supported, but I cannot find a way to sort by nested fields.
I have these classes:
#Entity(name = "Person")
#Table(name = "PERSON")
public class Person {
#ManyToOne
protected Address address;
#ManyToOne(targetEntity = Name.class, cascade = {
CascadeType.ALL
})
#JoinColumn(name = "NAME_PERSON_ID")
protected Name name;
#Id
protected Long id;
// Setter, getters, etc.
}
#Entity(name = "Name")
#Table(name = "NAME")
public class Name{
protected String firstName;
protected String lastName;
#Id
protected Long id;
// Setter, getters, etc.
}
For example, when using the method:
Page<Person> findByAddress_Id(#Param("id") String id, Pageable pageable);
And calling the URI http://localhost:8080/people/search/findByAddress_Id?id=1&sort=name_lastName,desc, the sort parameter is completely ignored by Spring.
The parameters sort=name.lastName and sort=nameLastName did not work either.
Am I forming the Rest request wrong, or missing some configuration?
Thank you!
The workaround I found is to create an extra read-only property for sorting purposes only. Building on the example above:
#Entity(name = "Person")
#Table(name = "PERSON")
public class Person {
// read only, for sorting purposes only
// #JsonIgnore // we can hide it from the clients, if needed
#RestResource(exported=false) // read only so we can map 2 fields to the same database column
#ManyToOne
#JoinColumn(name = "address_id", insertable = false, updatable = false)
private Address address;
// We still want the linkable association created to work as before so we manually override the relation and path
#RestResource(exported=true, rel="address", path="address")
#ManyToOne
private Address addressLink;
...
}
The drawback for the proposed workaround is that we now have to explicitly duplicate all the properties for which we want to support nested sorting.
LATER EDIT: another drawback is that we cannot hide the embedded property from the clients. In my original answer, I was suggesting we can add #JsonIgnore, but apparently that breaks the sort.
I debugged through that and it looks like the issue that Alan mentioned.
I found workaround that could help:
Create own controller, inject your repo and optionally projection factory (if you need projections). Implement get method to delegate call to your repository
#RestController
#RequestMapping("/people")
public class PeopleController {
#Autowired
PersonRepository repository;
//#Autowired
//PagedResourcesAssembler<MyDTO> resourceAssembler;
#GetMapping("/by-address/{addressId}")
public Page<Person> getByAddress(#PathVariable("addressId") Long addressId, Pageable page) {
// spring doesn't spoil your sort here ...
Page<Person> page = repository.findByAddress_Id(addressId, page)
// optionally, apply projection
// to return DTO/specifically loaded Entity objects ...
// return type would be then PagedResources<Resource<MyDTO>>
// return resourceAssembler.toResource(page.map(...))
return page;
}
}
This works for me with 2.6.8.RELEASE; the issue seems to be in all versions.
From Spring Data REST documentation:
Sorting by linkable associations (that is, links to top-level resources) is not supported.
https://docs.spring.io/spring-data/rest/docs/current/reference/html/#paging-and-sorting.sorting
An alternative that I found was use #ResResource(exported=false).
This is not valid (expecially for legacy Spring Data REST projects) because avoid that the resource/entity will be loaded HTTP links:
JacksonBinder
BeanDeserializerBuilder updateBuilder throws
com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of ' com...' no String-argument constructor/factory method to deserialize from String value
I tried activate sort by linkable associations with help of annotations but without success because we need always need override the mappPropertyPath method of JacksonMappingAwareSortTranslator.SortTranslator detect the annotation:
if (associations.isLinkableAssociation(persistentProperty)) {
if(!persistentProperty.isAnnotationPresent(SortByLinkableAssociation.class)) {
return Collections.emptyList();
}
}
Annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
public #interface SortByLinkableAssociation {
}
At project mark association as #SortByLinkableAssociation:
#ManyToOne
#SortByLinkableAssociation
private Name name;
Really I didn't find a clear and success solution to this issue but decide to expose it to let think about it or even Spring team take in consideration to include at nexts releases.
Please see https://stackoverflow.com/a/66135148/6673169 for possible workaround/hack, when we wanted sorting by linked entity.

Spring Data Redis Repository support does not read back embedded complex objects

I have a spring-boot application (1.4RC1, I know it's RC, but Spring Data Redis 1.7.2 is not) where I'm using spring-boot-starter-redis.
The application uses a Spring Data Repository (CrudRepository) which should save an object (using #RedisHash annotation) with String and Boolean properties and one custom class property, which also has only Strings and Longs as properties.
When I save an object (via the repository), everything went fine and I can see all the properties in the database as I would expect.
When I want to read the data from the database (via the repository) I only get the properties from the parent object. The custom class property is null.
I would expect to get the property loaded from the database as well. As the documentation states you can write a custom converter, but since I don't need to do that, when I want to write the data, I shouldn't need to write a reading converter as well.
I wonder if I need to annotate the custom class property, but I couldn't find anything in the documentation. Can you point me in the right direction?
The classes are as follows:
Class sample:
#Data
#EqualsAndHashCode(exclude = {"isActive", "sampleCreated", "sampleConfiguration"})
#RedisHash
public class Sample {
#Id
private String sampleIdentifier;
private Boolean isActive;
private Date sampleCreated;
private SampleConfiguration sampleConfiguration;
public Sample(String sampleIdentifier, SampleConfiguration sampleConfiguration){
this.sampleIdentifier = sampleIdentifier;
this.sampleConfiguration = sampleConfiguration;
}
}
Class SampleConfiguration:
#Data
public class SampleConfiguration {
private String surveyURL;
private Long blockingTime;
private String invitationTitle;
private String invitationText;
private String participateButtonText;
private String doNotParticipateButtonText;
private String optOutButtonText;
private Long frequencyCappingThreshold;
private Long optOutBlockingTime;
}
I added #NoArgsConstructor to my Sample class as Christoph Strobl suggested. Then the repository reads the SampleConfiguration correctly. Thanks, Christoph!

Resources