Skip to content

EnumSet deserialization does not work when we activate default typing in ObjectMapper #4214

@dvhvsekhar

Description

@dvhvsekhar

Search before asking

  • I searched in the issues and found nothing similar.

Describe the bug

If we serialize an object which has a property of type Set, but referring EnumSet at runtime, then serialization uses CollectionSerializer instead of EnumSetSerializer for this property, when we activate default typing in ObjectMapper. This causes deserialization (using EnumSetDeserializer) to fail, since EnumSetDeserializer expects encoding of EnumSetSerializer, but not of CollectionSerializer.

Version Information

2.14.0 and above

Reproduction

If we run the below given test (java code), we get following error:

[ERROR] EnumSetSerializationTest.testSerialization:69 » MismatchedInput Cannot deserialize value of type com.fasterxml.jackson.databind.ser.enums.EnumSetSerializationTest$MyEnum from Array value (token JsonToken.START_ARRAY)
at [Source: REDACTED (StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION disabled); line: 1, column: 191] (through reference chain: com.fasterxml.jackson.databind.ser.enums.EnumSetSerializationTest$EnumSetHolder["enumSet"]->java.util.RegularEnumSet[0])

package com.fasterxml.jackson.databind.ser.enums;

import com.fasterxml.jackson.databind.BaseMapTest;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.util.EnumSet;
import java.util.Set;

import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper.DefaultTyping;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;

public class EnumSetSerializationTest extends BaseMapTest {

	static enum MyEnum {
		ITEM_A, ITEM_B;
	}

	static class EnumSetHolder {
		private Set<MyEnum> enumSet; // use Set instead of EnumSet for type of this

		public Set<MyEnum> getEnumSet() {
			return enumSet;
		}

		public void setEnumSet(Set<MyEnum> enumSet) {
			this.enumSet = enumSet;
		}

		@Override
		public boolean equals(Object o) {
			if (!(o instanceof EnumSetHolder)) {
				return false;
			}
			EnumSetHolder eh = (EnumSetHolder) o;
			if (eh == this) {
				return true;
			}
			if (enumSet == null) {
				if (eh.getEnumSet() == null) {
					return true;
				} else {
					return false;
				}
			} else {
				if (eh.getEnumSet() == null) {
					return false;
				}
				return enumSet.containsAll(eh.getEnumSet());
			}
		}
	}

	public void testSerialization() throws Exception {
		ObjectMapper mapper = jsonMapperBuilder().build()
				.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
				// this type information is needed in json string, for this test
				.activateDefaultTyping(BasicPolymorphicTypeValidator.builder().allowIfBaseType(Object.class).build(),
						DefaultTyping.EVERYTHING);

		EnumSetHolder enumSetHolder = new EnumSetHolder();
		enumSetHolder.setEnumSet(EnumSet.allOf(MyEnum.class));
		String jsonStr = mapper.writeValueAsString(enumSetHolder);
		EnumSetHolder result = mapper.readValue(jsonStr, EnumSetHolder.class);
		assertEquals(result, enumSetHolder);
	}
}

Expected behavior

Above given test code should pass.

Additional context

As a workaround, if we make type of EnumSetHolder#enumSet property as EnumSet instead of Set, in the above given code, test passes.

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    2.17Issues planned at earliest for 2.17

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions