I'm using JAVA 1.6 and Jackson 1.9.9 I've got an enum
public enum Event {
FORGOT_PASSWORD("forgot password");
private final String value;
private Event(final String description) {
this.value = description;
}
#JsonValue
final String value() {
return this.value;
}
}
I've added a #JsonValue, this seems to do the job it serializes the object into:
{"event":"forgot password"}
but when I try to deserialize I get a
Caused by: org.codehaus.jackson.map.JsonMappingException: Can not construct instance of com.globalrelay.gas.appsjson.authportal.Event from String value 'forgot password': value not one of declared Enum instance names
What am I missing here?
The serializer / deserializer solution pointed out by #xbakesx is an excellent one if you wish to completely decouple your enum class from its JSON representation.
Alternatively, if you prefer a self-contained solution, an implementation based on #JsonCreator and #JsonValue annotations would be more convenient.
So leveraging on the example by #Stanley the following is a complete self-contained solution (Java 6, Jackson 1.9):
public enum DeviceScheduleFormat {
Weekday,
EvenOdd,
Interval;
private static Map<String, DeviceScheduleFormat> namesMap = new HashMap<String, DeviceScheduleFormat>(3);
static {
namesMap.put("weekday", Weekday);
namesMap.put("even-odd", EvenOdd);
namesMap.put("interval", Interval);
}
#JsonCreator
public static DeviceScheduleFormat forValue(String value) {
return namesMap.get(StringUtils.lowerCase(value));
}
#JsonValue
public String toValue() {
for (Entry<String, DeviceScheduleFormat> entry : namesMap.entrySet()) {
if (entry.getValue() == this)
return entry.getKey();
}
return null; // or fail
}
}
Note that as of this commit in June 2015 (Jackson 2.6.2 and above) you can now simply write:
public enum Event {
#JsonProperty("forgot password")
FORGOT_PASSWORD;
}
The behavior is documented here: https://fasterxml.github.io/jackson-annotations/javadoc/2.11/com/fasterxml/jackson/annotation/JsonProperty.html
Starting with Jackson 2.6 this annotation may also be used to change serialization of Enum like so:
public enum MyEnum {
#JsonProperty("theFirstValue") THE_FIRST_VALUE,
#JsonProperty("another_value") ANOTHER_VALUE;
}
as an alternative to using JsonValue annotation.
You should create a static factory method which takes single argument and annotate it with #JsonCreator (available since Jackson 1.2)
#JsonCreator
public static Event forValue(String value) { ... }
Read more about JsonCreator annotation here.
Actual Answer:
The default deserializer for enums uses .name() to deserialize, so it's not using the #JsonValue. So as #OldCurmudgeon pointed out, you'd need to pass in {"event": "FORGOT_PASSWORD"} to match the .name() value.
An other option (assuming you want the write and read json values to be the same)...
More Info:
There is (yet) another way to manage the serialization and deserialization process with Jackson. You can specify these annotations to use your own custom serializer and deserializer:
#JsonSerialize(using = MySerializer.class)
#JsonDeserialize(using = MyDeserializer.class)
public final class MyClass {
...
}
Then you have to write MySerializer and MyDeserializer which look like this:
MySerializer
public final class MySerializer extends JsonSerializer<MyClass>
{
#Override
public void serialize(final MyClass yourClassHere, final JsonGenerator gen, final SerializerProvider serializer) throws IOException, JsonProcessingException
{
// here you'd write data to the stream with gen.write...() methods
}
}
MyDeserializer
public final class MyDeserializer extends org.codehaus.jackson.map.JsonDeserializer<MyClass>
{
#Override
public MyClass deserialize(final JsonParser parser, final DeserializationContext context) throws IOException, JsonProcessingException
{
// then you'd do something like parser.getInt() or whatever to pull data off the parser
return null;
}
}
Last little bit, particularly for doing this to an enum JsonEnum that serializes with the method getYourValue(), your serializer and deserializer might look like this:
public void serialize(final JsonEnum enumValue, final JsonGenerator gen, final SerializerProvider serializer) throws IOException, JsonProcessingException
{
gen.writeString(enumValue.getYourValue());
}
public JsonEnum deserialize(final JsonParser parser, final DeserializationContext context) throws IOException, JsonProcessingException
{
final String jsonValue = parser.getText();
for (final JsonEnum enumValue : JsonEnum.values())
{
if (enumValue.getYourValue().equals(jsonValue))
{
return enumValue;
}
}
return null;
}
I've found a very nice and concise solution, especially useful when you cannot modify enum classes as it was in my case. Then you should provide a custom ObjectMapper with a certain feature enabled. Those features are available since Jackson 1.6. So you only need to write toString() method in your enum.
public class CustomObjectMapper extends ObjectMapper {
#PostConstruct
public void customConfiguration() {
// Uses Enum.toString() for serialization of an Enum
this.enable(WRITE_ENUMS_USING_TO_STRING);
// Uses Enum.toString() for deserialization of an Enum
this.enable(READ_ENUMS_USING_TO_STRING);
}
}
There are more enum-related features available, see here:
https://github.com/FasterXML/jackson-databind/wiki/Serialization-Features
https://github.com/FasterXML/jackson-databind/wiki/Deserialization-Features
Try this.
public enum Event {
FORGOT_PASSWORD("forgot password");
private final String value;
private Event(final String description) {
this.value = description;
}
private Event() {
this.value = this.name();
}
#JsonValue
final String value() {
return this.value;
}
}
I like the accepted answer. However, I would improve it a little (considering that there is now Java higher than version 6 available).
Example:
public enum Operation {
EQUAL("eq"),
NOT_EQUAL("ne"),
LESS_THAN("lt"),
GREATER_THAN("gt");
private final String value;
Operation(String value) {
this.value = value;
}
#JsonValue
public String getValue() {
return value;
}
#JsonCreator
public static Operation forValue(String value) {
return Arrays.stream(Operation.values())
.filter(op -> op.getValue().equals(value))
.findFirst()
.orElseThrow(); // depending on requirements: can be .orElse(null);
}
}
You can customize the deserialization for any attribute.
Declare your deserialize class using the annotationJsonDeserialize (import com.fasterxml.jackson.databind.annotation.JsonDeserialize) for the attribute that will be processed. If this is an Enum:
#JsonDeserialize(using = MyEnumDeserialize.class)
private MyEnum myEnum;
This way your class will be used to deserialize the attribute. This is a full example:
public class MyEnumDeserialize extends JsonDeserializer<MyEnum> {
#Override
public MyEnum deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
JsonNode node = jsonParser.getCodec().readTree(jsonParser);
MyEnum type = null;
try{
if(node.get("attr") != null){
type = MyEnum.get(Long.parseLong(node.get("attr").asText()));
if (type != null) {
return type;
}
}
}catch(Exception e){
type = null;
}
return type;
}
}
Here is another example that uses string values instead of a map.
public enum Operator {
EQUAL(new String[]{"=","==","==="}),
NOT_EQUAL(new String[]{"!=","<>"}),
LESS_THAN(new String[]{"<"}),
LESS_THAN_EQUAL(new String[]{"<="}),
GREATER_THAN(new String[]{">"}),
GREATER_THAN_EQUAL(new String[]{">="}),
EXISTS(new String[]{"not null", "exists"}),
NOT_EXISTS(new String[]{"is null", "not exists"}),
MATCH(new String[]{"match"});
private String[] value;
Operator(String[] value) {
this.value = value;
}
#JsonValue
public String toStringOperator(){
return value[0];
}
#JsonCreator
public static Operator fromStringOperator(String stringOperator) {
if(stringOperator != null) {
for(Operator operator : Operator.values()) {
for(String operatorString : operator.value) {
if (stringOperator.equalsIgnoreCase(operatorString)) {
return operator;
}
}
}
}
return null;
}
}
There are various approaches that you can take to accomplish deserialization of a JSON object to an enum. My favorite style is to make an inner class:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.hibernate.validator.constraints.NotEmpty;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import static com.fasterxml.jackson.annotation.JsonFormat.Shape.OBJECT;
#JsonFormat(shape = OBJECT)
public enum FinancialAccountSubAccountType {
MAIN("Main"),
MAIN_DISCOUNT("Main Discount");
private final static Map<String, FinancialAccountSubAccountType> ENUM_NAME_MAP;
static {
ENUM_NAME_MAP = Arrays.stream(FinancialAccountSubAccountType.values())
.collect(Collectors.toMap(
Enum::name,
Function.identity()));
}
private final String displayName;
FinancialAccountSubAccountType(String displayName) {
this.displayName = displayName;
}
#JsonCreator
public static FinancialAccountSubAccountType fromJson(Request request) {
return ENUM_NAME_MAP.get(request.getCode());
}
#JsonProperty("name")
public String getDisplayName() {
return displayName;
}
private static class Request {
#NotEmpty(message = "Financial account sub-account type code is required")
private final String code;
private final String displayName;
#JsonCreator
private Request(#JsonProperty("code") String code,
#JsonProperty("name") String displayName) {
this.code = code;
this.displayName = displayName;
}
public String getCode() {
return code;
}
#JsonProperty("name")
public String getDisplayName() {
return displayName;
}
}
}
In the context of an enum, using #JsonValue now (since 2.0) works for serialization and deserialization.
According to the jackson-annotations javadoc for #JsonValue:
NOTE: when use for Java enums, one additional feature is that value returned by annotated method is also considered to be the value to deserialize from, not just JSON String to serialize as. This is possible since set of Enum values is constant and it is possible to define mapping, but can not be done in general for POJO types; as such, this is not used for POJO deserialization.
So having the Event enum annotated just as above works (for both serialization and deserialization) with jackson 2.0+.
Besides using #JsonSerialize #JsonDeserialize, you can also use SerializationFeature and DeserializationFeature (jackson binding) in the object mapper.
Such as DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, which give default enum type if the one provided is not defined in the enum class.
In my case, this is what resolved:
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum PeriodEnum {
DAILY(1),
WEEKLY(2),
;
private final int id;
PeriodEnum(int id) {
this.id = id;
}
public int getId() {
return id;
}
public String getName() {
return this.name();
}
#JsonCreator
public static PeriodEnum fromJson(#JsonProperty("name") String name) {
return valueOf(name);
}
}
Serializes and deserializes the following json:
{
"id": 2,
"name": "WEEKLY"
}
I hope it helps!
Here, 'value' acts as a deserialiser and 'namespace' acts as a serialiser. Hence, you can pass in value "Student Absent" to API while saving, and in DB it will be saved as "STUDENT_ABSENT". On the other hand, while retrieving data in your class, your API will return "Student Absent"
import com.fasterxml.jackson.annotation.JsonProperty;
public enum AttendanceEnums {
STUDENT_PRESENT,
#JsonProperty(value = "Student Absent", namespace = "Student Absent")
STUDENT_ABSENT;
}
I had been looking for a solution to enum serialization and I finally made a solution.
https://github.com/sirgilligan/EnumerationSerialization
https://digerati-illuminatus.blogspot.com/2022/10/java-enum-generic-serializer-and.html
It uses a new annotation and two new classes, EnumerationSerializer and EnumerationDeserializer. You can subclass the EnumerationDeserializer and make a class that sets the enum Class (typical approach) or you can annotate the enum and you don't have to have a subclass of EnumerationDeserializer.
#JsonSerialize(using = EnumerationSerializer.class)
#JsonDeserialize(using = EnumerationDeserializer.class)
#EnumJson(serializeProjection = Projection.NAME, deserializationClass = RGB.class)
enum RGB {
RED,
GREEN,
BLUE
}
Notice how the implementation of ContextualDeserializer pulls the class from the annotation.
https://github.com/sirgilligan/EnumerationSerialization/blob/main/src/main/java/org/example/EnumerationDeserializer.java
There is a lot of good code in this that might give insights.
For your specific question you could do this:
#JsonSerialize(using = EnumerationSerializer.class)
#JsonDeserialize(using = EnumerationDeserializer.class)
#EnumJson(serializeProjection = Projection.NAME, deserializationClass = Event.class)
public enum Event {
FORGOT_PASSWORD("forgot password");
//This annotation is optional because the code looks for value or alias.
#EnumJson(serializeProjection = Projection.VALUE)
private final String value;
private Event(final String description) {
this.value = description;
}
}
Or you could do this:
#JsonSerialize(using = EnumerationSerializer.class)
#JsonDeserialize(using = EnumerationDeserializer.class)
#EnumJson(serializeProjection = Projection.NAME, deserializationClass = Event.class)
public enum Event {
FORGOT_PASSWORD("forgot password");
private final String value;
private Event(final String description) {
this.value = description;
}
}
That's all you have to do.
Then if you have a class that "has a" event you can annotate each occurance to serialize the way you want.
class EventHolder {
#EnumJson(serializeProjection = Projection.NAME)
Event someEvent;
#EnumJson(serializeProjection = Projection.ORDINAL)
Event someOtherEvent;
#EnumJson(serializeProjection = Projection.VALUE)
Event yetAnotherEvent;
}
The simplest way I found is using #JsonFormat.Shape.OBJECT annotation for the enum.
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum MyEnum{
....
}
I did it like this :
// Your JSON
{"event":"forgot password"}
// Your class to map
public class LoggingDto {
#JsonProperty(value = "event")
private FooEnum logType;
}
//Your enum
public enum FooEnum {
DATA_LOG ("Dummy 1"),
DATA2_LOG ("Dummy 2"),
DATA3_LOG ("forgot password"),
DATA4_LOG ("Dummy 4"),
DATA5_LOG ("Dummy 5"),
UNKNOWN ("");
private String fullName;
FooEnum(String fullName) {
this.fullName = fullName;
}
public String getFullName() {
return fullName;
}
#JsonCreator
public static FooEnum getLogTypeFromFullName(String fullName) {
for (FooEnum logType : FooEnum.values()) {
if (logType.fullName.equals(fullName)) {
return logType;
}
}
return UNKNOWN;
}
}
So the value of the property "logType" for class LoggingDto will be DATA3_LOG
This post is old, but if it can help someone, use JsonFormat.Shape.STRING
#JsonFormat(shape = JsonFormat.Shape.STRING)
public enum SomeEnum{
#JsonProperty("SOME_PROPERTY")
someProperty,
...
}
Code results is like this
{"someenum":"SOME_PROPERTY"}
#JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum LoginOptionType {
PHONE(1, "Phone"), MAIL(2, "mail"), PERSONAL_EMAIL(3, "Personal email");
private static List<LoginOptionType> all;
static {
all = new ArrayList<LoginOptionType>() {
{
add(LoginOptionType.PHONE);
add(LoginOptionType.MAIL);
add(LoginOptionType.PERSONAL_EMAIL);
}
};
}
private final Integer viewValue;
private final String name;
LoginOptionType(Integer viewValue, String name) {
this.viewValue = viewValue;
this.name = name;
}
public Integer getViewValue() {
return viewValue;
}
public String getName() {
return name;
}
public static List<LoginOptionType> getAll() {
return all;
}
}
Response
[
{
"viewValue": 1,
"name": "Phone"
},
{
"viewValue": 2,
"name": "mail"
},
{
"viewValue": 3,
"name": "Personal email"
}
]
I have the following classes:
public class MeetingCenter {
private String name;
private List<MeetingRoom> meetingRoomList;
}
public class MeetingRoom {
private MeetingCenter meetingCenter;
private String code;
private String name;
private List<Reservation> reservationList;
}
public class Reservation {
private MeetingRoom meetingRoom;
private String owner;
}
And I want to create a JSON with following schema:
This method throws me an exception on the line where I call the toJson() method:
private static void exportToJson(List<MeetingCenter> mcs) throws IOException {
Gson gson = new Gson();
String data = gson.toJson(mcs);
JsonWriter writer = new JsonWriter(new FileWriter("export.json"));
writer.setIndent(" "); // set indent
writer.beginObject(); // document start
writer.name("schema").value("PLUS4U.EBC.MCS.MeetingRoom_Schedule_1.0");
writer.name("uri").value("ues:UCL-BT:UCL.INF/DEMO_REZERVACE:EBC.MCS.DEMO/MR001/SCHEDULE");
writer.name("data").value(data);
writer.endObject(); // document end
writer.close();
}
The exception:
Exception in thread "main" java.lang.StackOverflowError
at java.lang.StringBuffer.append(StringBuffer.java:380)
at java.io.StringWriter.write(StringWriter.java:77)
at com.google.gson.stream.JsonWriter.beforeName(JsonWriter.java:614)
at com.google.gson.stream.JsonWriter.writeDeferredName(JsonWriter.java:401)
at com.google.gson.stream.JsonWriter.beginArray(JsonWriter.java:287)
at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.write(CollectionTypeAdapterFactory.java:95)
....
Your objects all have references to their parents.
GSON takes looks at a MeetingCenter then tries to serialize its child MeetingRooms. The MeetingRooms have reference back to the MeetingCenter, so GSON goes around and around in circles until you get a stack overflow.
To fix this, you can make sure to only expose the children and not the parents. There are plenty of questions showing this already. See Java Gson Exclude fields during serialization.
For example, your Reservation might look like this:
class Reservation {
MeetingRoom meetingRoom;
#Expose
String owner;
}
I'll leave the rest up to you.
Also, you have a method call to writer.endArray() when you have not started an array. Remove that line.
writer.beginObject(); // document start
writer.name("schema").value("PLUS4U.EBC.MCS.MeetingRoom_Schedule_1.0");
writer.name("uri").value("ues:UCL-BT:UCL.INF/DEMO_REZERVACE:EBC.MCS.DEMO/MR001/SCHEDULE");
writer.name("data").value(data);
//writer.endArray(); removed
writer.endObject(); // document end
writer.close();
The #Expose is the solution to avoid the stackoverflow exception but the statement
writer.name("data").value(data);
is not valide because the data will be enriched with escape char. For exampla you can have in the data field
"data": "{\"name\": \"center 1\" ... }"
so there can will be problems in the deserialize phase.
My implementation proposes a Container class for the MeetingCenter class where the schema and the URI can be configured.
/** Container class configures the schema and URI */
public class Container {
#Expose
private String schema;
#Expose
private String uri;
#Expose
private List<MeetingCenter> data;
}
public class Reservation {
private MeetingRoom meetingRoom;
#Expose
private String owner;
}
public class MeetingRoom {
private MeetingCenter meetingCenter;
#Expose
private String code;
#Expose
private String name;
#Expose
private List<Reservation> reservationList;
}
public class MeetingCenter {
#Expose
private String name;
#Expose
private List<MeetingRoom> meetingRoomList;
}
public class Main {
public static void main(String[] args){
Container container = meetingCenterInitialization();
GsonBuilder builder = new GsonBuilder();
builder.setPrettyPrinting();
// it is necessary to avoid stackoverflow
builder.excludeFieldsWithoutExposeAnnotation();
Gson gson = builder.create();
String jsonString = gson.toJson(container);
System.out.println(jsonString);
Container container1 = gson.fromJson(jsonString, Container.class);
System.out.println("\n\n\n\n" + container1.getData().get(0).getName());
}
}
The output of the main method is
{
"schema": "PLUS4U.EBC.MCS.MeetingRoom_Schedule_1.0",
"uri": "ues:UCL-BT:UCL.INF/DEMO_REZERVACE:EBC.MCS.DEMO/MR001/SCHEDULE",
"data": [
{
"name": "center name",
"meetingRoomList": [
{
"code": "room 1",
...
I like to deserialize with Jackson an empty String member ("") to null. The Deserialization Feature "ACCEPT_EMPTY_STRING_AS_NULL_OBJECT" can for this unfortunately not be used (see link).
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonInclude(Include.NON_NULL)
public class Supplier {
private Integer id;
private String name;
private String image;
private String link;
private String description;
}
So after deserialization of the following JSON String the string members "link" and "image" should be null and not "".
{"id":37,"name":"Life","image":"","link":"","description":null}
I am looking for a way to write an own deserializer which can be used for String members of a POJO. Is there a way to achieve this? I am using faster Jackson 2.6.0.
The custom deserializer can be done as follows in Jackson 2.6.0.
public class SupplierDeserializer extends JsonDeserializer<Supplier> {
#Override
public Supplier deserialize(JsonParser jp, DeserializationContext context) throws IOException, JsonProcessingException {
Supplier sup = new Supplier();
JsonNode node = jp.readValueAsTree();
sup.setId(node.get("id").asInt());
sup.setDescription(node.get("description").asText());
String image = node.get("image").asText();
if("".equals(image)) {
image = null;
}
sup.setImage(image);
String link = node.get("link").asText();
if("".equals(link)) {
link = null;
}
sup.setLink(link);
sup.setName(node.get("name").asText());
return sup;
}
}
Register the custom deserialiser with the Supplier class
#JsonDeserialize(using = SupplierDeserializer.class)
public class Supplier {
private Integer id;
private String name;
private String image;
private String link;
private String description;
// getters and setters
}
Call the ObjectMapper class to parse the JSON data
String jsonData = "{\"id\":37,\"name\":\"Life\",\"image\":\"\",\"link\":\"\",\"description\":null}";
Supplier sup = new ObjectMapper().readValue(jsonData, Supplier.class);
I'm retrieving data from a Web Service, which returns a String with the JSON representation of my object. What happens is that my object has two ArrayList<> of other objects, besides of other simple attributes. It seems to be a problem for the Gson. I've been searching and the answers do not match my specific problem.
My code is, basically:
Verbete
public class Verbete implements Serializable{
private long id;
private List<NomesVerbete> nomes;
private List<SignificadosVerbete> significados;
private int totalAcessos = 0;
private Date dataLancamento;
private int relevancia = 0;
//getters and setters
}
NomesVerbete
public class NomesVerbete implements Serializable {
private long id;
private String nome;
private String etimologia;
//getters and setters
}
SignificadosVerbete
public class SignificadosVerbete implements Serializable{
private long id;
private String significado;
private CategoriaVerbete categoria;
//getters and setters
}
CategoriaVerbete
public class CategoriaVerbete implements Serializable{
private long id;
private String nome;
private String descricao;
private int serie;
//getters and setters
}
The conversion from Verbete to JSON gives me this String (which seems to be correct, right?):
{"id":81,"nomes":[{"id":124,"nome":"aleluia","etimologia":"asasiajisjaijs"},{"id":126,"nome":"amém","etimologia":"asasiajisjaijs"}],"significados":[{"id":67,"significado":"asasjaijsiajsoijaoisjaoisj","categoria":{"id":3,"nome":"Nada","descricao":"asuahushaus","serie":1}}],"totalAcessos":0,"dataLancamento":1382066568000,"relevancia":0}
But, when I try to convert this JSON string to Verbete, it doesn't work, comproved by the NullPointerException that I get when trying to access the object. Here's is my conversion from Json:
Gson gson = new GsonBuilder().create();
Verbete verbete = gson.fromJson(resposta[1], Verbete.class); //resposta[1] is the json string above
Could someone give me a way to go in this conversion, please?
Thank you.
do like this
final String json = "{\"id\":81,\"nomes\":[{\"id\":124,\"nome\":\"aleluia\",\"etimologia\":\"asasiajisjaijs\"},{\"id\":126,\"nome\":\"amém\",\"etimologia\":\"asasiajisjaijs\"}],\"significados\":[{\"id\":67,\"significado\":\"asasjaijsiajsoijaoisjaoisj\",\"categoria\":{\"id\":3,\"nome\":\"Nada\",\"descricao\":\"asuahushaus\",\"serie\":1}}],\"totalAcessos\":0,\"dataLancamento\":1382066568000,\"relevancia\":0}";
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Date.class,
new JsonDeserializer<Date>() {
#Override
public Date deserialize(JsonElement jsonElement, Type type,
JsonDeserializationContext context)
throws JsonParseException {
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(jsonElement.getAsLong());
return calendar.getTime();
}
});
Gson gson = gsonBuilder.create();
Verbete verbete = gson.fromJson(json, Verbete.class);
System.out.println(verbete);
Using Play Framework, I serialize my models via GSON. I specify which fields are exposed and which aren't.
This works great but I'd also like to #expose method too. Of course, this is too simple.
How can I do it ?
Thanks for your help !
public class Account extends Model {
#Expose
public String username;
#Expose
public String email;
public String password;
#Expose // Of course, this don't work
public String getEncodedPassword() {
// ...
}
}
The best solution I came with this problem was to make a dedicated serializer :
public class AccountSerializer implements JsonSerializer<Account> {
#Override
public JsonElement serialize(Account account, Type type, JsonSerializationContext context) {
JsonObject root = new JsonObject();
root.addProperty("id", account.id);
root.addProperty("email", account.email);
root.addProperty("encodedPassword", account.getEncodedPassword());
return root;
}
}
And to use it like this in my view:
GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(Account.class, new AccountSerializer());
Gson parser = gson.create();
renderJSON(parser.toJson(json));
But having #Expose working for a method would be great: it would avoid making a serializer just for showing methods!
Check out Gson on Fire: https://github.com/julman99/gson-fire
It's a library I made that extends Gson to handle cases like exposing method, results Post-serialization, Post-deserialization and many other things that I've needed over time with Gson.
This library is used in production in our company Contactive (http://goo.gl/yueXZ3), on both Android and the Java Backend
Gson's #Expose seem to only be supported on fields. There is an issue registered on this: #Expose should be used with methods.
Couple different options based on Cyril's answer:
Custom serializer with a shortcut:
public static class Sample
{
String firstName = "John";
String lastName = "Doe";
public String getFullName()
{
return firstName + " " + lastName;
}
}
public static class SampleSerializer implements JsonSerializer<Sample>
{
public JsonElement serialize(Sample src, Type typeOfSrc, JsonSerializationContext context)
{
JsonObject tree = (JsonObject)new Gson().toJsonTree(src);
tree.addProperty("fullName", src.getFullName());
return tree;
}
}
public static void main(String[] args) throws Exception
{
GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(Sample.class, new SampleSerializer());
Gson parser = gson.create();
System.out.println(parser.toJson(new Sample()));
}
-OR-
Annotation based serializer
public static class Sample
{
String firstName = "John";
String lastName = "Doe";
#ExposeMethod
public String getFullName()
{
return firstName + " " + lastName;
}
}
public static class MethodSerializer implements JsonSerializer<Object>
{
public JsonElement serialize(Object src, Type typeOfSrc, JsonSerializationContext context)
{
Gson gson = new Gson();
JsonObject tree = (JsonObject)gson.toJsonTree(src);
try
{
PropertyDescriptor[] properties = Introspector.getBeanInfo(src.getClass()).getPropertyDescriptors();
for (PropertyDescriptor property : properties)
{
if (property.getReadMethod().getAnnotation(ExposeMethod.class) != null)
{
Object result = property.getReadMethod().invoke(src, (Object[])null);
tree.add(property.getName(), gson.toJsonTree(result));
}
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
return tree;
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD) //can use in method only.
public static #interface ExposeMethod {}
public static void main(String[] args) throws Exception
{
GsonBuilder gson = new GsonBuilder();
gson.registerTypeAdapter(Sample.class, new MethodSerializer());
Gson parser = gson.create();
System.out.println(parser.toJson(new Sample()));
}