Gson Serializing the Integer object - java

I'm generating JSON using the Gson library. I'm attempting to generate JSON like:
{"Plaintext":{"type":"List","value":["1","2","3","4"]},"SINGLE_FUNCTION":{"value":"1"},"IN_FLOW":{"value":10}}
However, the JSON that is appearing is..
{"Plaintext":{"type":"List","value":["1","2","3","4"]},"SINGLE_FUNCTION":{"value":"1"},"IN_FLOW":{"value":{"value":10}}}
The problem, I found is that when my objects are being created, an Integer object is being stored. This is because of the type of the mapping is Object, which means the int type that I'm storing is automatically wrapped to type Integer and Gson is rendering the only non-null value, value inside the Integer class.
My question is, is there a way of having Gson render an Integer object as if it were a primitive type int?
Edit
So, The whole JSON is a Map. This Map is defined as:
private Map<String, ParameterWrapper> parameterMap;
The class ParameterWrapper looks like this:
public class ParameterWrapper<T> {
private String type;
private T value;
public ParameterWrapper(String type, T value) {
this.type = type;
this.value = value;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
I suspect that because I'm not specifying a type T for ParameterWrapper, it's defaulting to Object, which is why Gson is outputting it as an Object and not an int, despite it being an instance of Integer.

This sample code (with lombok #Data and guava Lists):
package so28235867;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import lombok.Data;
import java.util.List;
public class Foo {
public static void main(final String[] args) {
final Root root = Root.of(Plaintext.of("List", Lists.newArrayList("1", "2", "3", "4")), new Value<String>("1"), new Value<Integer>(10));
System.out.println(new Gson().toJson(root));
}
#Data(staticConstructor = "of")
// TODO: tweak JSON serialisation to respect naming convention
static class Root {
private final Plaintext Plaintext;
private final Value<String> SINGLE_FUNCTION;
private final Value<Integer> IN_FLOW;
}
#Data(staticConstructor = "of")
static class Plaintext {
private final String type;
private final List<String> value;
}
#Data
static class Value<T> {
private final T value;
}
}
outputs:
{"Plaintext":{"type":"List","value":["1","2","3","4"]},"SINGLE_FUNCTION":{"value":"1"},"IN_FLOW":{"value":10}}
And that looks like what you want. So you probably have an error in the definition of your object hierarchy.

Change this,
public T getValue() {
return value;
}
to,
public T getValue() {
if( value instanceOf Integer ) {
return value.intValue();
}
return value;
}

My question is, is there a way of having Gson render an Integer object as if it were a primitive type int?
You can manually add the IN_FLOW (or any primitive) value as Integer primitive value.
jsonObject.add("IN_FLOW",new JsonPrimitive(obj.getInFlow()));

Related

How to use dynamic property names for a Json object

How can we make the JSON property name dynamic. For example
public class Value {
#JsonProperty(value = "value")
private String val;
public void setVal(String val) {
this.val = val;
}
public String getVal() {
return val;
}
}
when serializing this object it's saved as {"value": "actual_value_saved"} but I want to make the key also dynamic like {"new_key": "actual_value_saved"}. Any help is much appreciated.
You can use JsonAnySetter JsonAnyGetter annotations. Behind you can use Map instance. In case you have always one-key-object you can use Collections.singletonMap in other case use HashMap or other implementation. Below example shows how easy you can use this approach and create as many random key-s as you want:
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Collections;
import java.util.Map;
import java.util.Objects;
public class JsonApp {
public static void main(String[] args) throws Exception {
DynamicJsonsFactory factory = new DynamicJsonsFactory();
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.writeValueAsString(factory.createUser("Vika")));
System.out.println(mapper.writeValueAsString(factory.createPhone("123-456-78-9")));
System.out.println(mapper.writeValueAsString(factory.any("val", "VAL!")));
}
}
class Value {
private Map<String, String> values;
#JsonAnySetter
public void put(String key, String value) {
values = Collections.singletonMap(key, value);
}
#JsonAnyGetter
public Map<String, String> getValues() {
return values;
}
#Override
public String toString() {
return values.toString();
}
}
class DynamicJsonsFactory {
public Value createUser(String name) {
return any("name", name);
}
public Value createPhone(String number) {
return any("phone", number);
}
public Value any(String key, String value) {
Value v = new Value();
v.put(Objects.requireNonNull(key), Objects.requireNonNull(value));
return v;
}
}
Above code prints:
{"name":"Vika"}
{"phone":"123-456-78-9"}
{"val":"VAL!"}
You could have all the possible names as variables, and annotate them so they are ignored if null. This way you only get in your JSON the one that has a value
Then change your setter to feed into the variable mapped to the key you want.
class Value {
#JsonProperty("val")
#JsonInclude(JsonInclude.Include.NON_NULL)
private String val;
#JsonProperty("new_key")
#JsonInclude(JsonInclude.Include.NON_NULL)
private String newKey;
#JsonProperty("any_random_string")
#JsonInclude(JsonInclude.Include.NON_NULL)
private String anyRandomString;
public void setVal(String s) {
if(/* condition1 */)
this.val = s;
else if (/* condition2 */) {
this.newKey = s;
} else if (/* condition3 */) {
this.anyRandomString = s;
}
}
}
Good question #Prasad, This answer is not about JAVA or SPRING BOOT, I'm just putting this answer because I searched to do this with node and hope this helps somebody somehow. In JAVASCRIPT we can add dynamic property names for JSON objects as below
var dogs = {};
var dogName = 'rocky';
dogs[dogName] = {
age: 2,
otherSomething: 'something'
};
dogName = 'lexy';
dogs[dogName] = {
age: 3,
otherSomething: 'something'
};
console.log(dogs);
But when we need to dynamically change the name we have to
get that property
and create another property with the same content and new name
and delete the old property from JSON
assign the new property to the JSON
is there a another way to dynamically change JSON name except for this method, thanks in advance

How to set floating point format for JAXB (json using eclipse moxy)

I have a simple pojo class:
#XmlRootElement
public static class MyClass {
private double f; //[0-1]
#XmlAttribute
//#XmlJavaTypeAdapter(FixedFloatingPointXmlAdapter.class) <-- has issues
public double getF() {
return f;
}
}
field f holds values in the range [0,1], which can sometimes be small. I'd like to avoid seeing things like 1.234E-7 (scientific notation) and would like to set the format used to print field f.
I've seen other answers that suggest using an XmlAdapter for this, like so:
public class FixedFloatingPointXmlAdapter extends XmlAdapter<String, Double> {
private static final DecimalFormat FORMAT = new DecimalFormat("0.00000000");
#Override
public Double unmarshal(String v) throws Exception {
return v == null ? null : Double.parseDouble(v);
}
#Override
public String marshal(Double v) throws Exception {
return v == null ? null : FORMAT.format(v);
}
}
but the problem with this is that the value would be printed out as a json STRING (so "0.25000000" instead of 0.25000000) - because the XmlAdapter returns a String (not because FORMAT places quotes. it doesnt)
is there any JAXB/moxy annotation that would allow me to control the formatting without turning the field into a json string?
This solution is using transformation annotations of EclipseLink MOXy.
import org.eclipse.persistence.oxm.annotations.XmlTransformation;
import org.eclipse.persistence.oxm.annotations.XmlWriteTransformer;
#XmlRootElement
public static class MyClass {
#XmlAttribute
#XmlReadTransformer(transformerClass=FixedFloatingTransformer.class)
#XmlWriteTransformer(xmlPath="#f", transformerClass=FixedFloatingTransformer.class)
private double f; //[0-1]
public double getF() {
return f;
}
}
Here is the implementation of transformerClass.
import java.text.DecimalFormat;
import org.eclipse.persistence.mappings.foundation.AbstractTransformationMapping;
import org.eclipse.persistence.mappings.transformers.AttributeTransformer;
import org.eclipse.persistence.mappings.transformers.FieldTransformer;
import org.eclipse.persistence.sessions.Record;
import org.eclipse.persistence.sessions.Session;
public class FixedFloatingTransformer implements FieldTransformer, AttributeTransformer {
private AbstractTransformationMapping mapping;
private static DecimalFormat FORMAT = new DecimalFormat("0.00000000");
public Object buildAttributeValue(Record r, Object obj, Session arg2) {
return r.get(mapping.getFields().get(0));
}
public Object buildFieldValue(Object instance, String xpath, Session session) {
Object o2 = mapping.getAttributeValueFromObject(instance);
if (o2 instanceof Number) {
return ((DecimalFormat)FORMAT.clone()).format((Number)o2);
}
return null;
}
public void initialize(AbstractTransformationMapping mapping) {
this.mapping = mapping;
}
}
Update in 2016/12/13
The #XmlSchemaType are referenced when moxy unmarshal the object to JSON string.
If the name is boolean or number type, moxy will outputed the value string without quoted char.
Sample 1: You have to set formated value to f.
public static class MyClass {
private String f;
#XmlAttribute
#XmlSchemaType(name="double")
public String getF() {
return f;
}
}
Sample 2 : Add extra getter to return formatted value.
public static class MyClass {
private double f;
#XmlTransient //preventing the mapping
public double getF() {
return f;
}
#XmlAttribute(name="f")
#XmlSchemaType(name="double")
public String getFAsString() {
return new DecimalFormat("0.00000000").format(f);
}
}

java/jackson - #JsonValue/#JsonCreator and null

I have a Value class which holds a value:
public class Value {
protected final Object value;
#JsonValue
public Object getValue() {
return value;
}
#JsonCreator
public Value(final Object value) {
this.value = value;
}
}
This Value class is embedded as a field (amongst other fields) in a data class:
class Data {
protected final Value value;
#JsonProperty("value")
public Value getValue() {
return value;
}
...
#JsonCreator
public Data(#JsonProperty("value") final Value value, ...) {
this.value = value;
....
}
}
When the input JSON has null for the value field of a data object (see below for example), Data.value is null. I would like to have Data.value set to new Value(null). In other words, the data object must hold a non-null value object, which holds the null.
{
"value" : null,
...
}
What is the easiest way to achieve this? I could ofcourse alter the constructor of Data, but I am wondering if Jackson could resolve this automatically.
You can write a custom de-serializer and override the getNullValue() method according to your requirements e.g.
public final class InstantiateOnNullDeserializer
extends JsonNodeDeserializer
{
#Override
public JsonNode getNullValue()
{
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.convertValue(new Value(null), JsonNode.class);
return node;
}
}
and register it on the value field of your Data class
class Data {
#JsonDeserialize(using = InstantiateOnNullDeserializer.class)
protected final Value value;
#JsonProperty("value")
public Value getValue() {
return value;
}
#JsonCreator
public Data(Value value) {
this.value = value;
}
}
Note that you need to remove the #JsonProperty("value") to avoid argument type mismatch. By removing JsonProperty annotation you create a so-called "delegate creator",
Jackson will than first bind JSON into type of the argument, and then call a creator
I do not believe that this is possible without creating your own deserializer or modify the constructor (or #JsonCreator).
From a good old thread in the Jackson User Group:
#JsonProperty does not support transformations, since the data binding is based on incremental parsing and does not have access to full tree representation.
So, in order to avoid a custom deserializer I would do something along the lines of:
#JsonCreator
public Data(#JsonProperty("value") final String value) {
this.value = new Value(value);
}
Which is kind of the opposite of what you asked ;)

How do I make a RESTful service handle custom object in the Response?

How do I make custom object in a restful Response look like a native type. In other words, I don't want to use a getter and setter, but rather have the object marshal and unmarshall like a string. I can use a constructor for unmarshall, but I don't know how to get the object through the Response of the service. When I run the sample, I get this result:
<data>
<nType>123</nType>
<myType/>
<notMyType>
<value>def</value>
</notMyType>
</data>
I am trying to get myType to look like nType. Here is a short code example:
package rscust;
import java.util.HashSet;
import java.util.Set;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.xml.bind.annotation.XmlRootElement;
#Path("/")
public class Service extends Application {
#XmlRootElement
public static class Data {
public String nType;
public MyType myType;
public NotMyType notMyType;
public Data() {}
}
// custom class does not work in Response
public static class MyType {
private String value;
public MyType() {}
public MyType(String value) {
this.value=value;
}
public String toString() {
return value;
}
}
// works with getter and setter, but I don't want that
public static class NotMyType {
private String value;
public NotMyType() {}
public NotMyType(String value) {
this.setValue(value);
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
#GET
#Produces(value={MediaType.APPLICATION_XML})
public Response get(
#QueryParam(value = "ntype")String nType,
#QueryParam(value = "mytype")MyType myType,
#QueryParam(value = "notmytype")NotMyType notMyType
) {
Data data = new Data();
data.nType = nType;
data.myType = myType;
data.notMyType = notMyType;
return Response.ok().entity(data).build();
}
private HashSet<Object> singletons;
public Service() {
super();
singletons = new HashSet<Object>();
singletons.add(this);
}
public Set<Object> getSingletons() {
return singletons;
}
}
The answer is you need to add annotations to the custom class to tell it how to deserialize and serialize the different fields. Your get method looks fine, but the custom classes aren't annotated at all. MyType and NotMyType should also have # XmlRootElement as well as annotations to mark which fields are # Transient and which are # JsonProperty
#XmlRootElement
public static class Data {
#JsonProperty
public String nType;
#JsonProperty
public MyType myType;
#JsonProperty
public NotMyType notMyType;
public Data() {}
}
#XmlRootElement
public static class MyType {
#JsonProperty
private String value;
public MyType() {}
public MyType(String value) {
this.value=value;
}
public String toString() {
return value;
}
}
also, since you made those objects part of Data already, just request a single data object, and then you can pull the others from there.
public Response get( #QueryParam(value = "data")Data d,){
Data d = data.nType;
}
If you don't wont to use getter and setter, than you need set the fields 'public'. Because RESTeasy need access to the fields.

Jackson enum Serializing and DeSerializer

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!
#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"
}
]
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"}

Categories

Resources