Problem with Deserialization of Generic Classes - java

I came across a problem with Generics and Jackson recently and ended up with not using it.
I have an interface MonetaryType:
public interface MonetaryType implements Serializable {}
which is implemented by multiple enums like:
public enum IncomeType implements MonetaryType {
FULL_TIME_SALARY,
PART_TIME_SALARY,
CHILD_BENEFIT
}
public enum ExpenseType implements MonetaryType {
HEAT,
CONDO_FEES,
ELECTRICITY
}
I created a Generic Class:
public MonetaryValidation<T extends MonetaryType> {
private T monetaryType;
private boolean isPassed;
private String message;
// Getters and Setters
}
This object is not deserialized by Jackson library. Meaning that if any Spring REST endpoints are called while the payload contains MonetaryValidation object, it throws below exception:
java.lang.IllegalArgumentException: Cannot construct instance of
**.enumeration.MonetaryType (no Creators, like default construct,
exist): abstract types either need to be mapped to concrete types,
have custom deserializer, or contain additional type information
I do not want to solve the issue with Jackson polymorphic deserialization approach since it requires the client to pass an extra flag specifying the concrete implementation of the interface or abstract class, as far as I understood.
Unfortunately I ended up creating multiple sub classes of non-generic MonetaryValidation (one subclass per each MonetaryType subclass), which I know it is not a decent solution.
It is much appreciated if you could help me out to understand where the problem is and whether there is an approach to use #JsonSubTypes while passing an extra field is not needed.

There is an idea, try accept monetaryType as the String type parameter, and you can custom converter in the Generic class for handling the generic type field, such as:
public void setMonetaryType(String monetaryType) {
Optional<IncomeType> incomeType = Arrays.stream(IncomeType.values()).filter(i -> i.name().equals(monetaryType)).findFirst();
incomeType.ifPresent(i -> {
this.monetaryType = (T)i;
});
Optional<ExpenseType> expenseType = Arrays.stream(ExpenseType.values()).filter(i -> i.name().equals(monetaryType)).findFirst();
expenseType.ifPresent(i -> {
this.monetaryType = (T)i;
});
}
I think this is a simply way to achieve other than using JsonSubTypes or custom Converters, since it's really a generic parameter.

Related

Unkown Class error trying to implement dependency injection

I'm using generics to get my code reusable and to utilize dependency injection.
I have two Interfaces: DataParserImplementation and ObjectImplementation. I have classes that implement each: SalesRepbyId implements DataParserImpl (it parses the data into objects and puts those objects into collections). SalesRep implements Objectimpl (It is the object for a specific dataset).
I'm trying to get it so that I can select which kind of Objectimpl I use in my SalesRepbyId class so I can remove the coupling.
I know there is something called reflection that I've been told is the method I need to use. I also have heard about a "Factory Pattern" and a "Properties file" that allows me to do what I want to do. A lot of this is very confusing so please explain it like I'm five.
Here is the code with where it stops working:
EDIT: Revisions based on comments: I want to specify the type of DataObject (D) my class uses by passing it through the constructor via a common interface and using generic types. When I try and use it instead of a concrete implementing class, I get the error. I can't find anything about this error.
public class SalesRepbyId<D extends ObjectImplementation> implements DataParserImplementation<Map<String,D>> {
private FileParserImplementation<ArrayList<String[]>> FileParser;
private D dataObject;
public SalesRepbyId(FileParserImplementation<ArrayList<String[]>> FileParser,D d){
this.FileParser = FileParser;
this.dataObject = d;
}
#Override
public Map<String, D> Parse() {
try{
//reads the file and returns an array of string arrays
ArrayList<String[]> Salesrep_contactlist = FileParser.ReadFile;
//here it still says "Unknown Class." that's the problem
Map<String, dataObject> SalesrepByIdMap = new HashMap<>();
//I want to be able to put in any class that implements
//dataObject into this class and have it run the same way.
Summary of what I did
I Implemented the Factory Design pattern and created a properties file which allowed me to reflect in the class I wanted instead of trying to use a generic DataObject (or D) type.
Details of Solution
Reflecting the class using the properties file "config.properties" and then casting it to type Objectimplementation allowed me to use any class that implemented that interface (and was implemented in the Factory and set in the properties file). I then refactored all instances of D to type ObjectImplementation since the parent interface is the layer of abstraction needed here rather than a generic concrete class.
Why it didn't work the way I tried it in the question
the reason the generic D type doesn't work with reflection is because reflection uses a concrete classtype determined at runtime and the generic D type is specified before runtime. Thus I was trying to reflect in the classtype and its methods/instances without properly using reflection and the code was telling me that the classtype was unknown at the time I needed it.
Code example to compare to the Question code
Example of the working code:
public class SalesRepbyId implements
DataParserImplementation<Map<String,ObjectImplementation>> {
private FileParserImplementation<ArrayList<String[]>> FileParser;
//the Factory class that creates instances of the reflected class I wanted
private ObjectFactory Factory = new ObjectFactory();
public Map<String, ObjectImplementation> Parse() {
//the proeprties object which then loads properties from a file and reflects the classtype I want
Properties prop = new Properties();
//loading in the classtype and casting it to the subclass of ObjectImplementation that it actually is
prop.load(SalesRepbyId.class.getResourceAsStream("config.properties"));
Class<? extends ObjectImplementation> Classtouse = Class.forName(prop.getProperty("ObjectImplementation")).asSubclass(ObjectImplementation.class);
//construct instances of 'Classtouse' and parse the data into these dynamically typed objects
//return the map that holds these objects
}

Why doesn't DefaultJackson2JavaTypeMapper.toJavaType() support abstract classes and interfaces as inferred types?

I would like to send messages in JSON format through RabbitMQ from one Java application to another using spring-amqp (1.7.4). The two applications do not share the same domain model classes.
I have a single generic #RabbitListener annotated method on the receiving end, that takes a single argument of type Event, an interface.
I have properly configured Jackson to handle the Event type hierarchy on both sides, yet, spring-rabbit won't convert my JSON message into the proper type because DefaultJackson2JavaTypeMapper does not support inferred abstract classes or interfaces.
If I define a custom JavaTypeMapper that extends DefaultJackson2JavaTypeMapper and does the following, it works perfectly fine:
#Override
public JavaType toJavaType(MessageProperties properties) {
boolean hasInferredTypeHeader = hasInferredTypeHeader(properties);
if (hasInferredTypeHeader && getTypePrecedence().equals(TypePrecedence.INFERRED)) {
// do not check for abstract classes and interfaces here
JavaType targetType = fromInferredTypeHeader(properties);
return targetType;
}
return super.toJavaType(properties);
}
Wouldn't it be better to leave the user in charge of how the conversion is to take place (either using spring-rabbit conventions or using Jackson directly)? Maybe add a flag that enables abstract classes and interfaces support? Is there something I'm missing?
Feel free to open an Improvement JIRA Issue.
Contributions are welcome along with suitable test cases.

Specify Bounded Type Parameter containing an annotation during compile time?

Can I check if a class contains a specific annotation during compile time if using generics?
I'm creating a wrapper class that will be the response to various HTTP calls.
What I want is for this wrapper class to allow the user to pass in an object of any type that is annotated with my own annotation.
For example, my wrapper class can be as follows:
public class HTTPResponse<T> {
private HttpStatus status;
private int statusCode;
private T data;
public HTTPResponse(HttpStatus status, T data) {
this.status = status;
this.statusCode = status.value();
this.data = data;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
//other getters/setters
}
When using this class, I can create a new HTTPResponse object and declare the type of the 'data' field:
HTTPResponse<SomePOJO> response = new HTTPResponse<>(HttpStatus.OK, new SomePOJO());
The idea is that anybody trying to use this class can pass in their own custom POJO during construction. However, I would like to be able to check that the object being passed in during construction is annotated with a custom interface created by me.
For example, my SomePOJO class needs to look like below:
#MyCustomInterface
public class SomePOJO() {
//code stuff here
}
Is there a way to check that the SomePOJO class is annotated with #MyCustomInterface during compile time?
I know that I can mark my annotation so that it will be available at runtime and then perform the check in the constructor of HTTPResponse class. But I would really like for the user of my class to know as soon as possible (during compile time) that they haven't yet marked their POJO with the correct interface and thus needs to do that before creating an HTTPResponse object.
Is an annotation even the right way to go in this case? Is it 100% impossible to check for class annotation during compile time somehow and I should force users to implement my custom interface then specify
HTTPResponse with public class HttpResponse<T implements MyCustomInterface> {
//fields, getters, setters
}
Thanks for reading if you managed to make it this far down!
The purpose of my custom annotation above was to just mark a class as an item that can be used in the HTTPResponse class. The difficult part was validating this during compile time.
The simple answer to this issue is to use Marker Interfaces!
In Joshua Bloch's Effective Java, he writes:
Marker interfaces have two advantages over marker annotations. First
and foremost, marker interfaces define a type that is implemented by
instances of the marked class; marker annotations do not. The
existence of this type allows you to catch errors at compile time that
you couldn't catch until runtime if you used a marker annotation.
Thus, the question I was asking above is invalid. You shouldn't be using marker annotations if you did want to catch errors at compile time. After changing my custom annotation to an interface, I specified my bounded generics class as follows:
HTTPResponse with public class HttpResponse<T implements MyCustomInterface> {
//fields, getters, setters
}

Separate jackson feature from classes

I am using jackson to handle JSON (de)-serialization. I have a bunch of classes which are annotated with are essentially objects holding properties and associated getters and setters.
However, I often find that at some point I want to add additional properties which I don't want to include in the (de)-serialization process. This does work using #JsonIgnore, but it strikes me as relatively ugly since I have to add the annotation everywhere and things break down as soon as I forget.
I would like to know if there is a better way to separate the ignored and serialized properties. I have the following two ideas:
Use inheritance, add the new properties to the inherited class:
// everything here should be (de)-serialized
class Base {
public int getJSONProperty() {...}
}
// nothing specific to the class Derived should be (de)-serialized
class Derived extends Base {
// *not* to be included
public SomeClass getAdditionalProperty() {...}
}
However, I don't know how to tell jackson to deserialize the Derived
objects as Bases. Is this possible (Does jackson make guarantees
regarding (non)-polymorphic serialization of classes)?
Use MixIn annotations. This would require an additional abstract
class for each existing class. Also I am not sure whether this solves
the problem. Are getters which do not appear in the MixIn base class
ignored automatically or do I need to #JsonIgnore them manually?
I've seen that you don't like the previous solution i've provided, so I'm again here to provide another way to do what you want using Gson Library. I hope to help you this time.
This is The Base Class that you want to serialize
public class Base {
public int getJSONProperty() {
return jsonProperty;
}
private int jsonProperty = 2;
}
This is The Derived Class that you don't want to serialize
public class Derived extends Base{
public String getAdditionalProperty(){
return additionalProperty;
}
private String additionalProperty = "value-not-to-serialize";
}
Using Type type = new TypeToken<Base>(){}.getType(); you can define the class to use for serialization so you get the JSON String using:
Derived derived = new Derived();
Gson gson = new Gson();
Type type = new TypeToken<Base>(){}.getType();
String jsonString = gson.toJson(derived, type);
You know Gson? it's a good library to handle JSON.
You can use transient keyword to define variable thats not must be serialized, this works with Gson (It should work well with jackson, but i'm not sure)...
class Base {
// (de)-serialized
private int jsonProperty;
// not (de)-serialized
private transient SomeClass additionalProperty;
}
I think the best approach is to add annotation or use the transient variable.
Create the inheritance only for the purpose of serialization an object complicates the application uselessly in my point of view...

How can I add my own EnumValueMapperSupport

I'm trying to persist some enums in Hibernate and it looks like my two options for built in support are to use the name of the enum, which I would rather not do because it's string based instead of int based, or the ordinal of the enum, which I would rather not do because if I add one of the enum values at the top of the class later on, I break everything down the line.
Instead, I have an interface called Identifiable that has public int getId() as part of its contract. This way, the enums I want to persist can implement Identifable and I can know that they'll define their own id.
But when I try to extend EnumValueMapperSupport so I can utilize this functionality, I'm greeted with errors from the compiler because the EnumValueMapper interface and the EnumValueMapperSupport class are not static, and thus are expected to be locked into a given EnumType object.
How can I extend this functionality in Hibernate, short of rewriting a bunch of Hibernate code and submitting a patch. If I can't, is there another way to somehow store an enum based on something other than the ordinal or name, but instead on your own code?
In a related thought, has anyone personally been down this road and decided "let's see how bad the name mapping is" and just went with name mapping because it wasn't that much worse performance? Like, is it possible I'm prematurely optimizing here?
I'm working against Hibernate version 5.0.2-final.
At least for Hibernate 4.3.5 the EnumValueMapper is static - although private.
But you can extend EnumValueMapperSupport in an extension of EnumType:
public class ExampleEnumType extends EnumType {
public class ExampleMapper extends EnumValueMapperSupport {
...
}
}
To create an instance of this mapper you need an instance of your EnumType:
ExampleEnumType type = new ExampleEnumType();
ExampleMapper mapper = type.new ExampleMapper();
Or you create it inside your type:
public class ExampleEnumType extends EnumType {
public class ExampleMapper extends EnumValueMapperSupport {
...
}
public ExampleMapper createMapper() {
return new ExampleMapper();
}
}

Categories