Can I use jackson mixIns to modify "getters" behavior? - java

I'm in the need of do some clean up of some invisible characters (\r\n) and html tags for specific getters on my entities.
I've been trying to use mixIns to modify what's returned from the entity but I'm not sure how can I reference the target class in my MixIn so I can add the clean up logic there. From the my tests seems that not even my method is called.
This is what I have so far, but it never gets called
public abstract class BookMixIn {
#JsonProperty
public String getTitle() {
return StringUtils.deleteWhitespace(getTitle());
}
}
public class Book {
private String title;
// getter/setters omitted...
}
And the ObjectMapper config:
mapper.getSerializationConfig().addMixInAnnotations(com.company.Book.class,
com.company.BookMixIn.class);
mapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
String tmp = mapper.writeValueAsString(book);
log.info(tmp);
Can this be accomplished via MixIns?
Thanks

Jackson mix-ins are purely for associating annotations; they are not used for adding behavior (code).
So they would not help you here.
But the simple way that would work (possibly using mix-in too) is to add annotation for using custom serializer, which can use whatever filtering is needed:
#JsonSerialize(using=MyCoolSerializer.class) public String getTitle() { }
so either add that to POJO, if possible; but if not, associate it using mix-in.

If you are running Jackson 1.9, this works:
BookCleaner cleanBook = new BookCleaner(book);
mapper.getSerializationConfig().addMixInAnnotations(Book.class, BookMixIn.class);
mapper.writeValueAsString(cleanBook);
#JsonSerialize
class BookCleaner {
private Book book;
public BookCleaner(final Book book) { this.book = book; }
#JsonUnwrapped
public Book getBook() { return book; }
#JsonProperty("title")
public String getCleanTitle() { return cleanup(getBook().getTitle()); }
}
public interface BookMixIn {
#JsonIgnore public String getTitle();
}

I don't think it works like this; the class or interface is just used as a signature.
You could use AspectJ to modify the return value, but it might be easier to just create a decorator and serialize that instead of the underlying object.
Alternatively, you could create specific getters for the "safe" versions of things and use the #JsonProperty annotation to give it the name you need, and use #JsonIgnore on the "non-safe" getters.

Related

How to generate getters for lombok Builder?

I have the following definition:
#Value
#Builder(toBuilder = true)
public class MyEntity {
String name;
}
When trying to use a getter on a builder, e.g. MyEntityBuilder.getName(), IDEA states that it "Cannot resolve method". Also, IDEA doesn't auto-complete it.
The only available method (except build()) is the setter: MyEntityBuilder name(String name);
Is there a way to generate getter on lombok generated builders? Thanks.
Using Lombok 1.8.16.
There seems to be no option to do it out of the box according to the Lombok docs or the source code.
If you want to inspect the contents of the builder, the best you can do, it seems, is to build the object and inspect the field from there.
You mentioned you were using IntelliJ IDEA as your IDE. It so happens that there is an IDEA plugin exactly for this:
https://plugins.jetbrains.com/plugin/6317-lombok
Once the plugin is installed, IDEA should recognize Lombok's annotation syntax.
Builders do not,
generally,
have getters.
The builder pattern is this:
Create the builder.
Set values in the builder.
Call build().
As answer from DwB suggests builder pattern does not need nor use getters.
You can have a getter for Lombok builder values but I think that it would not be very useful. You can customize builder this way:
#Value
#Builder(toBuilder = true)
public class MyEntity {
String name;
String name2; // just something to set also
// Customized builder
public static class MyEntityBuilder {
private String name;
public String getName() {
return this.name;
}
// This is not actually needed but just as an example howto
// customise a setter.
public MyEntityBuilder name(String name) {
this.name = name;
return this;
}
}
}
And to test it (Junit5):
#Test
void test() {
MyEntityBuilder meb = MyEntity.builder();
var myEntity = meb
// You need to set this first to access it later
.name("Name #1")
// The benefit having a getter ?
.name2(meb.getName())
.build();
assertEquals(myEntity.getName(), myEntity.getName2());
}

How can I use Java Enums with Amazon DynamoDB and AWS SDK v2?

I am trying to implement a simple java event-handler lambda for AWS. It receives sqs events and should make appropriate updates to the dynamoDB table.
One of the attributes in this table is a status field that has 4 defined states; therefore I wanted to use an enum class in java and map it to this attribute.
Under AWS SDK v1 I could use the #DynamoDBTypeConvertedEnum annotation. But it does not exist anymore in v2. Instead, there is the #DynamoDbConvertedBy() which receives a converter class reference. There is also an EnumAttributeConverter class which should work nicely with it.
But for some reason, it does not work. The following is a snip from my current code:
#Data
#DynamoDbBean
#NoArgsConstructor
public class Task{
#Getter(onMethod_ = {#DynamoDbPartitionKey})
String id;
...
#Getter(onMethod_ = {#DynamoDbConvertedBy(EnumAttributeConverter.class)})
ExportTaskStatus status;
}
The enum looks as follows:
#RequiredArgsConstructor
public enum TaskStatus {
#JsonProperty("running") PROCESSING(1),
#JsonProperty("succeeded") COMPLETED(2),
#JsonProperty("cancelled") CANCELED(3),
#JsonProperty("failed") FAILED(4);
private final int order;
}
With this, I get the following exception when launching the application:
Class 'class software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.EnumAttributeConverter' appears to have no default constructor thus cannot be used with the BeanTableSchema
For anyone else coming here, it looks do me like just dropping the annotation from the enum altogether works just fine, i.e. the SDK applies the provided attribute converters implicitly. This is also mentioned in this Github issue. My own class looks like this (Brand is an enum here), and the enum is converted without any issues when fetching items.
#Value
#Builder(toBuilder = true)
#DynamoDbImmutable(builder = User.UserBuilder.class)
public class User {
#Getter(onMethod = #__({#DynamoDbPartitionKey}))
String id;
Brand brand;
...
}
How can I use Java Enums with Amazon DynamoDB and AWS SDK v2?
Although the documentation doesn't state it, the DynamoDbConvertedBy annotation requires any AttriuteConverter you supply to contain a parameterles default constructor
Unfortunately for you and me, whoever wrote many of the built-in AttributeConverter classes decided to use static create() methods to instantiate them instead of a constructor (maybe they're singletons under the covers? I don't know). This means anyone who wants to use these helpful constructor-less classes like InstantAsStringAttributeConverter and EnumAttributeConverter needs to wrap them in custom wrapper classes that simple parrot the converters we instantiated using create. For a non-generic typed class like InstantAsStringAttributeConverter, this is easy. Just create an wrapper class that parrots the instance you new up with create() and refer to that instead:
public class InstantAsStringAttributeConverterWithConstructor implements AttributeConverter<Instant> {
private final static InstantAsStringAttributeConverter CONVERTER = InstantAsStringAttributeConverter.create();
#Override
public AttributeValue transformFrom(Instant instant) {
return CONVERTER.transformFrom(instant);
}
#Override
public Instant transformTo(AttributeValue attributeValue) {
return CONVERTER.transformTo(attributeValue);
}
#Override
public EnhancedType<Instant> type() {
return CONVERTER.type();
}
#Override
public AttributeValueType attributeValueType() {
return CONVERTER.attributeValueType();
}
}
Then you update your annotation to point to that class intead of the actual underlying library class.
But wait, EnumAttributeConverter is a generic typed class, which means you need to go one step further. First, you need to create a version of the converter that wraps the official version but relies on a constructor taking in the type instead of static instantiation:
import software.amazon.awssdk.enhanced.dynamodb.AttributeConverter;
import software.amazon.awssdk.enhanced.dynamodb.AttributeValueType;
import software.amazon.awssdk.enhanced.dynamodb.EnhancedType;
import software.amazon.awssdk.enhanced.dynamodb.internal.converter.attribute.EnumAttributeConverter;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
public class EnumAttributeConverterWithConstructor<T extends Enum<T>> implements AttributeConverter<T> {
private final EnumAttributeConverter<T> converter;
public CustomEnumAttributeConverter(final Class<T> enumClass) {
this.converter = EnumAttributeConverter.create(enumClass);
}
#Override
public AttributeValue transformFrom(T t) {
return this.converter.transformFrom(t);
}
#Override
public T transformTo(AttributeValue attributeValue) {
return this.converter.transformTo(attributeValue);
}
#Override
public EnhancedType<T> type() {
return this.converter.type();
}
#Override
public AttributeValueType attributeValueType() {
return this.converter.attributeValueType();
}
}
But that only gets us half-way there-- now we need to generate a version for each enum type we want to convert that subclasses our custom class:
public class ExportTaskStatusAttributeConverter extends EnumAttributeConverterWithConstructor<ExportTaskStatus> {
public ExportTaskStatusAttributeConverter() {
super(ExportTaskStatus.class);
}
}
#DynamoDbConvertedBy(ExportTaskStatusAttributeConverter.class)
public ExportTaskStatus getStatus() { return this.status; }
Or the Lombok-y way:
#Getter(onMethod_ = {#DynamoDbConvertedBy(ExportTaskStatusAttributeConverter.class)})
ExportTaskStatus status;
It's a pain. It's a pain that could be solved with a little bit of tweaking and a tiny bit of reflection in the AWS SDK, but it's where we're at right now.
I am thinking that your annotations might actually be the problem here. I would remove all annotations that mention a constructor, and instead, write out your own constructor(s). For both Task and TaskStatus.
The dynamodb-enhanced SDK does this out of the box.
When you declare a #DynamoDbBean the DefaultAttributeConverterProvider provides a long list of possible ways to convert attributes between java types, including an EnumAttributeConverter which is used if type.rawClass().isEnum() is true. So you don't need to worry about it.
If you ever wanted to extend the number of converters, you would need to add the converterProviders annotation parameter, and declare the default one (or omit it), as well as any other providers you want.
Example:
#DynamoDbBean(converterProviders = { DefaultAttributeConverterProvider.class, MyCustomAttributeConverterProvider.class });
Solution based on watkinsmatthewp Answer:
public class TaskStatusConverter implements AttributeConverter<TaskStatus> {
#Delegate
private final EnumAttributeConverter<TaskStatus> converter;
public TaskStatusConverter() {
converter = EnumAttributeConverter.create(TaskStatus.class);
}
}
Task status attribute looks like this:
#Getter(onMethod_ = {#DynamoDbConvertedBy(TaskStatusConverter.class)})
TaskStatus status;

Generate MapStruct presence checker methods with Lombok

MapStruct is aware of source presence checking
and uses presence checker methods by default (if present of course) to verify if a field in a target object should be updated with a value in a source
object. Without presence checkers MapStruct by default updates only fields with non-null values.
I want to use DTO in a REST controller to implement partial update strategy using MapStruct's source presence checknig but since I use Lombok to generate getters and setters I also want to generate source presence checking methods.
There isn't any way to do that. SOURCE: Me. I'm a core lombok contributor.
However, there is a recent, and very long, feature request discussion on this very idea: Issue #2669: Generate hasXXX() method. It goes in multiple wrong directions, so I would suggest you start from the end, where some concrete plans on how to get there from here are listed. It's complicated.
In short I created a lombok extension which supports #PresenceChecker annotation and generates hasXXX() methods.
// Original code
#PresenceChecker
#Getter
#Setter
public class UserUpdateDto {
private String name;
}
//Generated code
public class UserUpdateDto {
private boolean hasName;
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
this.hasName = true;
}
public boolean hasName() {
return this.hasName;
}
}
More detailed answer can be found here

JAXB marshals "non-field"

I have a class called Ability that inherits from an abstract class CampaignObject.
CampaignObject has an abstract method called getInfo().
Abilities do not have their own Info. If they are asked for info, the info of their Owner should be provided.
When I use JAXB.marshal(...) to marshal that, it includes the owners info text as an XmlElement on every ability. Annotating Ability.getInfo() with #XmlTransient does not make a difference.
What can I do?
Thanks a lot.
Code like this:
#XmlRootElement(name = "ability")
public class Ability extends CampaignObject {
private String val;
private Hero owner;
...
#Override
#XmlTransient
public String getInfo() {
return getOwner().getInfo();
}
#XmlAttribute
public String getVal() {
return val;
}
#XmlTransient
public Hero getOwner() {
return owner;
}
I have circumvented the problem. I refactored/renamed getInfo() into retrieveInfo() and gave everything except Abilities a new getInfo().
Still, if anyone has an idea on how to actually solve the issue, I'm still willing to learn.
Try adding #XmlAccessorType(XmlAccessType.NONE) to your class to be sure it doesn"t use anything besides what you annotated for the mapping, this way, you should also be able to remove the #XmlTransient annotations.

How to extend object with no setters to obtain superclass state

I am extending a class to store some extra fields that I need to my application, but the class I am extending has no setter methods, and just a default constructor.
http://restfb.com/javadoc/com/restfb/types/Post.html#Post()
I am using a framework that requires the getters to be in a correct naming format as the fields in the type. However, as I cannot set the fields from the constructor, or from setters, I have kept a local copy of the initial object that I wish to store in my new object.
I then have overridden the methods to pull the data from the initial object as follows:
#Override
public String getMessage() {
return initialPost.getMessage();
}
This does not seem like a good way to do things, one annoying reason being that I would have to override every method if I wish to use all fields.
What is the best way to solve this issue? Would this be a use case for composition over inheritance?
I think I may have tried to combine both here, which seems incorrect!
public class MyPost extends Post{
private String postId;
private Post initialPost;
private PostType type;
private Brand brand;
private Product product;
private List<Photo.Image> postImages;
Thanks for all advice.
You indeed combined both composition and inheritance; which is a pretty confusing situation. I would go with inheritance since you are extending the behaviour of an object with a more specific purpose to just that object.
This also solves your problem because a public method from the Post class is also available as a public method from its subclasses (and as such, the framework can happily use getMessage() without you having to redefine it).
From my comment below:
Post is essentially an immutable object so it is not intended to be constructed by you. You could override the methods from Post in MyPost and add your own getters/setters, but you should reflect whether or not this is an approach you want to take.
An example of how you would implement this:
class Post {
private String body;
public String getBody() {
return body;
}
}
class MyPost extends Post {
private String body;
public void setBody(String body) {
this.body = body;
}
#Override
public String getBody() {
return body;
}
}
Now the getBody() method from the Post class is overridden by the selfdefined one from MyPost.

Categories