Deserialization errors in Jackson with duplicated objects with different elements - java

I'm trying to understand better some deserialization errors I'm seeing in Jackson in a legacy application being converted to a React front-end with Spring back-end.
I see the error when multiple objects of the same class are referenced in the JSON but they have different elements due to fields being ignored with #JsonIgnoreProperties to eliminate infinite recursion. The Spring application will send JSON to the front-end application from the database. We may manipulate a few fields but keep the same basic structure and when we post it back, it says it can't deserialize it. Here's a simplified example.
File named Child.java
public class Child implements java.io.Serializable {
private String name;
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Parent parent;
public Child() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Parent getParent() {
return parent;
}
public void setParent(Parent parent) {
this.parent = parent;
}
}
File name Parent.java
public class Parent implements java.io.Serializable {
private String name;
#OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
#JsonIgnoreProperties("parent")
private Set<Child> children = new HashSet<Child>(0);
public Parent() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Set<Child> getChildren() {
return children;
}
public void setChildren(Set<Child> children) {
this.children = children;
}
}
Controller (normally we would be saving to database here)
#ResponseBody
#RequestMapping(value = "/returnParent", method = RequestMethod.POST)
public ResponseEntity<Parent> returnParent(#RequestBody Child child) throws Exception {
return new ResponseEntity<Parent>(child.getParent(), HttpStatus.OK);
}
JSON posted
{
"name":"child1",
"parent":{
"name":"parent1",
"children":[
{
"name":"child1"
},
{
"name":"child2"
}
]
}
}
Error:
org.springframework.http.converter.HttpMessageNotReadableException:
JSON parse error: No _valueDeserializer assigned; nested exception is
com.fasterxml.jackson.databind.exc.MismatchedInputException: No
_valueDeserializer assigned\n at [Source: (org.springframework.util.StreamUtils$NonClosingInputStream); line: 7,
column: 24] (through reference chain:
...Child["parent"]->...Parent["children"]->java.util.HashSet[0]->...Child["name\
I know that if I add this
#JsonIgnoreProperties(value={"children"}, allowGetters=true)
private Parent parent;
to Child that it will suppress the error and ignore child.parent.children but what if we need to be able post back these nested structures? This feels like a bug that it can't read the same JSON it created.

Related

Java Spring Neo4jRepository unable to read a property of abstract type

I have just started experimenting with Neo4J/Java and expect this is an easy one I'm missing, and probably phrasing my queries wrong.
I have some model classes as follows:
#Node
public class Garment {
#Id
#GeneratedValue
private Long id;
private String name;
#Relationship(type = "DESIGNED_BY")
private Entity designer;
// Other properties getters/setters removed for readibility
public Entity getDesigner() {
return designer;
}
public void setDesigner(Entity designer) {
this.designer = designer;
}
}
public abstract class Entity {
#Id
#GeneratedValue
Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
#Node
public class Person extends Entity {
}
#Node
public class Company extends Entity {
}
And a corresponding repository
public interface Garment extends Neo4jRepository<Garment, Long> {
Garment findByName(String name);
}
I have no problem inserting, using repository.save(), this correctly adds everything; nodes, relationships. Fine. It gives Designers of type Person labels of Person, and Designers of type Company the label Company.
However, when I do a find, e.g. findByName(), findAll(). it is not matching the designer and just saying designer is null, according to the cipher being executed/logged it looks like it's trying to build a relationship there with nodes with an Entity label, which there are none.
How can I get my repository to return Garments with designers of Person and Companys. I expect this is going to be as simple as an annotation, in order to fix.
(Note I've tried adding a #Node on the entity type with Person and Company as labels, however it just results in every node being added as both a Person and a Company).

Force Jackson Backreference to be resolved first

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);
}
}

Deserializing JSON wrapper object with list returns null properties

I got json like below:
{"examinationTypes":[{"ExaminationTypeVO":{"id":1,"name":"Badanie krwi"}},{"ExaminationTypeVO":{"id":2,"name":"Spirometria"}},{"ExaminationTypeVO":{"id":3,"name":"Wymaz"}},{"ExaminationTypeVO":{"id":4,"name":"Ciśnienie"}},{"ExaminationTypeVO":{"id":5,"name":"EKG"}},{"ExaminationTypeVO":{"id":6,"name":"Elektrowstrząsy"}},{"ExaminationTypeVO":{"id":7,"name":"Tomografia"}},{"ExaminationTypeVO":{"id":8,"name":"Lewatywa"}},{"ExaminationTypeVO":{"id":9,"name":"Aneskopia"}},{"ExaminationTypeVO":{"id":10,"name":"Rektoskopia"}},{"ExaminationTypeVO":{"id":11,"name":"Kolonoskopioa"}},{"ExaminationTypeVO":{"id":12,"name":"Echo serca"}},{"ExaminationTypeVO":{"id":13,"name":"Ablacja"}},{"ExaminationTypeVO":{"id":14,"name":"Badnaie dopplerowskie"}},{"ExaminationTypeVO":{"id":15,"name":"Kapilaroskopia"}}]}
I have defined types:
#JsonRootName(value="ExaminationTypeVO")
#JsonIgnoreProperties(ignoreUnknown = true)
public class ExaminationTypeVO {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
and
public class ExaminationTypesVO {
private List<ExaminationTypeVO> examinationTypes;
public List<ExaminationTypeVO> getExaminationTypes() {
return examinationTypes;
}
public void setExaminationTypes(List<ExaminationTypeVO> examinationTypes) {
this.examinationTypes = examinationTypes;
}
When I am deserializing it like that:
ExaminationTypesVO l = m.readValue(result, ExaminationTypesVO.class);
I receive an wrapper object but the list inside contains objects of type ExaminationTypeVO with all properties set to null.
Can anybody help to figure it out?
Your issue is that you have an extra level of object that you are trying to deserialize. Trying to not be confusing as I explain this: you have an array of objects, those objects contain a single ExaminationTypeVO object.
If you are stuck with the structure of the JSON that you provided, then you will need to add another "level" to your deserialization. You can do this via a wrapper object inside of your ExaminationTypesVO class:
public class ExaminationTypesVO {
private List<ExaminationTypeVOWrapper> examinationTypes;
public List<ExaminationTypeVOWrapper> getExaminationTypes() {
return examinationTypes;
}
public void setExaminationTypes(List<ExaminationTypeVOWrapper> examinationTypes) {
this.examinationTypes = examinationTypes;
}
public static class ExaminationTypeVOWrapper {
private final ExaminationTypeVO examinationTypeVO;
#JsonCreator
public ExaminationTypeVOWrapper(#JsonProperty("ExaminationTypeVO") ExaminationTypeVO examinationTypeVO) {
this.examinationTypeVO = examinationTypeVO;
}
public ExaminationTypeVO getExaminationTypeVO() {
return examinationTypeVO;
}
}
}
If you have control over the JSON that you are deserializing, you can just remove the extra "level" (ExaminationTypeVO wrapping object) and not have to change your code. Your new JSON in this approach would look like:
{
"examinationTypes": [
{
"id": 1,
"name": "Badanie krwi"
}, ...
]
}
With either of these approaches you can remove both of the class-level annotations you have on ExaminationTypeVO.

Response with the parent class in SPRING REST

I have implemented a REST API with SPRING REST using Jackson (specifying in the pom.xml with the package org.codehaus.jackson - jackson-mapper-asl - 1.9.13). In the controller I have:
#Controller
#RequestMapping("/test")
public class TestController {
#RequestMapping(value="id", method= RequestMethod.GET)
#ResponseBody
public Parent findById(#PathVariable("id") int id) {
Child child = new Child();
child.setId(id);
child.setName("test");
return child;
}
}
For example in the Parent we could have:
public class Parent {
int id;
public void setId(int id) {
this.id = id;
}
}
And the child:
public class Child extend Parent {
String name;
public void setName(String name) {
this.name = name;
}
}
My problem is that I'd want the response to be only with the parent class and not with the child class (because now the response has the structure of the child).
One solution that would work for you is the following:
#JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE)
public class Parent {
#JsonProperty
int id;
public void setId(int id) {
this.id = id;
}
}
I have to admit that this solution is not terribly elegant since you need to add the #JsonProperty annotation to every field in the Parent class, but does have the benefit that no modifications are needed to any of it's subclasses
All the annotations that are present in the jackson library can be found here.
The javadoc for #JsonAutoDetectcan be found here

JPA - Using insertable/updatable

I am writing a webservice to maintain a database. I am trying to use JPA (EclipseLink) for the entity classes. However, the database uses natural primary keys and therefore there's potential that an update on the ID fields will fail due to foreign key constraints. Our DBA has provided a function to update the ID fields which will create a new parent record with the updated ID, update the child records to point to the new parent and delete the old parent.
If the ID fields could be updated "normally", I would have a situation like this:
#Entity
#Table(name = "PARENT")
public class Parent implements Serializable
{
private static final long serialVersionUID = 1L;
private String parent;
private String attribute;
private Set<Child> childs;
public Parent()
{
}
#Id
#Column(name = "PARENT")
public String getParent()
{
return this.parent;
}
public void setParent(String parent)
{
this.parent = parent;
}
#Column(name = "ATTRIBUTE")
public String getAttribute()
{
return this.attribute;
}
public void setAttribute(String attribute)
{
this.attribute = attribute;
}
#OneToMany(mappedBy = "parentBean")
public Set<Child> getChilds()
{
return this.childs;
}
public void setChilds(Set<Child> childs)
{
this.childs = childs;
}
}
#Entity
#Table(name = "CHILD")
public class Child implements Serializable
{
private static final long serialVersionUID = 1L;
private String child;
private String attribute;
private Parent parentBean;
public Child()
{
}
#Id
#Column(name = "CHILD")
public String getChild()
{
return this.child;
}
public void setChild(String child)
{
this.child = child;
}
#Column(name = "ATTRIBUTE")
public String getAttribute()
{
return this.attribute;
}
public void setAttribute(String attribute)
{
this.attribute = attribute;
}
#ManyToOne
#JoinColumn(name = "PARENT")
public Parent getParent()
{
return this.parent;
}
public void setParent(Parent parent)
{
this.parent = parent;
}
}
I also have a GenericServiceBean class with a method to call functions:
#Stateless
public class GenericServiceBean implements GenericService
{
#PersistenceContext(unitName = "PersistenceUnit")
EntityManager em;
public GenericServiceBean()
{
// empty
}
#Override
public <T> T create(T t)
{
em.persist(t);
return t;
}
#Override
public <T> void delete(T t)
{
t = em.merge(t);
em.remove(t);
}
#Override
public <T> T update(T t)
{
return em.merge(t);
}
#Override
public <T> T find(Class<T> type, Object id)
{
return em.find(type, id);
}
. . .
#Override
public String executeStoredFunctionWithNamedArguments(String functionName,
LinkedHashMap<String, String> namedArguments)
{
Session session = JpaHelper.getEntityManager(em).getServerSession();
StoredFunctionCall functionCall = new StoredFunctionCall();
functionCall.setProcedureName(functionName);
functionCall.setResult("RESULT", String.class);
for (String key : namedArguments.keySet())
{
functionCall.addNamedArgumentValue(key, namedArguments.get(key));
}
ValueReadQuery query = new ValueReadQuery();
query.setCall(functionCall);
String status = (String)session.executeQuery(query);
return status;
}
}
If I set the ID fields to be not editable:
#Id
#Column(name = "PARENT", udpatable=false)
public String getParent()
{
return this.parent;
}
and call parent.setParent(newParent) will this still update the ID in the entity object? How does this affect any child entities? Will they also be updated (or not)?
Another scenario I don't know how to deal with is where I need to update both the ID and another attribute. Should I call the function which updates (and commits) the ID in the database then make calls to set both the ID and attribute via the normal set* methods and then the persistence context will only commit the attribute change?
Perhaps this is a situation where JPA is not appropriate?
Any advice on this is greatly appreciated.
If I set the ID fields to be not editable (...) and call parent.setParent(newParent) will this still update the ID in the entity object? How does this affect any child entities? Will they also be updated (or not)?
updatable=false means that the column won't be part of the SQL UPDATE statement regardless of what you do at the object level so the Id shouldn't be updated. And I'm also tempted to say that child entities shouldn't be affected, especially since you're not cascading anything.
Another scenario I don't know how to deal with is where I need to update both the ID and another attribute (...)
Well, my understanding is that you'd have to call the function anyway so I would call it first.
Perhaps this is a situation where JPA is not appropriate?
I'm not sure raw SQL would deal better with your situation. Actually, the whole idea of changing primary keys sounds strange if I may.

Categories