I am new to JAX-RS and I want to serve my list of items as JSON. My entity model is something like this:
public class Entity {
private String name;
private Date date;
private Float number;
}
This is how I am invoking the service:
#Path("/entities")
public class EntitiesController {
#GET
#Produces({"application/json"})
public List<Entity> getEntities() {
return EntityDAO.entitiesList();
}
}
However, the date is not formatted but is displayed as a long.
This answer shows how to format a date using a JsonSerializer. If I extend JsonSerializer, then where do I put that subclass in my project?
I figured a solution myself:
Under a new serializers package I created the CustomJsonDateSerializer class, which will be delegated the responsibility of formatting the date attribute thanks to the #JsonSerialize(...) annotation.
So I modified my Entity class adding that annotation ontop of the field:
#JsonSerialize(using = CustomJsonDateSerializer.class)
private Date date;
And this is the content of CustomJsonDateSerializer:
package serializers;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
public class CustomJsonDateSerializer extends JsonSerializer<Date> {
#Override
public void serialize(Date value, JsonGenerator jgen, SerializerProvider provider)
throws IOException, JsonGenerationException {
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyy");
String format = formatter.format(value);
jgen.writeString(format);
}
}
Related
Is there a way to skip some properties on deserialization but at the same time knowing are they presented or not?
{
"id": 123,
"name": "My Name",
"picture": {
// a lot of properties that's not important for me
}
}
#JsonIgnoreProperties(ignoreUnknown=true)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
private int id;
}
So, I ignoreUnknown is what I want as a default behavior because I don't want name field and all other fields that can exist. The value of picture fields also is not important. I just want to know was picture property available or not. How I can do that?
You can add a boolean property and custom deserializer which just reads given value and returns true. Jackson invokes custom deserializer only if property exists in payload.
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import java.io.File;
import java.io.IOException;
public class JsonApp {
public static void main(String[] args) throws Exception {
File jsonFile = new File("./src/main/resources/test.json");
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue(jsonFile, User.class));
}
}
class PropertyExistsJsonDeserializer extends JsonDeserializer<Boolean> {
#Override
public Boolean deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
p.readValueAsTree(); //consume value
return Boolean.TRUE;
}
}
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(JsonInclude.Include.NON_NULL)
class User {
private int id;
#JsonDeserialize(using = PropertyExistsJsonDeserializer.class)
#JsonProperty("picture")
private boolean pictureAvailable;
//getters, setters, toString
}
Above code prints:
User{id=123, pictureAvailable=true}
I have a SpringBoot Service with:
Model
public class Payload {
private final String id;
public Payload(String id){
this.id = id;
}
public String getId() {
return this.id;
}
}
Controller
#RestController
#RequestMapping("/payload")
public class PayloadController {
#RequestMapping(method = RequestMethod.POST)
public Payload post(#RequestBody final Payload payload) {
return payload;
}
}
I need this Controller to be able to handle JSON & XML requests and respond with the same format.
This works fine providing I set the Content-Type and Accept headers to the correct media types.
However, my XML payloads need to be in a subtly different structure to my JSON:
XML:
<Payload>
<id value="some-value"/>
</Payload>
JSON:
{
id: "some-value"
}
How do I ensure my id is wrapped in an xml node and has the "value" as an attribute?
I have tried using a #JsonSerialize and #JsonDeserialize annotation on my Payload class but as soon as I do this I get the following error when POSTing XML
{
"timestamp": "2019-10-01T12:06:35.593+0000",
"status": 415,
"error": "Unsupported Media Type",
"message": "Content type 'application/xml;charset=UTF-8' not supported",
"path": "/payload"
}
You need to register 2 converters:
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter for JSON.
org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter for XML.
Because, Payload class fits JSON payload you need to add only JsonCreator and JsonProperty annotations to make it work:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Payload {
private final String id;
#JsonCreator
public Payload(#JsonProperty(value = "id") String id) {
this.id = id;
}
public String getId() {
return this.id;
}
}
XML payload does not fit by default, so we need to implement custom serialiser:
import com.example.demo.model.Payload;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import java.io.IOException;
public class PayloadXmlSerializer extends JsonSerializer<Payload> {
#Override
public void serialize(Payload value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
ToXmlGenerator toXmlGenerator = (ToXmlGenerator) gen;
toXmlGenerator.writeStartObject();
toXmlGenerator.writeObjectFieldStart("id");
toXmlGenerator.setNextIsAttribute(true);
toXmlGenerator.writeFieldName("value");
toXmlGenerator.writeString(value.getId());
toXmlGenerator.setNextIsAttribute(false);
toXmlGenerator.writeEndObject();
toXmlGenerator.writeEndObject();
}
}
and deserialiser:
import com.example.demo.model.Payload;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonPointer;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.node.TextNode;
import java.io.IOException;
public class PayloadXmlDeserializer extends JsonDeserializer<Payload> {
#Override
public Payload deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
TreeNode root = p.readValueAsTree();
TreeNode value = root.at(JsonPointer.compile("/id/value"));
if (value.isMissingNode()) {
return new Payload(null);
}
TextNode textNode = (TextNode)value;
return new Payload(textNode.textValue());
}
}
Finally, we need to register above HTTP converters and custom serialiser/deserialiser:
import com.example.demo.model.Payload;
import com.example.jackson.PayloadXmlDeserializer;
import com.example.jackson.PayloadXmlSerializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;
#EnableWebMvc
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//JSON
converters.add(new MappingJackson2HttpMessageConverter());
// XML
converters.add(new MappingJackson2XmlHttpMessageConverter(Jackson2ObjectMapperBuilder
.xml()
.modules(payloadModule())
.build()));
}
public SimpleModule payloadModule() {
SimpleModule module = new SimpleModule();
module.addDeserializer(Payload.class, new PayloadXmlDeserializer());
module.addSerializer(Payload.class, new PayloadXmlSerializer());
return module;
}
}
See also:
Using Jackson to add XML attributes to manually-built node-tree
415 Unsupported MediaType for POST request in spring application
Spring MVC
DTO:
#Getter
#Setter
#ToString
public class TestDto {
#NotNull
private String id;
#NotNull
#DateTimeFormat(pattern = "YYYY-MM-DD'T'hh:mm:ss.SSSZ")
private Instant timestamp;
}
When I give this input
{"timestamp":"4/23/2018 11:32 PM","id":"132"}
It gives BAD_REQUEST (which it should), but I want to handle this malformed date and throw an exception with my custom exception.
How can I add this?
Since OP requested feature is not supported yet: https://github.com/FasterXML/jackson-annotations/issues/130
Trying to do the same thing with a bit longer approach by using custom deserializer for a field timestamp
Custom exception class:
import com.fasterxml.jackson.core.JsonProcessingException;
public class MyException extends JsonProcessingException {
public MyException(String message) {
super(message);
}
}
Custom Deserializer class:
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Date;
public class InstantDeserializer extends StdDeserializer<Instant> {
public InstantDeserializer() {
this(null);
}
public InstantDeserializer(Class<?> vc) {
super(vc);
}
private SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-DD'T'hh:mm:ss.SSS'Z'");
#Override
public Instant deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
Date date = null;
try {
date = sdf.parse(node.asText());
} catch (Exception e) {
throw new MyException("Instant field deserialization failed");
}
return date.toInstant();
}
}
Updated TestDto class:
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotNull;
import java.time.Instant;
#Getter
#Setter
#ToString
public class TestDto {
#NotNull
private String id;
#NotNull
#JsonDeserialize(using = InstantDeserializer.class)
#DateTimeFormat(pattern = "YYYY-MM-DD'T'hh:mm:ss.SSS'Z'")
private Instant timestamp;
}
Invalid Input request:
{"timestamp":"4/23/2018 11:32 PM","id":"132"}
Response:
{
"timestamp": 1552845180271,
"status": 400,
"error": "Bad Request",
"message": "JSON parse error: Instant field deserialization failed; nested exception is com.fasterxml.jackson.databind.JsonMappingException: Instant field deserialization failed (through reference chain: TestDto[\"timestamp\"])"
}
Valid Input Request:
{"timestamp":"2018-04-23T11:32:22.213Z","id":"132"}
Response:
{
"id": "132",
"timestamp": {
"epochSecond": 1514700142,
"nano": 213000000
}
}
If you do not like the way timestamp field is getting deserialized and would like to change that, this SO post will be helpful.
I have to read date string from json and set it to java object.
I have used the following annotations on my variable.
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
#JsonInclude(JsonInclude.Include.NON_NULL)
public class RequestObject {
#JsonProperty("doj")
#JsonDeserialize(using = CustomDateDeserializer.class, as=Date.class)
private Date doj;
public Date getDoj() {
return doj;
}
public void setDoj(Date doj) {
this.doj = doj;
}
}
Below is my Custom DateDeserializer.
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
#SuppressWarnings("serial")
public class CustomDateDeserializer extends JsonDeserializer<Date>{
#Override
public Date deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException, JsonProcessingException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String date = jsonParser.getText();
try{
return sdf.parse(date);
}catch(ParseException e){
throw new RuntimeException(e);
}
}
}
I am testing this using postman, below is my postman request.
{
"doj":"2017-12-27"
}
But I am getting the following error.
Caused by: java.lang.IllegalArgumentException: Invalid format: "2017-12-27" is too short
at org.joda.time.format.DateTimeFormatter.parseDateTime(DateTimeFormatter.java:945) ~[joda-time-2.9.1.jar:2.9.1]
at com.exxonmobil.ace.hybris.jaxb.DateAdapter.unmarshal(DateAdapter.java:45) ~[DateAdapter.class:?]
at com.exxonmobil.ace.hybris.jaxb.DateAdapter.unmarshal(DateAdapter.java:1) ~[DateAdapter.class:?]
at org.eclipse.persistence.internal.jaxb.XMLJavaTypeConverter.convertDataValueToObjectValue(XMLJavaTypeConverter.java:149) ~[org.eclipse.persistence.moxy-2.6.1.jar:?]
Could someone please let me know where I am going wrong.
Regards,
Farhan
i am writing a rest services where i am getting response as in format "1448994600000" for date but i need it to give response in date,month,year format.
if i send data by giving 12-2-2015 format it give me error
The request sent by the client was syntactically incorrect
In response i get "12333333333" format,i need it to response with 12-2-2015
i have used below code to deserialize it,but its not working what is going wrong in my code,Please guide me.
import java.io.Serializable;
import java.sql.Date;
import java.sql.Timestamp;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.codehaus.jackson.annotate.JsonAutoDetect;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
import org.codehaus.jackson.map.annotate.JsonSerialize;
#JsonAutoDetect
#Entity
#Table(name = "DataValueTable")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class DataValueTable implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue
#Column(name = "ID")
private long id;
#JsonSerialize(using=JsonDateSerializer.class)
#Column(name = "Time")
private Date time;
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
JsonDateSerializer.java
package com.beingjavaguys.model;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;
import org.springframework.stereotype.Component;
/**
* Used to serialize Java.util.Date, which is not a common JSON
* type, so we have to create a custom serialize method;.
*
* #author Loiane Groner
* http://loianegroner.com (English)
* http://loiane.com (Portuguese)
*/
#Component
public class JsonDateSerializer extends JsonSerializer<Date>{
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy");
#Override
public void serialize(Date date, JsonGenerator gen, SerializerProvider provider)
throws IOException, JsonProcessingException {
String formattedDate = dateFormat.format(date);
gen.writeString(formattedDate);
}
}
method in my controller
/* Getting List of objects in Json format in Spring Restful Services */
#RequestMapping(value = "/list", method = RequestMethod.GET)
public #ResponseBody List getDatalist() {
List DataList = null;
try {
DataList = dataServices.getDataEntityList();
} catch (Exception e) {
e.printStackTrace();
}
return DataList;
}
update data
#RequestMapping(value = "/updateData", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public #ResponseBody
Status updateData(#RequestBody DataValueTable dataObject) {
try {
//response.addHeader("Access-Control-Allow-Origin","*");
dataServices.insertData(dataObject);
return new Status(1, "Data updated Successfully !");
} catch (Exception e) {
// e.printStackTrace();
return new Status(0, e.toString());
}
}
You can use implementation which has been introduced to java8
Import specific libraries
import java.time.LocalDate
import java.time.ZoneId
import java.time.format.DateTimeFormatter
LocalDate localDate= LocalDate.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
String stamp = localDate.format(formatter);
If you need conversion from Date, you would do it as following
Date input = new Date();
LocalDate localDate = input.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
And the output could be sth like 13-01-2016