skip to Main Content

JSON enum deserialization is not working.

Interface:

@JsonTypeInfo(
        use = JsonTypeInfo.Id.NAME,
        include = JsonTypeInfo.As.PROPERTY,
        property="rlType"
)
@JsonSubTypes({
        @JsonSubTypes.Type(value = B.class)
})
public interface IA {
}

B class

@JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
public enum B implements IA {
    X,
    Y,
    Z
}

Settings

public record Settings(IA rlType, int myint1, int myint2) {}

Here is a unit test of how i want to use it:

@Test
void testDeserialization() throws Exception {
        String json = """
            {
                "rlType": "X",
                "myint1": 5,
                "myint2": 10
            }
        """;
        ObjectMapper objectMapper = new ObjectMapper();
        RateLimiterSettings settings = objectMapper.readValue(json, Settings.class);

I’m getting the following error:

com.fasterxml.jackson.databind.exc.InvalidTypeIdException: Could not
resolve subtype of [simple type, class IA]: missing type id property
‘tlType’ (for POJO property ‘rlType’) at [Source: (String)" {
"rlType": "X",
"myint1": 5,
"myint2": 10
} "; line: 2, column: 19] (through reference chain: Settings["rlType"])

I tried to use Settings(**B** rlType, int myint1, int myint2) and it worked but I want to do that with the interface.
What am I missing here?

When I write the object to json string I get:
{"rlType":["B","X"], .. } – why is it array?

2

Answers


  1. Chosen as BEST ANSWER

    Was able to solve that with a JsonDeserializer.

    public class IADeserializer extends JsonDeserializer<IA> {
    
        @Override
        public IA deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            String typeName = p.getValueAsString();
    
            // Try to match with the first enum
            try {
                return B.valueOf(typeName);
            } catch (IllegalArgumentException e) {
                // Ignored
            }
    
            // Try to match with the second enum
            try {
                return C.valueOf(typeName);
            } catch (IllegalArgumentException e) {
                // Ignored
            }
    
            // Handle case where the value doesn't match any known enum
            throw new IOException("Invalid rlType value: " + typeName);
        }
    }
    

    And

    @JsonDeserialize(using = IADeserializer.class)
        public interface IRateLimiterType {
    }
    

  2. Meanwhile as we found out the interface is necessary, this solution will work:

        @JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property="riType")
        @JsonSubTypes({ @JsonSubTypes.Type(B.class) })
        public interface IA {
        }
    
        public enum B implements IA {
            X,
            Y,
            Z
        }
        
        public record Settings(IA key, int myint1, int myint2)
        {
            
        }
        
        @Test
        void testJson() throws Exception
        {
            String json = """
                    {
                    "key": ["B", "X"],
                    "myint1":"1",
                    "myint2":"2"
                    }
                    """;
            System.out.println(new ObjectMapper().readValue(json, Settings.class));
        }
    

    Obviously Jackson uses a different syntax for (de)serializing enum values and ordinary classes. For an enum instance just a ["type", "value"] is used, for a class instance the full object notation { "riType":..., "property":"value", etc. }.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search