I have a Document case class.
To Serialize it and deserialize to and from Json text, I defined implicit Reads and Writes object.
If my Document class contains only Int and String, I have no problem.
However when I have an Html type value in my Document case class, I have the issue.
It is a nesting serialization and deserialization.
I have a problem creating a Reader for Html. Play 2 Html is not a case class. Is that a problem?
Is the following code is right:
implicit object HtmlReads extends play.api.libs.json.Reads[Html] {
def reads(json: JsValue) = Html (
(json \ "text").as[String]
)
}
It does not work.
How should I do it?
Thanks
This is how I solved this problem in java (but I guess it is the same in scala):
I create a JsonSerializer class to translate a class into a string and then I annote the fields which will be translate into Json with my class.
An exemple to show you how it work for the date:
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
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
*/
#Component
public class JsonDateSerializer extends JsonSerializer<Date>{
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy");
#Override
public void serialize(Date date, JsonGenerator gen, SerializerProvider provider)
throws IOException, JsonProcessingException {
String formattedDate = dateFormat.format(date);
gen.writeString(formattedDate);
}
}
Then I annote the corresponding field with my class:
public class MyClass
{
#Formats.DateTime(pattern="dd/MM/yyyy")
#JsonSerialize(using=JsonDateSerializer.class)
public Date myDate;
}
Ainsi, lorsque j'utilise mapper.writeValueAsString(lst), j'obtiens des date au format: 08-13-2014
I copied the sources from Loiane Groner.
Related
I am trying json parsing with gson in a small java applicaiton. I have a json string which comes from .Net business layer, has a field as "1999-08-24T00:00:00". In my model like User model, I have java.time.Instant birthDay field. With gson i am trying to get json string to my user model. Also I have a InstantDeserializer class. But when I try to convert it I got a message like java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)..
Before the instant type I was using Date class. I wrote DateDeserializer class but I know Date class is deprecated. I googled to much page. I tried many things but i didn't how to figure out. So i just want to ask where I am making mistakes. What sould I do? How can I make my code more clear or what is the best approch? If you could give some code examples, I can understand better.
Any advice is appreciated..
Here is my code..
JSON String :
{
"Value":{
"ID":"123",
"NAME":"John",
"SURNAME":"Concept",
"BIRTHDAY":"1999-08-24T00:00:00",
"PAYMENTINFORMATION":[
{
"ID":"1",
"PAYMENTINFO":"RECIEVED"
}
]
},
"Succued": true
}
UserModel class
package Models;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
import java.time.LocalDateTime;
import java.util.Date;
import com.google.gson.annotations.SerializedName;
import java.time.Instant;
public class UserModel {
private long id;
private String name;
private String surname;
private Instant birthday;
private List<PaymentModel> paymentInformation;
//GETTER SETTER
public UserModel() {
paymentInformation= new ArrayList<>();
}
}
InstantDeserializer class
package Util;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
public class InstantDeSerializer implements JsonDeserializer<Instant> {
#Override
public Instant deserialize(JsonElement jelement, Type type, JsonDeserializationContext jdc) throws JsonParseException {
Instant insObj= Instant.ofEpochMilli(jelement.getAsJsonPrimitive().getAsLong());
return insObj;
}
}
And Main class
public class JSONTryMe {
public static void main(String[] args) throws Exception {
JSONObject responseJSON = new JSONObject(jsonString);
if (responseJSON.isNull("Value")) {
return;
}
GsonBuilder build = new GsonBuilder();
build.registerTypeAdapter(Instant.class, new InstantDeSerializer());
Gson gObj = build.create();
UserModel user = gObj.fromJson(responseJSON.getJSONObject("Value").toString(), UserModel.class);
System.out.println(user.getBirthday().toString());
}
}
Ant the error stackTrace is
java.lang.NumberFormatException: For input string: "1999-08-24T00:00:00"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Long.parseLong(Long.java:589)
at java.lang.Long.parseLong(Long.java:631)
at com.google.gson.JsonPrimitive.getAsLong(JsonPrimitive.java:206)
at Util.InstantDeSerializer.deserialize(InstantDeSerializer.java:25)
at Util.InstantDeSerializer.deserialize(InstantDeSerializer.java:21)
at com.google.gson.internal.bind.TreeTypeAdapter.read(TreeTypeAdapter.java:69)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$1.read(ReflectiveTypeAdapterFactory.java:131)
at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:222)
at com.google.gson.Gson.fromJson(Gson.java:932)
at com.google.gson.Gson.fromJson(Gson.java:897)
at com.google.gson.Gson.fromJson(Gson.java:846)
at com.google.gson.Gson.fromJson(Gson.java:817)
at Source.JSONTryMe.main(JSONTryMe.java:85)
Here are several conceptual flaws:
Birth dates should use LocalDate
Your JSON input provides ISO datetime, but your deserializer tries to read milliseconds since epoch. Use LocalDate#parse() for this
I use com.fasterxml.jackson and io.swagger libraries. In my REST endpoint I use org.javamoney.moneta.Money type for a GET query. When deploying the war i get following exception 1;
I have followed this reference and wrote following code[2]; and registered it at #ApplicationPath. But still getting same issue.
Any guide would be really helpful?
#ApplicationPath("/rest")
public class RestApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
HashSet<Class<?>> set = new HashSet<Class<?>>();
set.add(com.test.JsonMoneyProvider.class);
[2]
import javax.money.CurrencyUnit;
import javax.money.Monetary;
import javax.money.MonetaryAmountFactory;
import javax.ws.rs.ext.Provider;
import javax.xml.bind.annotation.XmlTransient;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
#Provider
public class JsonMoneyProvider extends JacksonJsonProvider {
public JsonMoneyProvider() {
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(MonetaryAmountFactory.class, MixIn.class);
setMapper(mapper);
}
public static interface MixIn {
#JsonIgnore
#XmlTransient
MonetaryAmountFactory setCurrency(CurrencyUnit currency);
#JsonIgnore
#XmlTransient
default MonetaryAmountFactory setCurrency(String currencyCode) {
return setCurrency(Monetary.getCurrency(currencyCode));
}
}
}
1
Caused by: java.lang.IllegalArgumentException: Conflicting setter definitions for property "currency": javax.money.MonetaryAmountFactory#setCurrency(1 params) vs javax.money.MonetaryAmountFactory#setCurrency(1 params)
at com.fasterxml.jackson.databind.introspect.POJOPropertyBuilder.getSetter(POJOPropertyBuilder.java:293)
at io.swagger.jackson.ModelResolver.resolve(ModelResolver.java:246)
at io.swagger.jackson.ModelResolver.resolve(ModelResolver.java:127)
at io.swagger.converter.ModelConverterContextImpl.resolve(ModelConverterContextImpl.java:99)
at io.swagger.jackson.ModelResolver.resolveProperty(ModelResolver.java:106)
a
Simply use this annotation on the deserialization setter method to indicate Jackson wich one to use: #com.fasterxml.jackson.annotation.JsonSetter
I am trying to parse a JSON file using Jackson Json parser and in the process using the get(String nodename) function of it.
But when i have multiple nodes of the same name , it is trying to get to the last of the similar nodes and act only on them. How do i get to all the nodes.
For example if my json file was
{"menu":{"a":"1", "b":"2"},
"menu":{"c":"1", "d":"2"},
"menu":{"e":"1", "f":"2"}}
and if i'm trying to do a get("menu") and try to print the field names in it, only e and f get printed whereas i want a b c d e f to get printed.
I'd probably make use of #JsonAnySetter, along the following lines.
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.ObjectMapper;
public class App
{
public static void main(String[] args) throws Exception
{
/* {"menu":{"a":"1", "b":"2"},"menu":{"c":"1", "d":"2"},"menu":{"e":"1", "f":"2"}} */
String json = "{\"menu\":{\"a\":\"1\", \"b\":\"2\"},\"menu\":{\"c\":\"1\", \"d\":\"2\"},\"menu\":{\"e\":\"1\", \"f\":\"2\"}}";
ObjectMapper mapper = new ObjectMapper();
Foo foo = mapper.readValue(json, Foo.class);
System.out.println(foo.menus);
}
}
class Foo
{
List<Menu> menus = new ArrayList<>();
#JsonAnySetter
public void addMenu(String key, Menu menu)
{
menus.add(menu);
}
}
class Menu
{
Map<String, Object> items = new HashMap<> ();
#JsonAnySetter
public void addItems(String itemName, String itemValue)
{
items.put(itemName, itemValue);
}
#Override
public String toString()
{
return String.format("%s", items);
}
}
I was wondering why MOXy is not providing a JSONProvider class similar to JACKSON to replace the default JSON provider in a jax-rs implementation?
This would be the easiest way to deal with all classes in a certain package.
What I ended up doing was to do the following as I feel that custom context resolver or MessageBodyWriter/Reader are mostly suited to handle certain classes, but not to handle all classes in a package especially if you have many classes.
Am I right?
What are your thoughts?
What is the best way to replace Jettison with MOXy in CXF to handle all classes in a package?
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import org.apache.cxf.jaxrs.provider.json.JSONProvider;
import org.eclipse.persistence.jaxb.MarshallerProperties;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
public class MyJSONProvider<T> extends JSONProvider<T> {
private static JAXBContext jaxbContext = null;
static {
try {
jaxbContext = JAXBContextFactory.createContext("com.bp.bs", null);
} catch (JAXBException jaxbe) {
jaxbe.printStackTrace();
throw new ExceptionInInitializerError(jaxbe);
}
}
#Override
public void writeTo(T obj, Class<?> cls, Type genericType,
Annotation[] anns, MediaType m,
MultivaluedMap<String, Object> headers, OutputStream os)
throws IOException, WebApplicationException {
Marshaller marshaller = null;
try {
marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(MarshallerProperties.MEDIA_TYPE,
"application/json");
marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false);
marshaller.marshal(obj, os);
} catch (JAXBException jaxbe) {
jaxbe.printStackTrace();
}
}
}
EclipseLink JAXB (MOXy) offers the org.eclipse.persistence.jaxb.rs.MOXyJsonProvider class that can be used to enable it as the JSON-provider.
Below is an example of a JAX-RS Application class that configures MOXyJsonProvider.
package org.example;
import java.util.*;
import javax.ws.rs.core.Application;
import org.eclipse.persistence.jaxb.rs.MOXyJsonProvider;
public class CustomerApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
HashSet<Class<?>> set = new HashSet<Class<?>>(2);
set.add(MOXyJsonProvider.class);
set.add(CustomerService.class);
return set;
}
}
MOXyJsonProvider was added in EclipseLink 2.4.0. The latest version is EclipseLink 2.4.1 which can be downloaded from the following link:
http://www.eclipse.org/eclipselink/downloads/
For More Information
http://blog.bdoughan.com/2012/05/moxy-as-your-jax-rs-json-provider.html
If I get the following json from a RESTful client, how do I elegantly unmarshal the java.util.Date? (Is it possible without providing (aka. hard-coding) the format, that's what I mean by elegantly...)
{
"class": "url",
"link": "http://www.empa.ch",
"rating": 5,
"lastcrawl" : "2009-06-04 16:53:26.706 CEST",
"checksum" : "837261836712xxxkfjhds",
}
The cleanest way is probably to register a custom DataBinder for possible date formats.
import java.beans.PropertyEditorSupport;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CustomDateBinder extends PropertyEditorSupport {
private final List<String> formats;
public CustomDateBinder(List formats) {
List<String> formatList = new ArrayList<String>(formats.size());
for (Object format : formats) {
formatList.add(format.toString()); // Force String values (eg. for GStrings)
}
this.formats = Collections.unmodifiableList(formatList);
}
#Override
public void setAsText(String s) throws IllegalArgumentException {
if (s != null)
for (String format : formats) {
// Need to create the SimpleDateFormat every time, since it's not thead-safe
SimpleDateFormat df = new SimpleDateFormat(format);
try {
setValue(df.parse(s));
return;
} catch (ParseException e) {
// Ignore
}
}
}
}
You'd also need to implement a PropertyEditorRegistrar
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
import grails.util.GrailsConfig;
import java.util.Date;
import java.util.List;
public class CustomEditorRegistrar implements PropertyEditorRegistrar {
public void registerCustomEditors(PropertyEditorRegistry reg) {
reg.registerCustomEditor(Date.class, new CustomDateBinder(GrailsConfig.get("grails.date.formats", List.class)));
}
}
and create a Spring-bean definition in your grails-app/conf/spring/resources.groovy:
beans = {
"customEditorRegistrar"(CustomEditorRegistrar)
}
and finally define the date formats in your grails-app/conf/Config.groovy:
grails.date.formats = ["yyyy-MM-dd HH:mm:ss.SSS ZZZZ", "dd.MM.yyyy HH:mm:ss"]
Be aware that the new version of Grails 2.3+ supports this type of feature out of the box.
See Date Formats for Data Binding
With that said, if you are forced to use a version of Grails prior to 2.3, the CustomEditorRegistrar
can be updated using the following code to eliminate the deprecation warning, and also uses the #Component annotation, which allows you to remove / skip the step of adding the bean directly in resources.groovy.
Also not that I changed the grails configuration property name to grails.databinding.dateFormats, which matches the property now supported in Grails 2.3+. Finally, my version is a .groovy, not .java file.
import javax.annotation.Resource
import org.codehaus.groovy.grails.commons.GrailsApplication
import org.springframework.beans.PropertyEditorRegistrar
import org.springframework.beans.PropertyEditorRegistry
import org.springframework.stereotype.Component
#Component
public class CustomEditorRegistrar implements PropertyEditorRegistrar {
#Resource
GrailsApplication grailsApplication
public void registerCustomEditors(PropertyEditorRegistry reg){
def dateFormats = grailsApplication.config.grails.databinding.dateFormats as List
reg.registerCustomEditor(Date.class, new CustomDateBinder(dateFormats))
}
}