Using JSON-B / Yasson is there any way to ignore case of enums when deserializing?
public class MyObject{
MyEnum condition;
//getters and setters
}
public enum MyEnum{
NEW, OLD, REFURBISHED;
}
part of incoming JSON: "condition" : "new"
The problem is that the incoming JSON uses the enums in lowercase.
I don't thing this should be available out of the box. Because you technically can have both old and OLD as valid values of your enum living together, allowing for out-of-the-box uppercase conversion can break roundtrip equivalence. Think of serializing a MyEnum.old value to end up with a MyEnum.OLD value on deserialization.
You can however force such a behavior by using an adapter.
public static class MyAdapter implements JsonbAdapter<MyEnum, String> {
#Override
public String adaptToJson(MyEnum value) {
return value.name();
}
#Override
public MyEnum adaptFromJson(String s) {
return MyEnum.valueOf(s.toUpperCase());
}
}
Next, annotate the enum with #JsonbTypeAdapter.
#JsonbTypeAdapter(MyAdapter.class)
public enum MyEnum {
NEW,
OLD,
REFURBISHED;
}
Alternatively, you create your Jsonb provider as follows.
Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withAdapters(new MyAdapter()));
Related
I have a common class
public CommonClass {
private Enum<?> field1;
private String field2;
private String field3;
private Map<? extends Enum<?>, Map<String, Object>> map;
// constructor, getters setters
}
And two enums
public enum A {
FIELD,
VALUE
}
public enum B {
ENUM1,
ENUM1
}
And json to parse in it class
{
"field1": "FIELD",
"field2": "field2",
"field3": "field2",
"map": {
"ENUM1": {
// some inner data
},
"ENUM2":{
// some inner data
}
}
}
When I try to parse this JSON to an object I got an exception
java.lang.IllegalArgumentException: No enum constants for class java.lang.Enum
This exception occurs when Jackson tried to deserialize "ENUM1" which is a ? extends Enum<?> to B
When I debug it, Jackson thought that this property is simple type.
Is any suggestion why it doesn't work?
I think you cannot deserialize even this simplified one by default:
public class CommonClass {
private Enum<?> field1;
}
I is because no Enum nor enum has public no args constructor. To serialize beforementioned is easy. But to deserialize you need to know which enum and which value. So if you had:
public class CommonClass {
private A field1;
}
It would work because Jackson sees the enum is of type A and - I guess - makes it like:
A.valueOf("FIELD");
And as you see here A is static. Enums are static and also their values are final.
If you want to deserialize arbitrary generic enum value Enum<?> you need to have custom deserializer that determines the enum type and together with value and gets instance of enum like above. And you might also need a custom serializer that serializes also the type for custom deserializer to read.
I have an object named AddOnsSRO.Only on serialization I want the names of fields of the object to be changed.
Tried using #JsonProperty on getter methods but it gives me a renamed field even on usages where serialization is not involved.
public class AddOnsSRO {
private String sideCar;
private String sideCarCoverage;
#JsonSerialize
#JsonProperty("abc")
public String getSideCar() {
return sideCar;
}
public void setSideCar(String sideCar) {
this.sideCar = sideCar;
}
#JsonSerialize
#JsonProperty("xyz")
public String getSideCarCoverage() {
return sideCarCoverage;
}
public void setSideCarCoverage(String sideCarCoverage) {
this.sideCarCoverage = sideCarCoverage;
}
}
Only on serialization the following fields : sideCar and sideCarCoverage must be renamed to abc and xyz respectively.
For any other use except serialization the field names should be sideCar and sideCarCoverage only.
Please help and suggest changes or annotations accordingly.
For effecting only serializing use #JsonGetter instead of #JsonProperty
#JsonGetter("abc")
public String getSideCar() {
return sideCar;
}
Getter means that when serializing Object instance of class that has this method (possibly inherited from a super class), a call is made through the method, and return value will be serialized as value of the property.
You can add #JsonSetter to setter method for deserialize:
#JsonSetter("sideCar")
public void setSideCar(String sideCar) {
this.sideCar = sideCar;
}
your code looks good...Please upgrade your jackson lib... if you are using old
Here's my situation,
I have a class with Enum type fields. I want to execute annotated validation for enum types, similar to annotations for strings, example: #Size, #NotNull etc.
Problem is, json deserializer fails on enum type before validation occurs.
public class myClass {
#JsonProperty
//#SomeCustomValidator -- ??
private EnumType enumValue;
}
public enum EnumType {
A,
B,
C
}
Few things:
I do not want to change the data type to String.
Tried steps in following threads, didn't fix my problem.
Tried this link, but get an error in deserialization before validation hits
Tried this link, but it works only when data Type is String
Validation works after the type of the argument is resolved. So I don't see a way how to use String validating annotations on enums. As workaround you can use #JsonCreator and do some validation before object creation.
public enum EnumType {
A,
B,
C;
#JsonCreator
public static EnumType from(String s) {
// add your logic here, for example
/*
if (Stream.of(values()).map(Enum::name).noneMatch(name -> name.equals(s))) {
throw new MyServiceException("Bad value " + s);
}
*/
return valueOf(s);
}
}
I have an enum something like this
public Enum MyEnum {
NEW("NEW"),
OLD("OLD"),
IN_PROCESS("IN PROCESS");
}
The mapping on ibatis works fine for the NEW and OLD, but encounters an error when the IN_PROCESS is encountered since the value of my IN_PROCESS in the DB is IN PROCESS, and the error indicates that ibatis tries to find an enum with that value, can someone suggest a solution?
MyBatis use an EnumTypeHandler to do mappings with enums. In this Enum type handler it uses the name() method of the Enums which returns the string value of the variable name.
For example NEW -> "NEW" and IN_PROCESS -> "IN_PROCESS".
Otherwise, to get the value it uses Enum.valueOf(type, s); which gets the value of the Enum through the String value which corresponds with the variable name ("NEW" -> MyEnum.NEW, "IN_PROCESS" -> MyEnum.IN_PROCESS) and internally is used the method name().
You cannot overwrite name() because is marked as final so you options are:
The easy way is to use IN_PROCESS instead of IN PROCESS. I prefer this, is easier and fast.
The second option is create your TypeHanlder for this enum and check if the parameter is IN PROCESS and search with IN_PROCESS.
I had the same issue, I ended up writing a custom setter on my POJO to convert the string value.
public Enum MyEnum {
NEW("NEW"),
OLD("OLD"),
IN_PROCESS("IN PROCESS");
public static MyEnum fromValue(String v){
.... find the enum based on value
}
}
public class POJO {
private MyEnum myEnum;
public void setMyEnum(String strV){
myEnum=MyEnum.fromValue(strV)
}
}
A client is having an issue running java2ws on some of their code, which uses & extends classes that are consumed from my SOAP web services. Confused yet? :)
I'm exposing a SOAP web service (JBoss5, Java 6). Someone is consuming that web service with Axis1 and creating a jar out of it with the data types and client stubs. They are then defining their own type, which extends one of my types. My type contains an enumeration.
class MyParent {
private MyEnumType myEnum;
// getters, settters for myEnum;
}
class TheirChild extends MyParent {
...
}
When they are running java2ws on their code (which extends my class), they get
Caused by: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 2 counts of IllegalAnnotationExceptions
net.foo.bar.MyEnuMType does not have a no-arg default constructor.
this problem is related to the following location:
at net.foo.bar.MyEnumType
at public net.foo.bar.MyEnumType net.foo.bar.MyParent.getMyEnum()
The enum I've defined is below. This is now how it comes out after being consumed, but it's how I have it defined on the app server:
#XmlType(name = "MyEnumType")
#XmlEnum
public enum MyEnumType {
Val1("Val1"),
Val2("Val2")
private final String value;
MyEnumType(String v) {
value = v;
}
public String value() {
return value;
}
public static MyEnumType fromValue(String v) {
if (v == null || v.length() == 0) {
return null;
}
if (v.equals("Val1")) {
return MyEnumType.Val1;
}
if (v.equals("Val2")) {
return MyEnumType.Val2;
}
return null;
}
}
I've seen things online and other posts, like (this one) regarding Jaxb's inability to handle Lists or things like that, but I'm baffled about my enum. I'm pretty sure you can't have a default constructor for an enum (well, at least a public no-arg constructor, Java yells at me when I try), so I'm not sure what makes this error possible. Any ideas?
Also, the "2 counts of IllegalAnnotationsExceptions" may be because my code actually has two enums that are written similarly, but I left them out of this example for brevity.
The no-arg constructor for JAXB doesn't have to be public, it can be private:
private String value;
private MyEnumType() {} // for JAXB
MyEnumType(String v) {
value = v;
}
You can't keep the value member final this way, though.
I am certain you can have a default constructor for an enum.
In fact, that what you have when you don't define a constructor explicitely
(like yours with a String parameter).
You can also have several constructors, one no-args and others.
In the precise example you give, it would be simple to avoid the String parameter altogether.
The provided name() method has exactly the value you are provided.
The code would even be simpler:
#XmlType(name = "MyEnumType")
#XmlEnum
public enum MyEnumType {
Val1, Val2;
public String value() {
return name();
}
public static MyEnumType fromValue(String v) {
for(MyEnumType type : values()) {
if (type.value().equals(v)) {
return type;
}
}
return null;
}
}
If you have really some complex parameters to set to each value, and can't have specific constructors because of a library, you could also store your varying values into an EnumMap, and read this as needed.
when you do from-java-to-wsdl, apache check at first is it enum class or not, and only if this check fail, it check for constructor. You can see it in org.apache.axis.wsdl.fromJava.Types::isBeanCompatible. Any normal man, will think that if he write
public enum MyEnum{}
it will be enough. But Apache developers does not think so (IDK why, may be for some compatibility reasons). They do this method - org.apache.axis.utils.JavaUtils::isEnumClassSub.
If you will decomile this class, you will see, that your enum
MUST implement public String getValue() {return name();}
MUST implement public MyEnum fromString(String v){return valueOf(v);}
CAN'T contain public void setValue(){}
MUST implement String toString(), but each object implement it.