Mapping a Subclass to it's Parent Class - java

I have the following classes:
public class ParentClass implements Serializable {
private String field1;
private String field2;
public class ChildClass extends ParentClass implements Serializable {
private String field3;
private String field4;
In a service, I generate the ChildClass first, then I want to return only field1 and field2.
In order to achieve that, first, I tried to use modelmapper directly, using my autowired ModelMapper,
modelmapper.map(childClassInstance, ParentClass.class) but it generated an object of the ChildClass, which doesn't makes sense to me. I tried adding the TypeMap but I got
1) Converter com.sss.config.modelmapper.aaa.ClassesModelMapperConfig$$Lambda$1031/1744645655#28f3c1bf failed to convert com.sss.service.model.aaa.ChildClass to com.sss.swaggergen.model.ParentClass.
1 error
... 1024 common frames omitted
Caused by: org.modelmapper.MappingException: ModelMapper mapping errors:
Is there a way to solve this? I don't even need to use modelmapper for this and what I want is really just getting a subclass's in the context of a parent's class. So, I would be happy if I could just initate a ParentClass with a function such as:
final ParentClass initatedParent = chillClassInstance.isolateSuperClass() or something similar. I don't want to add constructors, so if there's no built in method to achieve this, I probably need to create a method that maps everything manually.

Related

Java Nesting Ignore Inner Attributes

I have an object model that is handling relations between types. These references sometimes loop back around on themselves, and to stop that (in say a REST call) I am using things like the #JsonIgnore tag to make sure I don't get infinite nest recursion.
The issue with this is more a question of context. If I want an item to be included in one spot but not another (if being nested), #JsonIgnore stops it from both spots.
Example:
public class A implements Serializable{
Set<B> bs;
Set<C> cs;
...
}
public class B implements Serializable{
String name;
Set<D> ds;
...
}
public class C implements Serializable{
B b;
...
}
public class D implements Serializable{
...
}
If A is my main container which is transporting the objects. In the context of B as listed in A, I want the Set<D> to show. When an object B is used in context of C however, I want to hide Set<D> and only show the name.
If I were to mark #JsonIgnore on B.ds it wouldn't show up in either case. Is there some annotation/customization I can put on C.b to ignore inner attributes? #JsonIgnore("ds") or something? Is there another way to handle this entirely?
You can annotate the fields with #JsonView and then specify the serialization view you want to use in particular circumstances. Here's a post about using it with SpringMVC but the approach would be the same regardless.
So in your specific example,
public class View {
interface Full {}
interface Summary {}
}
public class B implements Serializable{
#JsonView({View.Summary,View.Full})
String name;
#JsonView(View.Full)
Set<D> ds;
}

Spring MVC bind on a typed field

I have a Spring MVC controller like this:
#RequestMapping(value = "/search", method = RequestMethod.GET)
#ResponseBody
public Object grid(Search<MyFilter> search){
...
}
My Search object is like:
public class Search<F extends Filter> {
private int offset;
private int size;
private F filter;
//... getters/ setters
}
Filter is just an Interface. MyFilter is an implementation of Filter with some fields like name, title, etc.
I'm doing an HTTP GET to this controller with: /search?offset=0&size=10&filter.name=john
But spring can't instantiate MyFilter. I've tried to make Filter a normal empty class and MyFilter to extend it, but it's not possible either.
Is it possible to spring to instantiate the correct filter and then bind the values?
[TL;DR] No, Spring won't instantiate MyFilter from parametrized type.
The Search object is instantiated, when request arrives.
The problem is, that at runtime information about type parametrization is erased and converted to the most general type. I mean that at runtime your Search object effectively will look like:
class Search {
/*
* F is replaced by Filter since it is the most general type
* for <F extends Filter> parametrization.
* This will be the only Search class representation that compiler generates.
*/
private Filter filter;
//Rest of class body omitted
}
This is how generics works. Sicne Filter is an interface the object mapper has no clue how it should be resolved. You could guide object mapper how to resolve Filter into specific class based on i.e. additional type parameter (I belive that such case is answered here). But I don't think if mixing this with generics is good idea. It will lead to complex and confusing code.
You obviously depend on MyFilter implementation so you could use the approach from #DeezCashews suggestion. Or you could think of more flexible solutions. If all kinds of Filter are just containers of fields names and of wanted values you could do something like:
public class Search {
private int offset;
private int size;
private List<FieldPredicateTuple> filters;
//getters and setters omitted
}
public class FieldPredicateTuple {
String field;
String value;
//getters and setters omitted
}
And then call endpoint: /search?offset=0&size=10&filters[0].field=name&filters[0].value=john
I don't know how you want use those filters so I don't know what to propose. But I'd recommend to redesign this somehow.
I know this may not be exactly what you are looking for but if you are stuck perhaps consider extracting out the shared variables into its own class and then pass two parameters to your grid method instead of one. Spring Data has a Pageable class that is similar so perhaps you can look at that for inspiration. Then you can just pass a bean with the criteria and another bean with offset and size. Ex:
public Object grid(MyFilter f, Paged p);
public class MyFilter {
String name;
...
}
public class Paged {
int offset;
int size;
...
}

Jackson Deserialize with Subclasses

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.

How to inject the variable in the abstract class while unit testing the subclass?

I have an abstract class BaseTemplate and multiple classes extending it. In one of the concrete class(SmsTemplate extends BaseTemplate), we have a private variable Gson. We have the same private variable(Gson) in the abstract class as well.
While unit tesing the concrete class, methods in the abstract class is getting called from the concrete class. In my Unit test, I am using Whitebox.setInternalState(smsTemplateObj, gsonObj); to inject the Gson object into the private members of SmsTemplate and BaseTemplate but the Gson is getting injected only in the subclass. In abstract class, its NULL, meaning not injected. Below is the implementation.
Can someone please tell how to inject the Gson object in the abstract class?
abstract class BaseTemplate{
private Gson gson;//Here its not getting injected
protected String getContent(Content content){
return gson.toJson(content); // ERROR - gson here throws NPE as its not injected
}
}
class SmsTemplate extends BaseTemplate{
private Gson gson;//Here its getting injected
public String processTemplate(Content content){
String strContent = getContent(content);
...
...
gson.fromJson(strContent, Template.class);
}
}
Whitebox.setInternalState() method will only set the value of the first field it encounters going up through the hierarchy of the object you pass. So once it finds gson field in your subclass, it won't look further and won't change the superclass field.
There are two solutions for this case:
Change the variables names. If the variables have different names, you can simply invoke Whitebox.setInternalState() twice, one for each variable.
Set the field manually using reflection. You can also just set the field without Mockito's help using something like the following snippet.
Snippet:
Field field = smsTemplateObj.getClass().getSuperclass().getDeclaredField("gson");
field.setAccesible(true);
field.set(smsTemplateObj, gsonObj);
You need a second abstraction layer:
abstract class BaseTemplate{
// private Gson gson;// no Gson here
protected String getContent(Content content){
// do what you want without Gson
}
}
abstract class BaseTemplateWithGson extends BaseTemplate{
protected Gson gson;
#Override
protected String getContent(Content content){
return gson.toJson(content);
}
}
class SmsTemplate extends BaseTemplateWithGson {
public String processTemplate(Content content){
String strContent = getContent(content);
...
...
gson.fromJson(strContent, Template.class);
}
}

Datanucleus/mongodb: Persisting map of lists

I'm using datanucleus 3.2.5 / JDO for persisting objects to a MongoDB database.
While trying to persist one map of lists I'm getting the following exception:
RuntimeException: json can't serialize type [list element type here]
Some sample code:
#PersistenceCapable
public class SomeClass {
private Map<String, List<SomeOtherClass>> myAttribute;
// ...
}
#PersistenceCapable(embeddedOnly="true")
public class SomeOtherClass {
private String attribute;
// ...
}
I could get around this problem annotating the embedded attribute as #Serialized, but I would rather prefer a more elegant way.
Am I missing anything? Is there a better approach to this issue?
Quoting Andy's reply to my question in the DataNucleus forums:
No persistence spec defines any support for a container of a container. You are always recommended to make the inner container (List in your case) a field of an intermediate class.
So there are two approaches here:
Use an intermediate class
By far the most elegant and maintainable solution. Following the example toy code:
#PersistenceCapable
public class SomeClass {
private Map<String, SomeOtherClassContainer> myAttribute;
// ...
}
#PersistenceCapable(embeddedOnly="true")
public class SomeClassContainer {
private List<SomeOtherClass> myAttribute;
// ...
}
#PersistenceCapable(embeddedOnly="true")
public class SomeOtherClass {
private String attribute;
// ...
}
Mark the attribute as #Serializable
Ugly and probably a source of headaches, specially if relying on java default serialization.
#PersistenceCapable
public class SomeClass {
#Serializable
private Map<String, List<SomeOtherClass>> myAttribute;
// ...
}
#PersistenceCapable(embeddedOnly="true")
public class SomeOtherClass implements Serializable {
private String attribute;
// ...
}

Categories

Resources