Serializing enums with Jackson
I have an Enum desrcibed below:
public enum OrderType {
UNKNOWN(0, "Undefined"),
TYPEA(1, "Type A"),
TYPEB(2, "Type B"),
TYPEC(3, "Type C");
private Integer id;
private String name;
private WorkOrderType(Integer id, String name) {
this.id = id;
this.name = name;
}
//Setters, getters....
}
I return enum array with my controller ( new OrderType[] {UNKNOWN,TYPEA,TYPEB,TYPEC};
), and Spring serializes it into the following json string:
["UNKNOWN", "TYPEA", "TYPEB", "TYPEC"]
What is the best approach to force Jackson to serialize enums just like POJOs? E.g.:
[
{"id": 1, "name": "Undefined"},
{"id": 2, "name": "Type A"},
{"id": 3, "name": "Type B"},
{"id": 4, "name": "Type C"}
]
I played with different annotations but couldn't manage to get such result.
Solution 1:
Finally I found solution myself.
I had to annotate enum with @JsonSerialize(using = OrderTypeSerializer.class)
and implement custom serializer:
public class OrderTypeSerializer extends JsonSerializer<OrderType> {
@Override
public void serialize(OrderType value, JsonGenerator generator,
SerializerProvider provider) throws IOException,
JsonProcessingException {
generator.writeStartObject();
generator.writeFieldName("id");
generator.writeNumber(value.getId());
generator.writeFieldName("name");
generator.writeString(value.getName());
generator.writeEndObject();
}
}
Solution 2:
@JsonFormat(shape= JsonFormat.Shape.OBJECT)
public enum SomeEnum
available since https://github.com/FasterXML/jackson-databind/issues/24
just tested it works with version 2.1.2
answer to TheZuck:
I tried your example, got Json:
{"events":[{"type":"ADMIN"}]}
My code:
@RequestMapping(value = "/getEvent") @ResponseBody
public EventContainer getEvent() {
EventContainer cont = new EventContainer();
cont.setEvents(Event.values());
return cont;
}
class EventContainer implements Serializable {
private Event[] events;
public Event[] getEvents() {
return events;
}
public void setEvents(Event[] events) {
this.events = events;
}
}
and dependencies are:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
<exclusions>
<exclusion>
<artifactId>jackson-annotations</artifactId>
<groupId>com.fasterxml.jackson.core</groupId>
</exclusion>
<exclusion>
<artifactId>jackson-core</artifactId>
<groupId>com.fasterxml.jackson.core</groupId>
</exclusion>
</exclusions>
</dependency>
<jackson.version>2.1.2</jackson.version>
Solution 3:
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.
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
Solution 4:
Here is my solution. I want transform enum to {id: ..., name: ...}
form.
With Jackson 1.x:
pom.xml:
<properties>
<jackson.version>1.9.13</jackson.version>
</properties>
<dependencies>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>
Rule.java:
import org.codehaus.jackson.map.annotate.JsonSerialize;
import my.NamedEnumJsonSerializer;
import my.NamedEnum;
@Entity
@Table(name = "RULE")
public class Rule {
@Column(name = "STATUS", nullable = false, updatable = true)
@Enumerated(EnumType.STRING)
@JsonSerialize(using = NamedEnumJsonSerializer.class)
private Status status;
public Status getStatus() { return status; }
public void setStatus(Status status) { this.status = status; }
public static enum Status implements NamedEnum {
OPEN("open rule"),
CLOSED("closed rule"),
WORKING("rule in work");
private String name;
Status(String name) { this.name = name; }
public String getName() { return this.name; }
};
}
NamedEnum.java:
package my;
public interface NamedEnum {
String name();
String getName();
}
NamedEnumJsonSerializer.java:
package my;
import my.NamedEnum;
import java.io.IOException;
import java.util.*;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.JsonSerializer;
import org.codehaus.jackson.map.SerializerProvider;
public class NamedEnumJsonSerializer extends JsonSerializer<NamedEnum> {
@Override
public void serialize(NamedEnum value, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
Map<String, String> map = new HashMap<>();
map.put("id", value.name());
map.put("name", value.getName());
jgen.writeObject(map);
}
}
With Jackson 2.x:
pom.xml:
<properties>
<jackson.version>2.3.3</jackson.version>
</properties>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>
Rule.java:
import com.fasterxml.jackson.annotation.JsonFormat;
@Entity
@Table(name = "RULE")
public class Rule {
@Column(name = "STATUS", nullable = false, updatable = true)
@Enumerated(EnumType.STRING)
private Status status;
public Status getStatus() { return status; }
public void setStatus(Status status) { this.status = status; }
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public static enum Status {
OPEN("open rule"),
CLOSED("closed rule"),
WORKING("rule in work");
private String name;
Status(String name) { this.name = name; }
public String getName() { return this.name; }
public String getId() { return this.name(); }
};
}
Rule.Status.CLOSED
translated to {id: "CLOSED", name: "closed rule"}
.