I am working in Java with Jackson.
I have an abstract class Base.class, which looks as follows:
#JsonDeserialize(as = Child1.class)
public abstract class Base {
#JsonProperty("field1")
private Boolean field1;
#JsonProperty("field2")
private Long field2;
//getters, setters
}
And a bunch of child classes that extend the abstract class, that look like this:
public class Child1 extends Base {
#JsonProperty("field3")
private Boolean field3;
#JsonProperty("field4")
private Long field4;
//getters, setters
}
When I deserialise the following JSON, it works perfectly well:
Base base = mapper.readValue(new File("src/test/resources/testerTemplate/example.json"), Base.class)
example.json :
{
"field1":true,
"field2":123456,
"field3":false,
"field4":123456,
}
The issue is that this limits this structure to one-to-one relationship between base and child classes - to deserialise into other child classes I would need to change the annotation every single time. I have tried other annotations but has not been able to work it out. How do I do this?
I have worked out a solution that allows the code to do what I need it to do, although I am not entirely happy with the outcome. Nonetheless, it works.
The solution is to extend the Base.class by an intermediate abstract class that will be deserialized instead of the Base.class, like so:
Base.class (contains the generic fields we want to include when deserialising child classes):
public abstract class Base {
#JsonProperty("field1")
private Boolean field1;
#JsonProperty("field2")
private Long field2;
//getters, setters
}
An "extension" class that we will use to deserialize a specific child:
#JsonDeserialize(as = Child1.class)
public abstract class BaseChild1 extends Base {}
And the Child1.class:
public class Child1 extends BaseChild1 {
#JsonProperty("field3")
private Boolean field3;
#JsonProperty("field4")
private Long field4;
//getters, setters
}
This means that for every child class, we would create an empty abstract class that extends from the Base.class.
We then deserialize as follows:
ObjectMapper mapper = new ObjectMapper();
BaseChild1 child1 = mapper.readValue(new File("src/test/resources/testerTemplate/example.json"), BaseChild1.class)
BaseChild2 child2 = mapper.readValue(new File("src/test/resources/testerTemplate/example.json"), BaseChild2.class)
...
It is not perfect, but it works.
Related
Trying to make a MapStruct implementation where I have a "parent"-object like this:
public abstract class Parent {
private String id;
}
Then I have children with a whole bunch of more attributes such as:
public class ChildA extends Parent{
private String name;
//And so on...
}
public class ChildB extends Parent{
private String address;
//And so on...
}
How do I represent this data-structure in MapStruct mappers? I only want to map the children and not the parent. I have successfully made a mapper to map a child with an abstract class, but I can't get the "parent" mapping to tag along without explicitly stating it inside the child-mappers.
Is there a way I can do something like:
#Mapping(source = "id" target = "targetId")
In a parent mapper, and then inherit that mapping statement to the children? I don't want the parent to have a mapper on its own, I just want it to hold that mapping statement to reduce redundancy.
I would love to extend my abstract child-mapper class with a parent class and then simply inherit. Is this possible?
I have a simple base class in which I want to have some common fields, like id etc. The base class is not an Entity by itself.
public class Base {
#Id
protected long id;
protected String someOtherCommonProperty;
}
And I have an entity class, extending the base class.
#Entity
public class Entity extends Base {
String name;
String address;
}
I would expect the entity class to inherit the fields from the base class, but what I get is
[ObjectBox] No #Id property found for 'Entity', add #Id on a not-null long property.
Is there any way to fix that, besides using interfaces and have a lot of duplicated code?
You can use the #BaseEntity annotation.
Have a look at the documentation: Objectbox - Entity Inheritence.
Shameless copy for future reference:
In addition to the #Entity annotation, we introduced a #BaseEntity annotation for base classes, which can be used instead of #Entity.
There three types of base classes, which are defined via annotations:
No annotation: The base class and its properties are not considered for persistence.
#BaseEntity: Properties are considered for persistence in sub classes, but the base class itself cannot be persisted.
#Entity: Properties are considered for persistence in sub classes, and the base class itself is a normally persisted entity.
Example:
// base class:
#BaseEntity
public abstract class Base {
#Id long id;
String baseString;
public Base() {
}
public Base(long id, String baseString) {
this.id = id;
this.baseString = baseString;
}
}
// sub class:
#Entity
public class Sub extends Base {
String subString;
public Sub() {
}
public Sub(long id, String baseString, String subString) {
super(id, baseString);
this.subString = subString;
}
}
I am trying to achieve a parent-child relation between some objects and I ran into a bit of trouble.
In my case, I am trying to store objects within other objects (e.g. container stores multiple items or other containers with items). The tricky part is that every object in the storage should be able to tell what it's outermost parent object is. While this seems to work in my in-memory database (using h2 at the moment), trying to get a JSON representation of all my storage items gives this (I return a List<StorageUnit> ):
Could not write JSON: Infinite recursion (StackOverflowError); nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: java.util.ArrayList[0]->com.warehousing.storage.FixedContentsCase["contents"]->java.util.ArrayList[0]->com.warehousing.storage.FixedContentsCase["contents"]->...
Here are the classes:
StorageUnit
#Entity
#Inheritance
public abstract class StorageUnit {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#ManyToOne
private Location location;
protected Long parentContainerId;
// <getters & setters>
public abstract List<StorageUnit> getContents();
}
FixedContentCase
#Entity
public class FixedContentsCase extends StorageUnit {
#OneToMany
private List<Item> items;
public FixedContentsCase() {
super();
items = new ArrayList<>();
}
// <getters & setters>
#Override
public List<StorageUnit> getContents() {
// Return the case itself and its contents
List<StorageUnit> contents = new ArrayList<>(Arrays.asList(this));
for (StorageUnit item : items)
contents.addAll(item.getContents());
return contents;
}
}
Item
#Entity
public class Item extends StorageUnit {
private String description;
public Item() {
super();
this.description = "";
}
// <getters & setters>
#Override
public List<StorageUnit> getContents() {
return Arrays.asList(this);
}
}
I have tried to annotate the StorageUnit class with #JsonIgnoreProperties("parentContainerId") but it didn't work. Annotating parentContainerId with #JsonIgnore didn't help either. I also tried annotating the getters instead of the attributes themselves (as per following). Is there a way to work around this or is some kind of design change necessary? Thanks!
Using Jackson this is definitely possible by annotations like #JsonIgnore or the DTO approach BugsForBreakfast mentioned.
I created a jackson MixIn handler to allow dynamic filtering which i use to avoid the boilerplate of DTOs
https://github.com/Antibrumm/jackson-antpathfilter
The examples in the readme should show how it works and if it‘s a possible solution for you.
Your problem is that you add the storage unit itself to its list of contents, leading to infinite recursion if you traverse the tree downwards. The solution: Use a reference and only serialize the object once, using #JsonIdentityInfo and #JsonIdentityReference:
public class MyTest {
#Test
public void myTest() throws JsonProcessingException {
final FixedContentsCase fcc = new FixedContentsCase();
fcc.setId(Long.valueOf(1));
final Item item = new Item();
item.setId(Long.valueOf(2));
item.setDescription("item 1");
fcc.getItems().add(item);
final ObjectMapper om = new ObjectMapper();
System.out.println(om.writeValueAsString(fcc));
}
}
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
#JsonIdentityReference(alwaysAsId = false)
class Item extends StorageUnit {
...
}
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
#JsonIdentityReference(alwaysAsId = false)
class FixedContentsCase extends StorageUnit {
...
}
abstract class StorageUnit {
...
}
I want to persist one class that extends another from an external API...
I've no access to API class so I can't anotate it as #MappedSuperclass or #Entity nor anotate its fields
But i would like to persit the public fields from superclass plus extra fields from my extended class.
//i've no access to modifications on this class as it comes from external api
public class BaseClass{
public int field1;
public int field2;
public int field3;
}
#Entity
public class MyClass extends BaseClass{
public int extraField1;
public int extraField2;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int extraField3;
}
How can I approach this situation?
I'm using Hibernate 5.1.0.Final
I'd suggest creating getters/setters for the inherited fields and using accessor-based annotations (like is shown in this answer)
I have many entities with common properties. There is no xml schema, so I write jaxb entities on my own.
abstract class SuperEntity {
protected String id;
protected String name;
#XmlElement
public void setId() { .. sets Id .. }
#XmlElement
public void setName() { .. sets name .. }
}
// id and name are null after deserialization .. they are completely ignored
// there are other entities such as this, I don't want to repeat my code
#XmlRootElement
#XmlSeeAlso({SuperEntity.class})
class SpecificEntity extends SuperEntity {
protected String specificField;
#XmlElement
public void setSpecificField() { .. sets specific field .. }
}
SuperEntity is not deserialized (unmarshelled) at all, leaving fields null. If i copy fields and setters from superclass to specific class, it works, but I dont want to just copy that code to every child entity. Thank you for your help.
Change the class definitions to
#XmlRootElement
#XmlSeeAlso({SpecificEntity.class})
abstract class SuperEntity {
#XmlRootElement
class SpecificEntity extends SuperEntity {
When JAXB is processing a class model it will also process super classes (the ones not annotated with #XmlTransient). By default it won't process subclasses. The #XmlSeeAlso needs to go on the super class and reference the subclasses.