I have a very basic Spring Boot app with .-data-jpa, .-data-rest and .-web dependencies.
In my model there is an entity Game that contains an Integer property homeGameSwitch.
When I get the resource with a REST call I get this exception:
.w.s.m.s.DefaultHandlerExceptionResolver : Failed to write HTTP
message:
org.springframework.http.converter.HttpMessageNotWritableException:
Could not write JSON: java.lang.Integer cannot be cast to
java.lang.String; nested exception is
com.fasterxml.jackson.databind.JsonMappingException: java.lang.Integer
cannot be cast to java.lang.String (through reference chain:
org.springframework.data.rest.webmvc.json.PersistentEntityJackson2Module$PersistentEntityResourceSerializer$1["content"]->com.coli.stripebackend.model.Game["homeGameSwitch"])
I find it strange that Jackson can't handle an Integer.
Is there something I can do prevent this error?
The entity:
#Entity
public class Game {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private Integer homeGameSwitch;
public Integer getHomeGameSwitch() {
return homeGameSwitch;
}
The DAO:
#Repository("gameDao")
public interface GameDao extends JpaRepository<Game, Integer> {}
The service:
#Service("gameService")
public class GameServiceImpl implements GameService {
#Autowired
private GameDao gameDao;
#Override
public Game retrieveGameById(Integer id) throws Exception {
Optional<Game> optionalGame = gameDao.findById(id);
return optionalGame.get();
}
The error occurs when calling localhost:8080/game/7
The problem was related to the version of Spring Boot. I was using the version 2.0.0.M2 in which there seems to be a bug.
I don't have the problem when using the version 1.5.4.RELEASE.
This is a warning to future readers who come here: There are many ways to trigger this exception: Some similar ones are collected in Jackson serialization exception when trying to serialize LocalDateTime
It seems I just ran into another way of getting the exact same exception: Have a class like this:
class Foo extends HashMap<String, String> {
public long bar;
}
and try to serialize it.
Related
I'm trying to build a simple application with Quarkus. Currently, I have two entity classes, which are related one-to-many:
#Entity
public class Person extends PanacheEntity {
public String name;
public LocalDate birthdate;
#OneToMany(mappedBy = "person")
public List<Address> addresses;
public static Person findByNameFirst(String name) {
return find("name", name).firstResult();
}
}
#Entity
public class Address extends PanacheEntity {
public String street;
...etc...
#ManyToOne
public Person person;
}
These are used by a simple REST webservice, which should store a Person to the database, select it again an return it:
#GET
#Path("storePerson")
#Produces(MediaType.APPLICATION_JSON)
#Transactional
public Person storePerson(
#QueryParam("name")String name,
#QueryParam("birthdate")String birthdate)
{
LocalDate birth = LocalDate.parse(birthdate, DateTimeFormatter.BASIC_ISO_DATE);
Person person = new Person(name, birth);
person.persistAndFlush();
Person p2 = Person.findByNameFirst(name);
return p2;
}
When calling the webservice the first time, the result is a JSON object with the stored data, which is as expected. When called again, an internal server error is thrown:
org.hibernate.LazyInitializationException: Unable to perform requested lazy initialization [Person.addresses] - no session and settings disallow loading outside the Session
As I understand, the error is thrown because the transaction only lasts until the storePerson method ends, but the conversion to JSON is happening outside of the method.
How can I prevent this error? I have read about the hibernate parameter "enable_lazy_load_no_trans" but it seems it is not supported in Quakus' application.properties.
The idea is to use a mapper framework such as MapStruct.
We don't recommend to directly expose your entities for 2 reasons:
the issue you have,
API management in the long run: you might have to change your model and not your API or the opposite.
There is an example here: https://github.com/mapstruct/mapstruct-examples/tree/master/mapstruct-quarkus .
The Quarkus version used is a bit old but AFAICS it should still work with latest Quarkus.
You can make the error go away by using Hibernate.initialize(person.addresses), then the collection gets initialized before the transaction ends.
Suppose I have the following JPA entities:
#Entity
public class Inner {
#Id private Long id;
private String name;
// getters/setters
}
#Entity
public class Outer {
#Id private Long id;
private String name;
#ManyToOne private Inner inner;
// getters/setters
}
Both Spring and java EE have REST implementations with default serializers which will marshall the entities to/from JSON without further coding. But when converting Outer to JSON, both Spring and EE nest a full copy of Inner within it:
// Outer
{
"id": "1234",
"name": "MyOuterName",
"inner": {
"id": "4321",
"name": "MyInnerName"
}
}
This is correct behavior but problematic for my web services, since the object graphs can get deep/complex and can contain circular references. Is there any way to configure the supplied marshaller to marshall the POJOs/entities in a "shallow" way instead without having to create a custom JSON serializer for each one? One custom serializer that works on all entities would be fine. I'd ideally like something like this:
// Outer
{
"id": "1234",
"name": "MyOuterName",
"innerId": "4321"
}
I'd also like it to "unmarshall" the JSON back into the equivalent java object. Bonus kudos if the solution works with both Spring and java EE. Thanks!
After many problems I give reason to Cássio Mazzochi Molin saying that "the use of entities persistence in your REST API can not be a good idea"
I would do that the business layer transform persistence entities to DTO.
You can do this very easily with libraries like mapstruct
If you still want to continue with this bad practice you can use jackson and customize your jackson mapper
To unscramble complex object graphs using jaxb #XmlID and #XmlIDREF is made for.
public class JSONTestCase {
#XmlRootElement
public static final class Entity {
private String id;
private String someInfo;
private DetailEntity detail;
#XmlIDREF
private DetailEntity detailAgain;
public Entity(String id, String someInfo, DetailEntity detail) {
this.id = id;
this.someInfo = someInfo;
this.detail = detail;
this.detailAgain = detail;
}
// default constructor, getters, setters
}
public static final class DetailEntity {
#XmlID
private String id;
private String someDetailInfo;
// constructors, getters, setters
}
#Test
public void testMarshalling() throws JAXBException {
Entity e = new Entity( "42", "info", new DetailEntity("47","detailInfo") );
JAXBContext context = org.eclipse.persistence.jaxb.JAXBContextFactory.createContext(new Class[]{Entity.class}, null);
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
m.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false);
m.marshal(e, System.out);
}
}
This will result in the following json-fragment
{
"detailAgain" : "47",
"detail" : {
"id" : "47",
"someDetailInfo" : "detailInfo"
},
"id" : "42",
"someInfo" : "info"
}
Unmarshalling of this json will ensure that detail and detailAgain are the same instances.
The two annotations are part of jaxb, so it will work in Spring as well as in java EE. Marshalling to json is not part of the standard, so i use moxy in the example.
Update
Explicitly using moxy is not neccessary in a JAX-RS Resource. The following snipped perfectly runs on a java-EE-7 container (glassfish 4.1.1) and results in the above json-fragment:
#Stateless
#Path("/entities")
public class EntityResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Entity getEntity() {
return new Entity( "42", "info", new DetailEntity("47","detailInfo") );
}
}
I had the same problem and ended up using jackson annotations on my Entities to control the serialization:
What you need is #JsonIdentityReference(alwaysAsId=true) to instruct the bean serializer that this reference should be only an ID. You can see an example on my repo:
https://github.com/sashokbg/company-rest-service/blob/master/src/main/java/bg/alexander/model/Order.java
#OneToMany(mappedBy="order", fetch=FetchType.EAGER)
#JsonIdentityReference(alwaysAsId=true) // otherwise first ref as POJO, others as id
private Set<OrderDetail> orderDetails;
If you want a full control of how your entities are represented as JSON, you can use JsonView to define which field is serialized related to your view.
#JsonView(Views.Public.class)
public int id;
#JsonView(Views.Public.class)
public String itemName;
#JsonView(Views.Internal.class)
public String ownerName;
http://www.baeldung.com/jackson-json-view-annotation
Cheers !
for this problem There are two solutions.
1-using jackson json view
2- Createing two mapping classe for innner entity. one of them includes custom fields and another one includes all fields ...
i think jackson json view is better solution ...
Go through the FLEXJSON library to smartly include/exclude nested class hierarchy while serializing Java objects.
Examples for flexjson.JSONSerializer presented here
You can detach the JPA entity before serialization, if you use lazyloading it's avoid to load sub objects.
Another way, but is depend of the JSON serializer API, you can use "transient" or specifics annotation.
Why does JPA have a #Transient annotation?
A bad way is to use tool like dozer to copy JPA object in another class with only the properties need for json (but it works... little overhead of memory, CPU and time...)
#Entity
public class Outer {
#Id private Long id;
private String name;
#ManyToOne private Inner inner;
//load manually inner.id
private final Long innerId;
// getters/setters
}
i'm coding a restService who can update some datas in a database via Nhibernate.
The service receive DTO objects from a client.
I'm using Automapper to map my Dto to NhibernateObject.
The problem is my DTO class reference itself. here an example :
public class UserDto
{
public String Name{get;set;}
public string Lastname{get;set;}
public UserDto UserOwner{get;set;}
}
here's my BusinessClass
public class User
{
public String Name{get;set;}
public string Lastname{get;set;}
public String Adress{get;set;}
public User UserOwner{get;set;}
}
Sometimes User object and UserOwner properties references the same object.
So when i do that
User usr = Automapper.Mapper.Map<UserDto,User>(myUserDtoObject); // this works fine
but when i do
Automapper.Mapper.Map(myUserdtoObject,MyUserNhibernateObject); // i've got a stackoverflowexception
I can use the first option but if i do that, when my new UserEntity returned by Map function is created the value of "Adress" properties is not set (UserDto does not contains it).
You need to use MaxDepth - AutoMapper doesn't know how far to go down your rabbit hole.
ForMember(dest => dest.UserOwner, opt => opt.MaxDepth(1))
This is important for NHibernate, which uses proxy objects to load indefinitely. The other option is to ignore the UserOwner member, but that's likely not your intent here.
I has a problem:There will be three child class Student,Teacher,Parent.
public class Person implements Serializable{
private String name;
private String address;}
Student:
public class Student extends Person {
private String cardNo;
}
spring rest :
#RequestMapping(method = RequestMethod.POST, value = "/create")
#ResponseBody
public CemeteryRestResponse<Boolean> create(
#RequestBody Person person) throws Exception {.....}
I want to use the one rest method to create these three role.
but in client post Student as JSON it throws a Exception :
org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "cardNo"
thanks in advance!
The Jackson library supports polymorphic type handling which is what it appears you are after. The specific annotation you should be looking for is the #JsonTypeInfo annotation, and you should apply it to the base class (the Person class).
This feature was added in version 1.5.
I am using Jackson in order to send data in JSON type between a client a server.
I am trying to use Jackson's full binding feature and I am applying it over a standard POJO.
The problem is that Jackson seem to add redundant data during marshaling on the server so when I try to unmarshall it back to the POJO on the client side I'm getting an error.
Here's an excerpt of the Jackson String:
{"_class":"com.mycoomp.MyObject","_id":{"time":1300314145000,"new":false,"machine":1652794940,"inc":-510750341},"language":"","type".....
MyObject contains "language" and "type" but it it doesn't contain “time”, “new” and “machine” that are not part of it but on the client side i'm getting this error:
Unrecognized field "time" (Class org.bson.types.ObjectId), not marked as ignorable at [Source: java.io.StringReader#1c56c60; line: 1, column: 102] (through reference chain: com.mycomp.MyObject["_id"]->org.bson.types.ObjectId["time"])
Any ideas...?
The solution is to provide a custom serializer/deserializer for ObjectId:
public class ObjectIdMapping {
public static class ObjectIdSerializer extends JsonSerializer<ObjectId>{
#Override
public void serialize(ObjectId id, JsonGenerator json,
SerializerProvider provider) throws IOException,
JsonProcessingException {
json.writeString(id.toString());
}
}
public static class ObjectIdDeerializer extends JsonDeserializer<ObjectId>{
#Override
public ObjectId deserialize(JsonParser jp, DeserializationContext context)
throws IOException, JsonProcessingException {
if (!ObjectId.isValid(jp.getText())) throw context.mappingException("invalid ObjectId " + jp.getText());
return new ObjectId(jp.getText());
}
}
}
And register them as any of the methods described in the documentation. For example, add in your POJO:
#JsonSerialize(using = ObjectIdMapping.ObjectIdSerializer.class)
#JsonDeserialize(using = ObjectIdMapping.ObjectIdDeerializer.class)
public ObjectId od;
You need to give type definitions for types you are serializing. Jackson does not add any entries that are not discoverable from objects (via getters, public fields, or explicitly annotated); except in cases where you add #JsonTypeInfo annotation to also add type identifier.
So maybe object you are serializing has more public fields that will be serialized?
I've just come across this as I had the same problem. Seems like a job for the mongo-jackson-mapper
I would also advise taking infrastructural classes such as ObjectId out of your domain.