How to achieve that some properties are ignored in the JSON-output (like XmlTransient for XML-output)?
#GET
#Path("/{companyId}")
#Produces(MediaType.APPLICATION_JSON)
public PortfolioCompany getCompany(#PathParam("companyId") long id);
I've been playing around with the RESTeasy-support in Seam 2.3 deployed as an EAR on a JBoss 7.1. I started by adding the same dependencies to my ejb-project as in the Seam-restbay-example.
It is basically working fine for #Produces(MediaType.APPLICATION_XML), where all properties annotated with #XmlTransient are ignored, in order to prevent some LazyInitialisationExceptions.
But how to achieve this behavior for #Produces(MediaType.APPLICATION_JSON)?
I've read Seam uses Jettison by default, which uses the #XmlTransient annotation for both, XML and JSON (because technically it transforms from XML -> to JSON). But I get a "Caused by: org.codehaus.jackson.map.JsonMappingException: No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer" which indicates that in fact it is using Jackson..?
For Jackson there is the annotation like #JsonIgnore, but having the same maven dependencies like restbay - this "cannot to be resolved to a type".
/**
* #return the contact
*/
#OneToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
#XmlTransient // working for produces XML but not for JSON
//#JsonIgnore = unknown type
public Contact getContact() {
return contact;
}
Anyone any experiences or hints on that?
thanks
EDIT: Really no one having the need of realizing lazy collections for REST-services with Seam??
After some research:
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property ="#id")
is what apparently would be needed. But that is only provided with Jackson 2.x. But the seam2.3/jboss7 setup is obviously using Jackson 1.9...
The default annotation mode of #XmlType/#XmlRootElement is to capture everyfield, including the lazy initializer from hibernate. Use #XmlAccessorType(XmlAccessType.NONE) in your entities and then individually annotate the fiels with #XmlElement so that only the necessary fields are processeded
Related
I developed a small application that stores data coming from a device: I chose to store data in JSON format, and the serialization/deserialization of the data works just fine, even if it involves some custom types created by me...but only I work in the IDE (Eclipse, for that matter).
When I export a runnable JAR file though, the deserialization of the data encounters some kind of problem, because the software always throws this exception:
Caused by: java.lang.UnsupportedOperationException: Cannot allocate class LocalDateTime
at com.google.gson.internal.UnsafeAllocator$4.newInstance(UnsafeAllocator.java:104)
at com.google.gson.internal.ConstructorConstructor$14.construct(ConstructorConstructor.java:225)
... 88 common frames omitted
I thought I'd encounter problems with custom types, not a built-in one. At this point, I discovered two things:
if I use a full JRE 9 to run the JAR file, the exception is not thrown: I double checked the modules included in the custom JRE I created with Jlink.exe, and everything is included correctly. I still want to use a smaller JRE, so I did not investigate further yet (I guess this explains why in the IDE it works perfectly)
I added a custom deserializer to the Gson object (see below), with which I simply manually converted the JSON string into a valid data, and that avoided the exception on the LocalDateTime class...but the exception reappeared simply on another class, this time a custom-made one.
At this point, I guess I can simply add a deserializer for each data type that causes problem, but I'm wondering why the issue won't happen with a full JRE, and why a smaller JRE causes this, even if all the modules required are included. Maybe it's worth mentioning also that I added no custom serializer to the Gson object that saves the data, it is all serialized as per Gson default.
LocalDateTime deserializer:
#Override
public LocalDateTime deserialize(JsonElement json, java.lang.reflect.Type type,
JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
JsonObject joDate = json.getAsJsonObject().get("date").getAsJsonObject();
JsonObject joTime = json.getAsJsonObject().get("time").getAsJsonObject();
//JSON example: {"date":{"year":2019,"month":1,"day":9},"time":{"hour":6,"minute":14,"second":1,"nano":0}
return LocalDateTime.of(joDate.get("year").getAsInt(),
joDate.get("month").getAsInt(),
joDate.get("day").getAsInt(),
joTime.get("hour").getAsInt(),
joTime.get("minute").getAsInt(),
joTime.get("second").getAsInt(),
joTime.get("nano").getAsInt());
}
}
Jdeps.deps modules list:
com.google.gson
java.base
javafx.base
javafx.controls
javafx.fxml
javafx.graphics
org.slf4j
After the answer I received, I opened an issue here.
TL;DR
You need a runtime image (e.g. full JDK or something built with jlink) that includes the module jdk.unsupported.
Full Answer
GSON wants to create instances of classes it deserializes without calling any constructors (so nothing gets initialized without GSON saying so). This can't normally be done, but sun.misc.Unsafe offers a way to do this with the method allocateInstance. To that end, GSON needs an instance of sun.misc.Unsafe. The topmost frame in the call stack is from UnsafeAllocator, which uses common trickery to get Unsafe.
The problem is, sun.misc.Unsafe is in module jdk.unsupported, which is present in a full JDK but you won't usually find in runtime images.
When creating your runtime image with jlink, make sure to include the option --add-modules jdk.unsupported and you should be good to go.
Arguably, GSON should declare an optional dependency on jdk.unsupported with requires static.
I have faced the same issue when packing compose a desktop application.
update build.gradle file, add an unsupported module.
compose.desktop {
application {
mainClass = "MainKt"
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "admin"
packageVersion = "1.0.0"
modules("java.sql")
modules("jdk.unsupported")
}
}
}
I am trying to create a very simple REST service using Jersey. Here is the service code
#Path("/UserService")
public class UserService {
#GET
#Path("/users")
#Produces(MediaType.APPLICATION_XML)
public List<User> getUsers() {
User user = new User(1, "Thomas", "Greene");
List<User> userList = new ArrayList<User>();
userList.add(user);
return userList;
}
}
When I run it through Postman, it returns me a XML response
Now, I want to get a JSON response back. So, I changed the mediatype to application/json:
#Path("/UserService")
public class UserService {
#GET
#Path("/users")
#Produces(MediaType.APPLICATION_JSON)
public List<User> getUsers(){
User user = new User(1, "Thomas", "Greene");
List<User> userList = new ArrayList<User>();
userList.add(user);
return userList;
}
}
It gives me the below error in Tomcat logs:
SEVERE: MessageBodyWriter not found for media type=application/json, type=class java.util.ArrayList, genericType=java.util.List.
Can someone please guide me how to get a JSON response back?
To use Jackson 2.x as your JSON provider you need to add jersey-media-json-jackson module to your pom.xml file:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.22.2</version>
</dependency>
And then register the JacksonFeature in your Application/ResourceConfig subclass.
For more details, have a look at Jersey documentation.
I am a bit upset about JAXB binding as well at the moment, therefore let me summarize my findings here - please correct me if I say something stupid:
Of course you have to have a library to do the JSON (de)serialization, in my case it is Moxy.
You have to tell JAXB which classes it should support. There are multiple ways to do that, the simplest seems to be to add a jaxb.properties file in the directory matching your classes and its sole content is the text javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory. With the directory I mean, if your classes are in the directory src/main/java and there the package com.pkg1.entities, add this file in src/main/resources and there as com/pkg1/entities/jaxb.properties.
By default JAXB works on POJOs. So you need to have a constructor without arguments, a get- and a set-method. Only then this field will be present in the JSON.
What I do often is to add a second constructor that gets the runtime object passed in and sets all fields to be exposed directly. Hence I do not need and do not want a set-method. Solution is to annotate the get method with #XmlElement.
Did I say you need an empty/default constructor? Took me once three hours to find out why class1 was working fine, class2 got the MessageBodyWriter error. I had forgotten the constructor. Grrrrr.
You get the same error (I believe) when the class is configured fine but one of its fields returns a type it cannot serialize.
I believe to have had one case where the class annotation #XmlRootElement caused that error. Not sure but I barely use that annotation at the moment.
In case you have a List as one of the elements to be turned into a Json array, JAXB will use the myAbstract class to do the serialization. Not very useful, you want the actual objects to be serialized. But how should JAXB know who implemented/extended this class? You have to tell with the annotation #XmlSeeAlso. So the MyAbstract class gets a class annotation #XmlSeeAlso({MyConcrete1.class, MyConcrete2.class}). At least Moxy does add then an additional type field telling the consumer which class it was. Makes total sense.
Although you can return userList the better option would be to return Response.ok().entity(userList).build(); Then you can return errors as well. Logically it is the same.
Watchout what kind of data types you use. String is fine, ArrayList as well, Hashtable not. Depends on the serializer you use as well.
I hope this helps others.
You need a json serializer on your class path to make this work.
Just add jackson and jersey will use this in the writer.
E.g. if you are using maven, add this to the pom.xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.7.4</version>
</dependency>
Your xml was working so I assume that you have #XmlRootElement annotation in your User class.
The thing is, it knows how to convert it to xml with the annotation #XmlRootElement but it doesn't know how to convert it to JSON.
So for making it convert everything to JSON with the same annotation of xml(ie #XmlRootElement) we can add
jersey-media-moxy-<whatever version>.jar
or for maven users
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
</dependency>
Also it should have a no argument constructor
I tried a ton of these dependencies but none of them worked for my version of Jersey3. What I needed to do was turn the Arraylist into an actual array. I managed this with toArray() and it started serializing correctly!
I'm trying to use SerializationConfig.Feature.WRITE_CHAR_ARRAYS_AS_JSON_ARRAYS but I'm not configuring the mapper myself, relying on annotations exclusively and letting Spring's RestTemplate (de)serialize automatically. Is there a way to enable the aforementioned feature in this scenario (i.e. annotations only)?
NOTE: I'm using Jackson 1.x and can't upgrade due to other libs...
With JAX-RS (like DropWizard) you can actually annotated resource endpoints, using #JacksonFeatures
public class Resource {
#Path("item")
#GET
#JacksonFeatures(serializationEnable={ SerializationFeature.WRAP_ROOT_VALUE })
public Pojo getItem(String id) {
...
}
}
I don't know if Spring exposes similar functionality, but it seems possible it does. And if not, it is something they should be able to add to allow per-endpoint setting/clearing of SerializationFeatures / DeserializationFeatures. So if it is not available, maybe file a feature request for Spring project?
Yes, it is possible.
checkout this link: http://jackson.codehaus.org/1.7.0/javadoc/org/codehaus/jackson/map/annotate/JsonSerialize.html
Example:
#JsonSerialize(using=MySerializer.class,
as=MySubClass.class,
include=JsonSerialize.Inclusion.NON_NULL,
typing=JsonSerialize.Typing.STATIC
)
i'm using the Restlet library for a WS server and i've recently switched from XStream/Jettison to Jackson as a JSON serializer/deserializer because of some issues.
A first drawback is that my ArrayList< Profile > (previously a Vector with Jettison) it doesn't wrap the list of Profiles when serialized and the JSON instead of "Profile:[{firstProfile}, {secondProfile}]" it looks like: [{firstProfile}, {secondProfile}]
I can overcome to this issue in the client telling manually which is the correct mapping but i would prefer to use a KVC approach.
I've looked around and it seems that it's a known issue: http://wiki.fasterxml.com/JacksonPolymorphicDeserialization (5.1 Missing type information on Serialization) that it suggest to:
Use arrays instead of Lists
Sub-class list, using class MyPojoList extends ArrayList { }
Force use of specific root type
the simplest way it should be to return an "Profile[] profile" array but it seems not working, before trying the other solutions i've rechecked around and it seems that you can use a #XmlRootElement(name = "Profile") to wrap the JSON root element: http://jira.codehaus.org/browse/JACKSON-163?focusedCommentId=213588&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-213588
so for using JAXB annotations with Jackson you need to configure the objectMapper: http://wiki.fasterxml.com/JacksonJAXBAnnotations
but in restlet to do so you need to override createObjectMapper to pass a Custom converter (see: http://restlet-discuss.1400322.n2.nabble.com/Set-custom-objectMapper-to-Jackson-Extension-td6287812.html and http://restlet-discuss.1400322.n2.nabble.com/Jackson-Mix-in-Annotations-td6211060.html#a6231831)
this is what i'm trying now! the question is there a more straightforward way to achieve this??
Thanks!!
the solution for me is to annotate the Profile class with:
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.WRAPPER_OBJECT)
public class Profile extends Element implements Serializable {
and now the json now looks like:
{"Profile":{ ... }}
and the return type is a Sub-classed list:
public class ProfileList extends ArrayList<Profile>
{}
see http://wiki.fasterxml.com/JacksonPolymorphicDeserialization 5.1
I think what you want is not really available in a sense that JAX-B seems to have some rules on how to deal with lists. See this converstation on the RESTeasy mailing list
Jackson #JsonIgnoreProperties not ignoring possibleTargets ,but ignoring owner and lemf .How i can ignore lists.
#JsonIgnoreProperties( {"owner","lemf"," possibleTargets"} )
#Entity
public class Warrant extends Nameable {
#LazyCollection(LazyCollectionOption.FALSE)
#OneToMany(mappedBy ="warrant",targetEntity = com.endersys.lims.model.Target.class)
private List<Target> possibleTargets;
Output:
[{"status":"SCHEDULED","startDate":1320962400000,"endDate":1320962400000,"caseId":"1","possibleTargets":[],"name":"warrant_1","description":"decription","identity":"warrant_1","version":1,"systemId":1,"active":true}]
I was also have problem lazyload because jackson was trying to serialize ignored fields.I have find a post related to this.Using jackson 1.9 problem solved ,I use #JsonIgnore but #JsonIgnoreProperties still not working on onetomany relations.
Use Jackson version 2.4 it works perfectly.
This is how you can implemented:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
#JsonIgnoreProperties({"id"})
In this developerWork post I found an example to implement Jackson v2.4 all the code and library is available.