I need to serialize an entity with only two column when it's called by a foreign key. I'am working in Wildfly, so I'am searching for a jackson solutions.
Suppose I have entity class A
public class A{
private Long id;
private String name;
private String anotherinfo;
private String foo;
...
}
and another class B:
public class B{
private Long id;
private String name;
private A parent;
}
I want to serialize A with all his field when i search for A, but when i need to retrieve an istance of B, i need only two field (an ID and a label)
If I use annotations:
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
#JsonIdentityReference(alwaysAsId=true)
private A parent;
i'll return only the id.
The result i want will be like:
B: {
"id" : 1,
"name" : "test",
"parent" : {
"id" : 1,
"name" : 2
}
}
You can use the JsonIgnoreProperties annotation, to disable specific fields for serialization (and deserialization):
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
public class B {
private Long id;
private String name;
#JsonIgnoreProperties({"anotherinfo", "foo"})
private A parent;
Have A extend another class, say C:
class C {
Long id;
String name;
}
class A extends C {
String anotherinfo;
String foo;
...
}
Then, in B:
class B {
Long id;
String name;
#JsonSerialize(as=C.class)
A parent;
}
When you serialize B, its parent field will have just the fields from C, but everywhere else that you serialize an A object you will see all the fields from both A and C.
For more information, take a look at https://github.com/FasterXML/jackson-annotations#annotations-for-choosing-moreless-specific-types
Solved adding a Json Serializer.
I have created an NationJsonSerializer for the parent class:
public class NationJsonSerializer extends JsonSerializer<TNation> {
#Override
public void serialize(TNation value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
jgen.writeStartObject();
jgen.writeNumberField("id", value.getId());
jgen.writeStringField("name", value.getComune());
jgen.writeStringField("iso", value.getCap());
jgen.writeEndObject();
}
}
Then,in the city class, i put the annotation
#JoinColumn(name = "idtnation",referencedColumnName = "id",nullable = true)
#ManyToOne(targetEntity = TNation.class, fetch = FetchType.EAGER)
#JsonSerialize(using = NationJsonSerializer.class)
private TNation nation;
So, if I use a method Nation n = getNation(long id); i'll receive all columns, but if i use getCity(), I'll receive a simplified version.
Related
I have trying to implement MapStruct mapping library. I have made samples and for simple mapping it works fine but I stucked in 1 issue.
I have 2 jpa entity classes which have two way relationships. One is in another and another is in one. It creates cyclic mapping issue so MapStruct throws StackOverflow error.
I have created minimal code to reproduce the case on github.
Sample code:
public class A {
private Long id;
private String name;
private B bData;
//getter-setter
}
public class B {
private Long id;
private String name;
private Set<A> aData;
//getter-setter
}
DataGenerator
public class DataGenerator {
public static A generateData(){
A a = new A();
a.setId(1L);
a.setName("foo");
B b = new B();
b.setId(2L);
b.setName("bar");
A a2 = new A();
a2.setId(3L);
a2.setName("john");
a2.setbData(b);
A a3 = new A();
a3.setId(4L);
a3.setName("doe");
a3.setbData(b);
Set<A> aData = new HashSet<A>();
aData.add(a2);
aData.add(a3);
b.setaData(aData);
a.setbData(b);
return a;
}
}
Mapper
#Mapper
public interface CustomMapper {
CustomMapper INSTANCE = Mappers.getMapper(CustomMapper.class);
ADto atoADto(A a);
}
App
public class AppMain {
public static void main(String[] args) {
A a = DataGenerator.generateData();
ADto aDto = CustomMapper.INSTANCE.atoADto(a);
System.out.println(aDto.getId());
}
}
Dto/Destination classes are same as original source classes.
The main is cyclic/recursive mapping issue which causes stackoverflow error.
Same thing working with spring BeanUtils.copyProperties but I want to implement MapStruct. Currently I am thinking to replace spring BeanUtils with MapStruct.
any suggestions?
See this mapstruct github issue for the solution, which is to ignore the field causing the recursion. I quote:
"You can achieve it with the #Qualifier. You can use #Named and qualifiedByName, or you can use your own custom #CountryWithoutCities qualifier with qualifiedBy.
Class country{
String id;
String name;
List<City> cities;
}
Class City{
String id;
String name;
Country country;
}
#Mapper(uses = CityMapper.class)
interface CountryMapper {
#Mapping( target = "cities", qualifiedByName = "noCountry")
CountryDto toDto(Country country);
#CountryWithoutCities
#Mapping( target = "cities", ignore = true)
CountryDto toDtoWithoutCities(Country country);
}
#Mapper(uses = CountryMapper.class)
interface CityMapper {
#Named( "noCountry" )
#Mapping( target = "country", ignore = true)
CityDto toDtoWithoutCountry(City city);
#Mapping( target = "country", qualifiedBy= CountryWithoutCities.class)
CityDto toDto(City city);
}
There's an example here in the MapStruct repo how to deal with cycles and recursion. Basically you need to keep track of state. The example makes use of a context object to do so.
I have a JSON object tree like this:
{
"name": "parent",
"children": [
{
"name":"child",
"value":5
}
]
}
In Java the parent class has a list of children marked with #JsonManagedReference and the child class has a corresponding #JsonBackReference. My problem is that the setter method of the value property depends on the parent. Sadly in all my experiments the back reference is resolved last and thus the parent field in the child object is still null when the value is set. Is there a way to change this deserialization order? Or is there another way to solve this elegantly without introducing unnecessary fields.
public class Parent {
#JsonManagedReference
private List<Child> children;
private String name;
//getter/setter
public int doStuff(int input) {
return 0; //complex calculations here
}
}
public class Child{
#JsonBackReference
private Parent parent;
private String name;
private int value;
//getter/setter
public void setValue(int v) {
this.value = getParent().doStuff(v);
}
}
I need to implement custom conversion for ID field in Company and Employee classes. I have already implemented custom converter extended from StrutsTypeConverter and it is successfully used to convert Company.ID field, but it does not work for Employee.ID.
Seems like the main problem is in conversion properties file. How should I specify converter class for employee ID field in conversion properties file?
MyAction-conversion.properties:
company.id = com.struts2.convertors.MyCustomConverter
company.??????.id = com.struts2.convertors.MyCustomConverter
MyAction:
public class MyAction extends ActionSupport {
private Company company;
public Company getCompany () {
return company;
}
public void setCompany (Company company) {
this.company= company;
}
#Override
public String execute() {
return SUCCESS;
}
}
Company:
public class Company {
private ID id;
private List<Employee> employees;
// getters and setters
}
Employee
public class Employee{
private ID id;
private String name;
// getters and setters
}
TypeConversion Annotation:
This annotation is used for class and application wide conversion rules.
The TypeConversion annotation can be applied at property and method level.
#TypeConversion(converter = “com.test.struts2.MyConverter”)
public void setAmount(String amount)
{
this.amount = amount;
}
This annotation specifies the location of one of my converters. literally, by using this annotation, I register my class com.test.struts2.MyConverter as a converter, and gets executed every time when setAmount(String amount) method is invoked.
Try the following by adding a converter for the ID type to the xwork-conversion.properties file
com.struts2.ID = com.struts2.convertors.MyCustomConverter
I would like to put into db a class that have java.awt.geom.Point2D field. Is it possible?
Here is my code.
#Entity
#Table(name = "my_class_table")
public class MyClass {
private String aliasId;
private Point2D field;
public Point2D getField() {
return field;
}
public void setFieldPoint2D field) {
this.field = field;
}
public String getAliasId() {
return aliasId;
}
public void setAliasId(String aliasId) {
this.aliasId = aliasId;
}
}
And the reason of the exception which is thrown:
Could not determine type for: java.awt.geom.Point2D, at table: my_class_table, for columns: [org.hibernate.mapping.Column(field)]
Of course, the reason is quite obvious. My question is: how should I annotate the class to be able to use a field of Point2D class? Is it possible at all?
The simplest way is to use a java.awt.Point that extends Point2D and is a Serializable class. This way hibernate will automatically map it with SerializableType and you don't need to do anything more. The point object will be saved in its serialized form in a blob database table column.
You have also the option to define a custom hibernate type for the Point2D class. Here is a link of how to define a custom hibernate type.
You can't add annotations to existing classes.
But you can define a CompositeUserType to tell Hibernate how to map a Point2D.
Thanks guys for response. Unfortunatelly java.awt.Point class uses Integer, so it is useless in my case. The easiest way to solve it would be to use Point2D.Double which implements Serializable (but definition of UserType or CompositeUserType is more convenient if you don't want to change class definition). So, the simple solution:
#Entity
#Table(name = "my_class_table")
public class MyClass {
private String aliasId;
private Point2D.Double field;
public Point2D.Double getField() {
return field;
}
public void setField(Point2D.Double field) {
this.field = field;
}
public String getAliasId() {
return aliasId;
}
public void setAliasId(String aliasId) {
this.aliasId = aliasId;
}
}
But my final goal was to create a class with ordered list of points. If anybody is interested here is an example of the class representing line:
#Entity
public class Line {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "idDb", unique = true, nullable = false)
private int id;
#ElementCollection
#CollectionTable(name="points_table", joinColumns = #JoinColumn(name="idDb"))
#IndexColumn(name = "idx")
#Column(name="point_val")
private List<Point2D.Double> points = new ArrayList<Point2D.Double>();
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public List<Point2D.Double> getPoints() {
return points;
}
public void setPoints(List<Point2D.Double> points) {
this.points = points;
}
}
The question may seems stupid, but for me a cycle reference is for example the object A refers an object B AND the object B refers the object A.
I am working with on a android application communicating with a GAE server with objectify DB.
My model is quite simple but I get a error:
org.codehaus.jackson.map.JsonMappingException: Direct self-reference leading to cycle (through reference chain: java.util.ArrayList[0]->com.my.model.MyMessage["senderKey"]->com.googlecode.objectify.Key["root"])
Here is my model: a MyMessage refers a MyUser (the MyUser DOESNT refer a MyMessage...
Here is the code:
public class MyMessage implements Serializable {
private static final long serialVersionUID = -1075184303389185795L;
#Id
private Long id;
#Unindexed
private String sendMessage;
#Unindexed
private String answerMessage;
private MessageStatus status = MessageStatus.FREE;
#Parent
Key<MyUser> senderKey;
Key<MyUser> answererKey;
#SuppressWarnings("unused")
private MyMessage() {
}
public MyMessage(MyUser user, String message) {
super();
this.sendMessage = message;
this.senderKey = new Key<MyUser>(MyUser.class, user.getId());
}
[... getters and setters ...]
}
.
public class MyUser implements Serializable {
private static final long serialVersionUID = 7390103290165670089L;
#Id private String id;
#SuppressWarnings("unused")
private MyUser() {
this.setId("default");
}
public MyUser(String mail) {
this.setId(mail);
}
public void setId(String mail) {
this.id = mail;
}
public String getId() {
return id;
}
}
So what is exactly a Direct self-reference ?? What is wrong with my model??
Thank you.
Key internally contains reference to parent Key, this is type-wise a reference to iteslf, i.e. a direct self-reference. This could potentially lead to endless loop, so Jackson is throwing an error.
Bottom line: Key is not serializable out-of-the-box. You might get by by writing a custom Jackson serializer/deserializer.