Process JSON with Jersey and Jackson - json

I am using jersey in Java. I want to get JSON data sent via a post request. However, I am not sure how to do this, despite my searching. I am able to receive JSON data at a path, yet I can't figure out how to parse it into java variables. I assume that I need to use jackson to do this. However, I don't understand how to pass the received JSON to jackson.
#Path("/register")
public class ResourceRegister
{
#POST
#Consumes(MediaType.APPLICATION_JSON)
public String RegisterUser(//not sure what to take in here to get the json )
{
//code to deal with the json
}

There are several ways of accepting the JSON and using it in back-end.
1. set POJO elements using JAXB APIs and use object of that POJO class to access passed parameters. this will be helpful while JSON size is large.
Example:
your service declaration would be as following
#Path("/register")
public class ResourceRegister
{
#POST
#Consumes(MediaType.APPLICATION_JSON)
public String RegisterUser(RegParams regParams)
{
//code to deal with the json
}
.....
}
and you will write a POJO like following
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#JsonIgnoreProperties(ignoreUnknown=true)
#JsonWriteNullProperties(false)
public class RegParams implements Serializable {
#JsonProperty("userId")
private long userId;
#JsonProperty("userName")
private String userName;
..
..
}
retrive JSON as a string and use jersey APIs to work with the same.
in this case you can declare your service as following
#Path("/register")
public class ResourceRegister
{
#POST
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String RegisterUser(#FormParam("jsonObj")String jsonString)
{
//code to deal with the json
}
.....
}
and you can process that string by using jersey APIs like following
ObjectMapper om = new ObjectMapper();
JsonNode mainNode = om.readTree(jsonString);
//access fields
mainNode.get..(as per data passed, string, int etc)
for more referance you can refer this or this

You just need to place #JsonProperty annotation to your class properties and add that class to your Resource method as paramater.
You might need #JsonIgnoreProperties annotation as well if you are not deserializing everything inside the incoming json
See below:
#POST
#Consumes(MediaType.APPLICATION_JSON)
public String registerUser(MyUser myUser)
{
//code to deal with the json
}
public class MyUser{
#JsonProperty
private String name;
#JsonProperty
private String surname;
//getters & setters & constructors if you need
}

Related

JSON parse error: Cannot deserialize when using spring RestTemplate.exhange

I am new to JSON and spring. Was able to write simple examples consuming REST webservices. But as I apply to realtime scenario, the JSON is nested, and I don't have control in changing the structure.
Below code giving me Parse Error. Cannot deserialize instance of pg.domain.LookupDo[] out of START_OBJECT token
LookupDo[] lookupDos = template.exchange(url, HttpMethod.GET, request, LookupDo[].class).getBody();
How do I structure domain object?
#JsonIgnoreProperties
public class LookupDo {
private String companyCode;
private String plant;
private String category;
private String value;
private String descr;
You need to match your class with your JSON structure
Response res = template.exchange(url, HttpMethod.GET, request, Response.class).getBody();
LookupDo[] lookupDos = res.getD().getResults();
public class Response{
private Data d;
...
}
public class Data{
private LookupDo[] results;
...
}

Role base Json output in Spring Boot

Is is possible to exclude JsonProperties in the output of a Spring Boot Rest call based on a defined condition? (eg. the role of the user)
Example:
public class Employee{
#JsonProperty
private String name;
#JsonProperty
private String fieldForManagerOnly;
#JsonProperty
private String fieldForEmployeeOnly;
}
I want to have the fieldForManagerOnly only serialized in the JSON output when the user has the ROLE manager.
I've already tried the solution with the #JsonView (as described in Latest Jackson integration improvements in Spring) but that solution is very limited as the #JsonView is bound to one controler method and I want to have only one controller method.
I've solved the problem myself. I used the JsonView solution but instead of an annotation I select the JsonView from code.
First you need an interface for the Views.
public class JsonViews {
public interface EmployeeView {}
public interface ManagerView {}
}
Mark the fields in the Model class with the #JsonView annotations.
public class Employee{
#JsonProperty
private String name;
#JsonView(JsonViews.ManagerView.class)
private String fieldForManagerOnly;
#JsonView(JsonViews.EmployeeView.class)
private String fieldForEmployeeOnly;
}
In your controller set the JsonView to use based on the role (or some other condition):
#RequestMapping(value = "/{employeeId}", method = RequestMethod.GET)
public ResponseEntity<MappingJacksonValue> getEmployee(#PathVariable long employeeId) {
Employee employee = employeeService.getEmployee(employeeId);
MappingJacksonValue jacksonValue = new MappingJacksonValue(employeeResourceAssembler.toResource(employee));
if (getRole().equals("MANAGER")) {
jacksonValue.setSerializationView(JsonViews.ManagerView.class);
} else if (getRole().equals("EMPLOYEE")) {
jacksonValue.setSerializationView(JsonViews.EmployeeView.class);
}
return new ResponseEntity<>(jacksonValue, HttpStatus.OK);
}
Annotate the field with
#JsonInclude(JsonInclude.Include.NON_NULL)
and make sure to set the field fieldForManagerOnly to null if the current user is not a manager.

How to send/accept JSON using JerseyTest Framework

I am attempting to write a simple test class that emulates a RESTful Web Service creating a Customer via a POST method. The following fails at assertEquals, I receive a 400 Bad Request response. I cannot use debugger to observe stack trace. However the console tells me the following...
INFO: Started listener bound to [localhost:9998]
INFO: [HttpServer] Started.
public class SimpleTest extends JerseyTestNg.ContainerPerMethodTest {
public class Customer {
public Customer() {}
public Customer(String name, int id) {
this.name = name;
this.id = id;
}
#JsonProperty("name")
private String name;
#JsonProperty("id")
private int id;
}
#Override
protected Application configure() {
return new ResourceConfig(MyService.class);
}
#Path("hello")
public static class MyService {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public final Response createCustomer(Customer customer) {
System.out.println("Customer data: " + customer.toString());
return Response.ok("customer created").build();
}
}
#Test
private void test() {
String json = "{" +
"\"name\": \"bill\", " +
"\"id\": 4" +
"}";
final Response response = target("hello").request(MediaType.APPLICATION_JSON_TYPE).post(Entity.json(json));
System.out.println(response.toString());
assertEquals(response.getStatus(), 200);
}
}
Instead of printing response.toString(), you can read the actual body using response.readEntity(String.class). What you will find in the body is an error message from Jackson
No suitable constructor found for type [simple type, class simple.SimpleTest$Customer]: can not instantiate from JSON object (need to add/enable type information?)
At first glance your Customer class looks ok; it has a default constructor. But the really problem is that Jackson cannot instantiate it because it is a non-static inner class. So to fix it, simply make the Customer class static.
public static class Customer {}
As a general rule, when working with JSON and Jackson with Jersey, often when you get a 400, it a a problem with Jackson, and Jackson is pretty good at spitting out a meaningful message that will help us debug.

Serialize Date in a JSON REST web service as ISO-8601 string

I have a JAX-RS application using JBoss AS 7.1, and I POST/GET JSON and XML objects which include Dates (java.util.Date):
#XmlRootElement
#XmlAccessorType(XmlAccessField.FIELD)
public class MyObject implements Serializable
{
#XmlSchemaType(name = "dateTime")
private Date date;
...
}
When I use #Produce("application/xml") on the get method, the objets are serialized as XML and the dates are converted into ISO-8601 strings (e.g. "2012-12-10T14:50:12.123+02:00").
However, if I use #Produce("application/json") on the get method, the dates in the JSON objects are timestamps (e.g. "1355147452530") instead of ISO-8601 strings.
How can I do to configure the JAX-RS implementation (RESTEasy) to serialize dates in JSON format as ISO-8601 strings instead of timestamps ?
Thank you for your answers.
Note: I also tried to use a custom JAX-RS provider to do the JSON serialization for Dates
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class CustomJsonDateProvider implements MessageBodyWriter<Date>
{
...
}
This provider seems to be registered by RESTeasy on JBoss startup:
[org.jboss.jaxrs] Adding JAX-RS provider classes: package.CustomJsonDateProvider
...
[org.jboss.resteasy.cdi.CdiInjectorFactory] No CDI beans found for class package.CustomJsonDateProvider. Using default ConstructorInjector.
but it is never used !
I assume your json parser is Jackson, try:
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd,HH:00", timezone="CET")
public Date date;
(since Jackson 2.0)
The default JBoss parser is Jettison, but I wasn't able to change the date format. So I switched to Jackson and added the following class to my project to configure it:
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class JacksonConfig implements ContextResolver<ObjectMapper>
{
private final ObjectMapper objectMapper;
public JacksonConfig()
{
objectMapper = new ObjectMapper();
objectMapper.configure(SerializationConfig.Feature.WRITE_DATES_AS_TIMESPAMPS, false);
}
#Override
public ObjectMapper getContext(Class<?> objectType)
{
return objectMapper;
}
}
Sorry people for yelling out loud - I found the answers here
http://wiki.fasterxml.com/JacksonFAQDateHandling,
here
http://wiki.fasterxml.com/JacksonFAQ#Serializing_Dates,
here
http://wiki.fasterxml.com/JacksonHowToCustomSerializers
here
http://jackson.codehaus.org/1.1.2/javadoc/org/codehaus/jackson/map/util/StdDateFormat.html
Using the #JsonSerialize(using= ... ) way:
public class JsonStdDateSerializer
extends JsonSerializer<Date> {
private static final DateFormat iso8601Format =
StdDateFormat.getBlueprintISO8601Format();
#Override
public void serialize(
Date date, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonProcessingException {
// clone because DateFormat is not thread-safe
DateFormat myformat = (DateFormat) iso8601Format.clone();
String formattedDate = myformat.format(date);
jgen.writeString(formattedDate);
}
}
Declare the same Serializer used by Soap/XML:
#XmlElement(name = "prealert_date")
#XmlSchemaType(name = "dateTime")
#JsonSerialize(using = XMLGregorianCalendarSerializer.class)
protected XMLGregorianCalendar prealertDate;

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