i read a lot about the use of TypeAdapter and JsonSerializer/Deserializer to deal with abstract class, my problem is in case of nested abstract class.
Let say this class:
abstract class A {
String content;
}
abstract class G {
String otherContent;
}
class B extends A {
G g;
}
class C extends A {
String someThing;
}
class H extends G {
Integer num;
}
I already coded a JsonSerializer/Deserializer class for each abstract class A and G.
I know I can use the chaining on: gsonbuilder.registerTypeAdapter(A_Adapter).registerTypeAdapter(G_Adapter), but i need to use something more like the TypeAdapterFactory to identify witch adapter to use (to specify the adapter class corresponding to the abstract class i used a java annotaion/reflection).
I also seen the TypeAdapter class but it too complex to implement due to the missing of the context element present in the JsonSerializer/Deserializer.
Any idea how to do it?
To use same serializer and desrializer for every subtype of Aclass(A+B+C) you can use registerTypeHierarchyAdapter(A.class, new A_Adapter())
From documentation:
Configures Gson for custom serialization or deserialization for an
inheritance type hierarchy. This method combines the registration of a
TypeAdapter, JsonSerializer and a JsonDeserializer. If a type adapter
was previously registered for the specified type hierarchy, it is
overridden. If a type adapter is registered for a specific type in the
type hierarchy, it will be invoked instead of the one registered for
the type hierarchy.
Related
I'm working with JSON data and converting it into Java POJO class, then I built an interface like this:
public interface DataUtil<T extends DataUtil<T>> {
default T someDefaultFn() { ... };
}
And I used this interface for some POJO data class like:
public MyPoJo extends DataUtil<MyPojo> { ... }
And I get stuck when I to try check the type variable Class<FType> (a FieldType of any fields are declared inside T class) whether FType extends DataUtil<FType extends DataUtil<FType>> or not? I'm also trying to research the java.reflect package but did not find the expected answer. Can anyone help me to resolve this problem or find another design solution for this scenario?
I'm writing a custom Java annotation for processing CrudRepositories with Reflection in Java Spring. With the org.reflections.reflections library. I'm getting all interfaces annotated with my annotation as a class file like so:
Reflections reflections = new Reflections("basePackage");
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(MyAnnotation.class);
Only interfaces, which at some point extend JpaRepository are annotated with my #MyAnnotation at the class level.
My repository structure is as follows:
There are two cases,
first case:
public interface SomeRepo extends JpaRepository<SomeEntity, Long> {...}
the second case is composed out of a inheritance hierarchy:
public interface SuperClassRepo <T extends SomeRandomEntity> extends JpaRepository<T, String> {...}
public interface SubClassRepo extends SuperClassRepo<SubEntityOfSomeRandomEntity> {...}
My goal is now to extract the generic type parameters of the underlying JpaRepository.
I achieved to do that if the annotated class is a Java class, not an interface. How can I achieve the same for an interface? I guess I'm also having trouble because of the inheritance. I guess I have to get the "super class" until I reach the JpaRepository and then somewhat extract the generic type arguments.
Help is very much appreciated, thanks in advance
I found a solution by looking at the GenericsUtils#getParameterType as suggested:
private static Class<?> extractKeyFromRepository(Class<?> repository) {
ResolvableType currentType = ResolvableType.forType(repository);
ResolvableType resolvableType = currentType.getInterfaces()[0];
if (JpaRepository.class.equals(resolvableType.getRawClass())) {
ResolvableType[] generics = resolvableType.getGenerics();
ResolvableType generic = generics[1];
return generic.resolve();
} else {
return extractKeyFromRepository(resolvableType.toClass());
}
}
This works only because I ensure beforehand, that what I'm putting in this method is valid. I do it like so, also I pay attention that only one interface is extended, by extending more than one interface one has to figure out which of these interfaces is the JpaRepository:
if (!repository.isInterface()) {throw new IllegalArgumentException();}
if (!JpaRepository.class.isAssignableFrom(repository)) {throw new IllegalArgumentException();}
Consider an interface with several implementations
Public interface A {}
#DynamoDBDocument
Public class C1 implements A {
variables…
}
#DynamoDBDocument
Public class C2 implements A {
variables…
}
#DynamoDBTable
Public class TopLevelClass {
A obj; //It can be any implementation C1, C2 ….
//Getter and setter of A
}
Saving an object of type C1 in the dynamoDB works fine, but we are unable to deserialize it back. Jackson does it by adding a #class attribute to the json to help it figure out which implementation to instantiate, however DynamoDBMapper has no idea and ends up trying to instantiate an object of A which obviously fails as it’s just an interface.
Is there a way we can achieve this? We don’t want to use marshallers as it will just deserialize the whole object as string and defeats the purpose of DynamoDBDocument type.
Ok, I know there are a bunch of similar questions, but nothing seems to work.
I have the following structure set up for my entities.
public abstract class MyAbstractClass {
// bunch of properties, getters, and setters that subclasses share
public abstract String getType();
}
public class MySubclass1 extends MyAbstractClass {
// a few unique properties, getters, and setters
public String getType() {
return "Type_1"; //always the same for each instance of MySubclass1
}
}
public class MySubclass2 extends MyAbstractClass {
// a few unique properties, getters, and setters
public String getType() {
return "Type_2"; //always the same for each instance of MySubclass2
}
}
In my controller, I try to map a request to the following method.
public #RequestBody MyAbstractClass saveObject(#RequestBody MyAbstractClass mac) {
// call model to save object
}
I would like to use 1 controller method versus separate ones for the 2 entities. But using the above results in the following.
com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of path.to.my.entity.MyAbstractClass, problem: abstract types either need to be mapped to concrete types, have custom deserializer, or be instantiated with additional type information
Makes sense.
TRY 1
#JsonTypeInfo(use=JsonTypeInfo.Id.CLASS, include=JsonTypeInfo.As.PROPERTY, property="implementingClass")
public abstract class MyAbstractClass
What I think it does - adds a metadata implementingClass property that will store the subclass class.
What the result is.
Unexpected token (END_OBJECT), expected FIELD_NAME: missing property 'implementingClass' that is to contain type id (for class path.to.my.entity.MyAbstractClass)
Tried with "class" instead of "implementingClass" for the property and got similar results.
TRY 2
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.WRAPPER_OBJECT)
#JsonSubTypes({
#Type(name="MySubclass1", value=MySubclass1.class),
#Type(name="MySubclass2", value=MySubclass2.class)
})
public abstract class MyAbstractClass
What I think it does - uses the defined name to do some sort of wrapping thing.
What the result is.
Could not resolve type id 'myUuid' into a subtype of [simple type, class path.to.my.entity.MyAbstractClass]
Same results even when adding #JsonTypeName("MySubclass1") and #JsonTypeName("MySubclass2") to the 2 subclasses.
Other Tries
I tried a lot. Nothing works. Won't include everything here.
I feel like there should be a simple way to do this, but I just keep on configuring things incorrectly.
I feel like the getType could maybe be leveraged, but I don't want to add an actual property for type (it's just a helper method). Also I would like to do this with annotations versus other options.
Thank you.
I figured it out but I guess I'll answer in case anyone else has this problem.
I added a type property to my subclasses instead of just a helper method (one example included below).
public class MySubclass1 extends MyAbstractClass {
#Transient
private final String type = "TYPE_1";
public String getType() {
return type;
}
}
Then I did the following for my abstract superclass.
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
#JsonSubTypes({
#Type(name="TYPE_1", value=MySubclass1.class),
#Type(name="TYPE_2", value=MySubclass2.class)
})
public abstract class MyAbstractClass
When providing the JSON, I was sure to include the type. I won't include this because it's weird knockout insanity.
It's not great. But it worked.
While working on an web-application , I need to get a set of classes at few steps and I am thinking to separate this logic to a simple Factory so as based on the Class type We can create class instance as well init it with default values.
Current structure of Class hierarchy is
public interface DataPopulator<Source,Target>{
// some method decaration
}
Abstract class
public abstract class AbstractDataPopulator<Source,Target> implements DataPopulator<Source, Target>{
// some common implimentation
}
And now classes which will be used as actual implementations like
Type1Populator extends AbstractDataPopulator.
Type2Populator extends AbstractDataPopulator.
Each of these implementation needs a set of common dependencies based on what functionality is being executed by those Populators.
As of Now I am creating instance with new and than filling those dependencies with simple setter methods.
I am thinking about creating a simple factory pattern like
public interface PopulatorFactory{
<T extends Object> T create(String className) throws Exception;
<T extends Object> T create(Class populatorClass) throws Exception;
}
Abstract class
public abstract class DefaultPopulatorFactory impliments PopulatorFactory{
public <T> T create(final Class populatorClass) throws Exception{
return Class.forName(populatorClass);
}
// other method.
}
Implementation classes
public Type1PopulatorFactory extends DefaultPopulatorFactory {
public <T> T create(final Class populatorClass) throws Exception{
final T populator= super.create(populatorClass);
}
}
I also want to initialize newly created instances with some default values specific to each implementation, but I'm not sure what's the best way to do this?
Should I define another method say initDefaults?
What is the best way to pass those dependencies to these poulators.
Is the approach outlined by me fine or is it overly complicated?
In cases when you are building not-so-trivial objects it is usually better to use the Builder pattern instead of a Factory.
In your case if you don't need external data sources you can simply write constructors for your classes where you can supply the default values and get rid of the contraption in your question.
If you use the Builder pattern you can simplify your framework by using a Builder object for the common data and a SomeOtherBuilder which extends from Builder and adds the custom values of the specialized implementation. You can make your classes constructors which are taking a Builder object.
public class Builder {
// your fields go here
}
public class SomeOtherBuilder extends Builder {
// your specialized fields go here
}
public class YourClass {
public YourClass(Builder builder) {
// construct it here
}
}
You can also make your classes generic with using something like T extends Builder.