Jersey Jackson unmarshall JSON - json

I am working on an embedded jersey instance which will run a JAXB RESTful service. I have configured Jackson with two steps:
Adding this to my POM
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.23.2</version>
</dependency>
Registering it in my application
public HandheldApplication() {
scripts.add(HandheldServer.class);
scripts.add(BasicScript.class);
// Add JacksonFeature.
scripts.add(JacksonFeature.class);
scripts.add(LoggingFilter.class);
}
I have a complex object being passed back and forth as shown below:
package com.ziath.handheldserver.valueobjects;
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.*;
#SuppressWarnings("restriction")
#XmlRootElement
public class Widget {
private String key;
private String name;
private List<String> options = new ArrayList<String>();
private String value;
private String type;
public Widget(){
super();
}
public Widget(String key, String name, List<String> options, String value,
String type) {
super();
this.key = key;
this.name = name;
this.options = options;
this.value = value;
this.type = type;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getOptions() {
return options;
}
public void setOptions(List<String> options) {
this.options = options;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
When I execute this in a GET method as shown below:
#Override
#GET
#Path("getKeys")
#Produces(MediaType.APPLICATION_JSON)
public List<Widget> getKeys(#QueryParam(value = "page") int page)
This works fine and I get JSON back; however when I execute it is a PUT as shown below:
#Override
#PUT
#Path("validateKeys")
#Produces({MediaType.APPLICATION_JSON})
#Consumes(MediaType.APPLICATION_JSON)
public boolean validateKeys(#QueryParam(value = "page")int page, #QueryParam(value = "widgets")List<Widget> widgets)
When I execute a PUT to access this method I get a stack trace as follows:
Caused by: org.glassfish.jersey.internal.inject.ExtractorException: Error un-marshalling JAXB object of type: class com.ziath.handheldserver.valueobjects.Widget.
at org.glassfish.jersey.jaxb.internal.JaxbStringReaderProvider$RootElementProvider$1.fromString(JaxbStringReaderProvider.java:195)
at org.glassfish.jersey.server.internal.inject.AbstractParamValueExtractor.convert(AbstractParamValueExtractor.java:139)
at org.glassfish.jersey.server.internal.inject.AbstractParamValueExtractor.fromString(AbstractParamValueExtractor.java:130)
at org.glassfish.jersey.server.internal.inject.CollectionExtractor.extract(CollectionExtractor.java:88)
at org.glassfish.jersey.server.internal.inject.CollectionExtractor$ListValueOf.extract(CollectionExtractor.java:107)
at org.glassfish.jersey.server.internal.inject.QueryParamValueFactoryProvider$QueryParamValueFactory.provide(QueryParamValueFactoryProvider.java:89)
... 38 more
Caused by: javax.xml.bind.UnmarshalException
- with linked exception:
[org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1; Content is not allowed in prolog.]
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.createUnmarshalException(AbstractUnmarshallerImpl.java:335)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.createUnmarshalException(UnmarshallerImpl.java:563)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:249)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:214)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:140)
at javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(AbstractUnmarshallerImpl.java:123)
at org.glassfish.jersey.jaxb.internal.JaxbStringReaderProvider$RootElementProvider$1.fromString(JaxbStringReaderProvider.java:190)
... 43 more
So it seems to me that Jackson is correctly marshalling my POJO into JSON but trying to unmarshall it as XML. Note that I switched to Jackson away from MOXy because I needed to be able to handle collections coming back and forth and apparently MOXy cannot do that.
Is there a setting I've missed to tell Jackson/Jersey to go both ways for JSON?

Try removing #QueryParam(value = "widgets") because you should pass it as entity body - not query param.
#PUT
#Path("validateKeys")
#Produces({MediaType.APPLICATION_JSON})
#Consumes(MediaType.APPLICATION_JSON)
public boolean validateKeys(#QueryParam(value = "page")int page, List<Widget> widgets)
Also you can make wrapper class:
#XmlRootElement
public class Widgets {
private List<Widget> widgets;
// other fields, setters and getters
}
And then:
#PUT
#Path("validateKeys")
#Produces({MediaType.APPLICATION_JSON})
#Consumes(MediaType.APPLICATION_JSON)
public boolean validateKeys(#QueryParam(value = "page")int page, Widgets widgets)
I would suggest to read some discussions about REST design because you're using verbs in your paths:
Is this a bad REST URL?
Understanding REST: Verbs, error codes, and authentication

I was switching between QueryParam and FormParam to try and get one of them to work. If I use FormParam I also need to change the consumes to APPLICATION_FORM_URLENCODED.
The actual issue was that the default unmarshalling with Jackson was using XML because it was tagged as an XML resource - take that out! I finally managed to work out how to unmarshall from JSON by using a static fromString method. Then to handle the list; I cannot use a wrapper class because this needs to be highly cross language and exposing a wrapper with a list would have complicated the implementation from Python, C#, etc. The way to get it to accept a list with a wrapper is to post the name of the param (in this case widgets) multiple time. Then each JSON passed in will be called against the fromString method.

Related

rest api returns empty bracket for GET request

I implemented Rest api with Spring Boot. In my controller class, I have code to handle GET request which will return JSON if record found.
// SeqController.java
#Autowired
private SeqService seqService;
#RequestMapping(
value = "/api/seqs/{analysis_id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<SeqTb>> getSeqByAnalysisId(#PathVariable("analysis_id") String analysis_id) {
List<SeqTb> seqs = seqService.findByAnalysisId(analysis_id);
return new ResponseEntity(seqs, HttpStatus.OK);
}
I also create a bean class SeqServiceBean that extends the interface SeqService which in turn calls methods from the following JPA repository for query.
//SeqRepository.java
#Repository
public interface SeqRepository extends JpaRepository<SeqTb, Integer> {
#Override
public List<SeqTb> findAll();
public List<SeqTb> findByAnalysisId(String analysisId);
}
Problem is when I typed the url (http://localhost:8080/api/seqs/fdebfd6e-d046-4192-8b97-ac9f65dc2009) in my browser, it returned nothing but a pair of empty brackets. I just looked in the database and that record is indeed there. What did I do wrong?
A bit late to answer this quesiton, but in case anyone else is having this issue.
This problem may be caused by the class (that we want to be displayed as a json object) missing getter and/or setter methods.
In your case the "seqTab" class may be not have getters.
Without the getters our application can not extract the fileds to build the json object.
Example :
Sample user class
public class User {
private String firstname;
private String lasttname;
int age;
public User(){
}
public User(String fname, String lname, int age){
this.firstname = fname;
this.lasttname = lname;
this.age = age;
}
}
Sample rest controller
#RestController
public class SampleRS {
#RequestMapping(value = {"/sample/{input}"}, method = RequestMethod.GET , produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<User> startService(#PathVariable("input") String input){
User u = new User(input,"bikila",45);
return new ResponseEntity<User>(u,HttpStatus.OK);
}
}
// If we try to hit the endpoint /sample{input} .. e.g.
Request : localhost:8080/Sample/abebe
Response :
{}
But adding the getters for the User class will solve the problem.
Modified User class with getters
public class User {
private String firstname;
private String lasttname;
int age;
public User(){
}
public User(String fname, String lname, int age){
this.firstname = fname;
this.lasttname = lname;
this.age = age;
}
public String getFirstname() {
return firstname;
}
public String getLasttname() {
return lasttname;
}
public int getAge() {
return age;
}
}
Request : http://localhost:8080/sample/abebe
Response : {"firstname":"abebe","lasttname":"bikila","age":45}
Hope that helps !
In most of case, database driver jar is not deployed in server. Check deployment assembly of project in eclipse. Also see console message to check if it is showing driver jar not found.
If this is case simply deploy this jar in deployment assembly of eclipse.
One thing, if build path has this jdbc driverjar in eclipse, main method will connect to database. But if jar is not deployed jdbc connection will not happen over http request.

Hide JSON fields in Jersey RESTful

The thing is that I want to hide the null elements from a RESTFul JSON response (if it's possible).
The REST controller retrieves the information from a Mongo database and because this elements doesn't exist there I would like to ignore them when they are null.
This is my REST Controller (exposed with Jersey):
#Stateless
#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
#Path(PropertiesRestURIConstants.PROPERTIES)
#Produces(MediaType.APPLICATION_JSON)
#RequestScoped
public class GetPropertiesController {
#EJB(mappedName = PropertiesManagerRemote.MAPPED_NAME)
PropertiesManagerRemote propertiesManager;
#GET
#Path(PropertiesRestURIConstants.PROPERTIES_ALL)
public List<PropertyEntity> getAllProperties() throws DBLayerException {
return propertiesManager.getAllProperties();
}
...
...
...
}
This is my entity:
#Document(collection = "property")
public class PropertyEntity implements GenericEntity {
#Id
private String id;
private String propertyName;
private String propertyValue;
public PropertyEntity() {
}
public PropertyEntity(String propertyName, String propertyValue) {
this.propertyName = propertyName;
this.propertyValue = propertyValue;
}
...
...
...
}
And this is the result:
[{"id":"542c00c2ff5e0ba4ea58790d","propertyName":"property1","propertyValue":null},{"id":"542c00c2ff5e0ba4ea58790e","propertyName":"property2","propertyValue":null},{"id":"542c00c2ff5e0ba4ea58790f","propertyName":"property3","propertyValue":null}]
I use Spring Data for the persistence layer. I tried with JSONIgnore annotations and similar things, but nothing works for me.
Any help will be welcome.
Thanks in advance.
Try to annotate it this way:
#JsonInclude(Include.NON_EMPTY)
public class PropertyEntity implements GenericEntity {

Jackson with JAXB - abstract types instantiation

I'm facing problem with Jackson's ObjectMapper using JAXB annotations. To be concrete, I'm having collection with interface generic information and although I can deserialize input from XML, it is not possible with Jackson (using JAXB introspector). Maybe I'm just missing some configuration property or JAXB annotation? The problem is that "abstract types can only be instantiated with additional type information", I thought #XmlElementRef (or #XmlElement) with type information will handle this problem, but obviosly it does not.
Please note, that I want to stay only with JAXB annotations if possible.
E.g. using #JsonTypeInfo or #JsonDeserialize would be the last thing to do.
IEntry.java:
#XmlSeeAlso(Entry.class)
public interface IEntry {
String getValue();
}
Entry.java:
#XmlRootElement(name = "entry")
public class Entry implements IEntry {
#XmlElement(name = "value")
String value;
public Entry() {
}
public Entry(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
Aggregator.java:
#XmlRootElement(name = "aggregator")
public class Aggregator {
#XmlElementRef(type = Entry.class)
private Set<IEntry> entries;
public Aggregator() {
}
public Aggregator(Set<IEntry> entries) {
this.entries = entries;
}
public Set<IEntry> getEntries() {
return entries;
}
}
Test method:
#Test
public void testSerialization() throws Exception {
ObjectMapper om = new ObjectMapper();
AnnotationIntrospector intr = new JaxbAnnotationIntrospector();
om.getDeserializationConfig().withAnnotationIntrospector(intr);
String json = "{\"entries\":[{\"value\":\"X\"},{\"value\":\"Y\"},{\"value\":\"Z\"}]}\";\n}";
Aggregator agr = om.readValue(json, Aggregator.class);
}
Thanks for all response
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
I am not sure if Jackson supports this use case or not, but you appear to be using #XmlElementRef incorrectly. When you use #XmlElementRef the root element name associated with the class is used to determine the instance to be instantiated. If your example the node entries does not match the #XmlRootElement(name="entry") annotation.
You could try one of the following options (they all work with MOXy's JSON binding, see: http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html):
OPTION 1 - Change #XMLRootElement on Entry
#XmlRootElement(name = "entries")
public class Entry implements IEntry {
#XmlElement(name = "value")
String value;
public Entry() {
}
public Entry(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
OPTION #2 - Change the JSON Document
{"entry":[{"value":"X"},{"value":"Y"},{"value":"Z"}]}}
OPTION #3 - Use #XMLElement instead of #XMLElementRef
If you use the #XmlElement annotation you can specify on the field/property what the node name should be instead of relying on the #XmlRootElement annotation. Also if you annotate the fields you should specify #XmlAccessorType(XmlAccessType.FIELD) at the type level.
import java.util.Set;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "aggregator")
#XmlAccessorType(XmlAccessType.FIELD)
public class Aggregator {
#XmlElement(type = Entry.class)
private Set<IEntry> entries;
public Aggregator() {
}
public Aggregator(Set<IEntry> entries) {
this.entries = entries;
}
public Set<IEntry> getEntries() {
return entries;
}
}
For More Information
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-substitution.html
http://blog.bdoughan.com/2011/05/jaxb-and-interface-fronted-models.html
import org.codehaus.jackson.map.annotate.JsonDeserialize;
#JsonDeserialize(as = Entry.class)
public interface IEntry {
String getValue();
}

Jackson deserialization - with contained ArrayList<T>

Good day,
I am currently integration attempting to consume a REST service that produces JSON (written in .NET) using Jackson (with Jersey). The JSON consists of a possible error message and an array of objects. Below is a sample of the JSON returned as produced by Jersey's logging filter:
{
"error":null,
"object":"[{\"Id\":16,\"Class\":\"ReportType\",\"ClassID\":\"4\",\"ListItemParent_ID\":4,\"Item\":\"Pothole\",\"Description\":\"Pothole\",\"Sequence\":1,\"LastEditDate\":null,\"LastEditor\":null,\"ItemStatus\":\"Active\",\"ItemColor\":\"#00AF64\"}]"
}
I have two classes to represent the type (the outer ListResponse):
public class ListResponse {
public String error;
public ArrayList<ListItem> object;
public ListResponse() {
}
}
and (the inner ListItem):
public class ListItem {
#JsonProperty("Id")
public int id;
#JsonProperty("Class")
public String classType;
#JsonProperty("ClassID")
public String classId;
#JsonProperty("ListItemParent_ID")
public int parentId;
#JsonProperty("Item")
public String item;
#JsonProperty("Description")
public String description;
#JsonAnySetter
public void handleUnknown(String key, Object value) {}
public ListItem() {
}
}
The class that invokes and returns the JSON looks like this:
public class CitizenPlusService {
private Client client = null;
private WebResource service = null;
public CitizenPlusService() {
initializeService("http://localhost:59105/PlusService/");
}
private void initializeService(String baseURI) {
// Use the default client configuration.
ClientConfig clientConfig = new DefaultClientConfig();
clientConfig.getClasses().add(JacksonJsonProvider.class);
client = Client.create(clientConfig);
// Add a logging filter to track communication between server and client.
client.addFilter(new LoggingFilter());
// Add the base URI
service = client.resource(UriBuilder.fromUri(baseURI).build());
}
public ListResponse getListItems(String id) throws Exception
{
ListResponse response = service.path("GetListItems").path(id).accept(MediaType.APPLICATION_JSON_TYPE, MediaType.APPLICATION_XML_TYPE).get(ListResponse.class);
return response;
}
}
The important call here is the getListItems method. Running the code in a test harness, produces the following:
org.codehaus.jackson.map.JsonMappingException: Can not deserialize instance of java.util.ArrayList out of VALUE_STRING token
at [Source: java.io.StringReader#49497eb8; line: 1, column: 14] (through reference chain: citizenplus.types.ListResponse["object"])
Please assist.
Regards,
Carl-Peter Meyer
You may be missing a #JsonDeserialize attribute as the type information does get lost in generics at run-time. Also you should avoid using concrete classes for collections if you can.
public class ListResponse {
public String error;
#JsonDeserialize(as=ArrayList.class, contentAs=ListItem.class)
public List<ListItem> object;
}
Your problem is that the 'object' property value is a String and not an array! The string contains a JSON array but Jackson expects a native array (without the wrapping quotes).
I had the same problem and I created a custom deserializer, which will deserialize a string value to a generic collection of the desired type:
public class JsonCollectionDeserializer extends StdDeserializer<Object> implements ContextualDeserializer {
private final BeanProperty property;
/**
* Default constructor needed by Jackson to be able to call 'createContextual'.
* Beware, that the object created here will cause a NPE when used for deserializing!
*/
public JsonCollectionDeserializer() {
super(Collection.class);
this.property = null;
}
/**
* Constructor for the actual object to be used for deserializing.
*
* #param property this is the property/field which is to be serialized
*/
private JsonCollectionDeserializer(BeanProperty property) {
super(property.getType());
this.property = property;
}
#Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException {
return new JsonCollectionDeserializer(property);
}
#Override
public Object deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
switch (jp.getCurrentToken()) {
case VALUE_STRING:
// value is a string but we want it to be something else: unescape the string and convert it
return JacksonUtil.MAPPER.readValue(StringUtil.unescapeXml(jp.getText()), property.getType());
default:
// continue as normal: find the correct deserializer for the type and call it
return ctxt.findContextualValueDeserializer(property.getType(), property).deserialize(jp, ctxt);
}
}
}
Note that this deserializer will also work if the value actually is an array and not a string, because it delegates the actual deserialization accordingly.
In your example you would now have to annotate your collection field like so:
public class ListResponse {
public String error;
#JsonDeserialize(using = JsonCollectionDeserializer.class)
public ArrayList<ListItem> object;
public ListResponse() {}
}
And that should be it.
Note: JacksonUtil and StringUtil are custom classes, but you can easily replace them. For example by using new ObjectMapper() and org.apache.commons.lang3.StringEscapeUtils.
The register subTypes works!
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="type")
public interface Geometry {
}
public class Point implements Geometry{
private String type="Point";
....
}
public class Polygon implements Geometry{
private String type="Polygon";
....
}
public class LineString implements Geometry{
private String type="LineString";
....
}
GeoJson geojson= null;
ObjectMapper mapper = new ObjectMapper();
mapper.disable(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES);
mapper.registerSubtypes(Polygon.class,LineString.class,Point.class);
try {
geojson=mapper.readValue(source, GeoJson.class);
} catch (IOException e) {
e.printStackTrace();
}

MOXy JSON support

I'm using EclipseLink's MOXy as the JAXB implementation in my RESTEasy project.MOXy's advanced functionality which has been brought by annotations like #XmlDiscriminatorNode & Value helped me a lot. Everything's working fine except one thing: JSON support. I'm using JettisonMappedContext of RESTEasy but unfortunately there're only instance variable fields belong to the abstract superclass in my JSON after marshalling.
#XmlRootElement
#XmlDiscriminatorNode("#type")
public abstract class Entity {
public Entity(){}
public Entity(String id){
this.id = id;
}
private String id;
#XmlElement
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
Subclass:
#XmlRootElement
#XmlDiscriminatorValue("photo")
public class Photo extends Entity{
private String thumbnail;
public Photo(){}
public Photo(String id) {
super(id);
}
public void setThumbnail(String thumbnail) {
this.thumbnail = thumbnail;
}
#XmlElement(name="thumbnail")
public String getThumbnail() {
return thumbnail;
}
}
XML after marshalling:
<object type="photo">
<id>photoId423423</id>
<thumbnail>http://dsadasadas.dsadas</thumbnail>
</object>
JSON after marshalling:
"object":{"id":"photoId423423"}
Is there any other way to achieve this?
Thank you.
UPDATE 2
EclipseLink 2.4 has been released with MOXy's JSON binding:
http://www.eclipse.org/eclipselink/releases/2.4.php
UPDATE 1
Get a sneak peak of the native MOXy object-to-JSON binding being added in EclipseLink 2.4:
http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html
Ensure that you have included a file named jaxb.properties file with your model classes that contains the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Without this entry the reference implementation will be used, and the EclipseLink JAXB (MOXy) extensions will not appear in the resulting XML/JSON.
Using the #DescrimatorNode example from my blog, the XML produced would be:
<customer>
<contactInfo classifier="address-classifier">
<street>1 A Street</street>
</contactInfo>
</customer>
When I marshal leveraging Jettison:
StringWriter strWriter = new StringWriter();
MappedNamespaceConvention con = new MappedNamespaceConvention();
AbstractXMLStreamWriter w = new MappedXMLStreamWriter(con, strWriter);
marshaller.marshal(customer, w);
System.out.println(strWriter.toString());
Then I get the following JSON:
{"customer":{"contactInfo":{"#classifier":"address-classifier","street":"1 A Street"}}}
For more information on JAXB and JSON see:
http://bdoughan.blogspot.com/2011/04/jaxb-and-json-via-jettison.html