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

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;

Related

In Spring, Deserialize Dates with or without zeros (yyyy-M-d) but Serialize them always with zeros (yyyy-MM-dd)?

I have a Spring application using
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.11.0</version>
</dependency>
I can receive a JSON object with a date field from the front-end, either with or without leading zeros (1991-2-3 or 1991-02-03). I know for this, using yyyy-M-d works for deserializing the input:
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class MyDto implements Serializable{
//other fields...
#JsonDeserialize(using = LocalDateDeserializer.class)
#JsonSerialize(using = LocalDateSerializer.class)
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-M-d")
LocalDate date;
}
The problem is it always returns dates without the padding of zeros, e.g. 1991-2-3. Is there a way I can get it to accept either pattern, but always serialize to include the zeros, e.g. 1991-02-03?
I figured-out that one can implement custom de/serializers in lieu of using #JsonFormat, specifying different formats with a DateTimeFormatter:
public class CustomLocalDateDeserializer extends JsonDeserializer<LocalDate> {
private final static Logger log = LoggerFactory.getLogger(CustomLocalDateDeserializer.class);
#Override
public LocalDate deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException{
String dateAsString = jsonParser.getText();
if(dateAsString.isEmpty()) return null;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-M-d");
LocalDate date = LocalDate.parse(dateAsString, formatter);
return date;
}
}
and
public class CustomLocalDateSerializer extends JsonSerializer<LocalDate>{
#Override
public void serialize(LocalDate value, JsonGenerator generator, SerializerProvider provider) throws IOException{
if (value == null) {
generator.writeNull();
} else {
generator.writeString(value.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
}
}
}
then, just use the custom de/serializers in the annotations
#JsonDeserialize(using = CustomLocalDateDeserializer.class)
#JsonSerialize(using = CustomLocalDateSerializer.class)
private LocalDate aLocalDate;

Spring Boot http message converter serialize null sometimes

In Spring boot, is it possible to have many different versions of gson or Jackson http converters and use them dynamically whenever I need a specific type of data format?
You have to create two beans for GsonHttpMessageConverter the first one with settings by default and second one with setting for serializing nulls by following way:
#Bean
public GsonHttpMessageConverter gsonHttpMessageConverter() {
return buildGsonHttpMessageConverter(MapperUtil.getGsonInstance());
}
#Bean
public GsonHttpMessageConverter gsonHttpMessageConverterWithNulls() {
return buildGsonHttpMessageConverter(MapperUtil.getGsonInstanceSerializeNulls());
}
private GsonHttpMessageConverter buildGsonHttpMessageConverter(final Gson gson) {
final GsonHttpMessageConverter converter = new GsonHttpMessageConverter();
converter.setGson(gson);
return converter;
}
And when you want to use one of them then call #Qualifier("someBean") annotation. by following way:
#Autowired
#Qualifier("gsonHttpMessageConverter")
GsonHttpMessageConverter gsonHttpMessageConverter;
#Autowired
#Qualifier("gsonHttpMessageConverterWithNulls")
GsonHttpMessageConverter gsonHttpMessageConverterWithNulls;

How to change the data fomat of the JSON node while serializing and deserializing in jackson

I have following Incoming json, which I deserialize to Model.java and then copy that java object to ModelView.java. I wan't to convert date from String to milliseconds and send the Outgoing json as response.
How do I go for it ?
I've specific reason to copy the value from Model.java to ModelView.java using object mapper. So please don't suggest to modify that part. I'm looking to do this via annotation. I'm pretty sure that it can be done, but don't know how.
The json provided here is a simplistic one. I have a large json in actual scenario.
Incoming json
{
"date":"2016-03-31"
}
Outgoing Json
{
"date":236484625196
}
My Controller Class
#Controller
public class SomeController extends BaseController {
#Autowired
private SomeService someService;
#RequestMapping(method = RequestMethod.GET, produces = "application/json")
public #ResponseBody
ResponseEntity<RestResponse> getDetails(HttpServletRequest request, HttpServletResponse response) {
Model model = someService.getData();
ModelView modelView = ModelView.valueOf(model);
return getSuccessResponse(modelView);
}
}
Model.java
#JsonInclude(Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Model implements Serializable {
private String date;
//normal getters and setters
}
ModelView.java
#JsonInclude(Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class ModelView implements Serializable {
private Long date;
//normal getters and setters
public static ModelView valueOf(Model model){
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-mm-dd");
ObjectMapper mapper = new ObjectMapper();
ModelView modelView = mapper.convertValue(model, ModelView.class);
try {
modelView.setDate(sdf.parse(model.getDate()).getTime());
} catch (ParseException e) {
IntLogger.error("logging error here");
}
return modelView;
}
}
I'm open to change the variable name from "date" to something else in ModelView.java but the outgoing json should remain same.
Jackson has some build in date formatting, for example, you can set the DateFormatter on the object mapper, but i believe this only works if the serialization and deserialization format is the same.
A simpler approach to date serialization and deserialization, if you want serialization and deserialization to be different format, is to use #JsonSerialize and #JsonDeserialize annotations on your Model.class directly (this could obsolete the need for ModelView if your only purpose was to convert the date).
You can create two classes for serialization and deserialization:
public class JsonDateDeserializer extends JsonDeserializer<Date> {
#Override
public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
String dateString = jsonParser.getText();
try {
return dateFormat.parse(dateString);
}
catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
Then for the serialization to your Outgoing json:
public class JsonDateSerializer extends JsonSerializer<Date> {
#Override
public void serialize(Date date, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
jsonGenerator.writeString(Long.toString(date.getTime()));
}
}
Now, you an just annotate your Model.java:
#JsonInclude(Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
public class Model implements Serializable {
#JsonSerialize(using = JsonDateSerializer.class)
#JsonDeserialize(using = JsonDateDeserializer.class)
private String date;
//normal getters and setters
}

Process JSON with Jersey and Jackson

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
}

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