Skip to content

SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS should not apply to Maps with uncomparable keys #4773

@nathanukey

Description

@nathanukey

Search before asking

  • I searched in the issues and found nothing similar.

Describe the bug

We have a global ObjectMapper configured in springboot used to convert objects to JSON for REST return values as well as sending objects over JMS and other places.

To have somewhat deterministic ordering of the JSON elements, we have turned on

objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);

This has been working fine for years, but now one of the other teams have implemented a map of exchange rates
Map<java.util.Currency, internal.dto.ExchangeRate> exchangeRates

When the system tries to convert this to JSON, it causes an exception:

Caused by: java.lang.ClassCastException: class java.util.Currency cannot be cast to class java.lang.Comparable (java.util.Currency and java.lang.Comparable are in module java.base of loader 'bootstrap')
	at java.base/java.util.TreeMap.compare(TreeMap.java:1569)
	at java.base/java.util.TreeMap.addEntryToEmptyMap(TreeMap.java:776)
	at java.base/java.util.TreeMap.put(TreeMap.java:785)
	at java.base/java.util.TreeMap.put(TreeMap.java:534)
	at java.base/java.util.AbstractMap.putAll(AbstractMap.java:281)
	at java.base/java.util.TreeMap.putAll(TreeMap.java:326)
	at java.base/java.util.TreeMap.<init>(TreeMap.java:187)
	at com.fasterxml.jackson.databind.ser.std.MapSerializer._orderEntries(MapSerializer.java:1182)
	at com.fasterxml.jackson.databind.ser.std.MapSerializer.serializeWithoutTypeInfo(MapSerializer.java:754)
	at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:720)
	at com.fasterxml.jackson.databind.ser.std.MapSerializer.serialize(MapSerializer.java:35)
	at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:733)
	at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:774)
	... 32 common frames omitted

The solution seems to be to disable map sorting feature, but then we lose the huge value this has given everywhere else with deterministic ordering.

In this situation, would be preferable if the fallback was just not to sort a map that can't be sorted. Perhaps warrants a different feature flag, ORDER_COMPARABLE_MAP_ENTRIES_BY_KEYS?

Some way to specify global sorting comparators would be nice, but since there is always possibility a new Map would be created using keys that weren't sorted, defaulting to converting/sending message (even if not sorted) would still be preference.

That secondary ability to sort seems to already be raised here: #2162
But still feel this is separate issue/bug, ability to keep sorting feature without failing on unsortables.

Version Information

2.14

Reproduction

    private static class Entity {
        private Map<Currency, String> exampleMap = new HashMap<>();

        public Map<Currency, String> getExampleMap() {
            return exampleMap;
        }

        public void setExampleMap(Map<Currency, String> exampleMap) {
            this.exampleMap = exampleMap;
        }
    }

    @Test
    void testDeserializationCurrencyMap3() throws IOException {
        final Entity entity = new Entity();
        final Map<Currency, String> exampleMap = entity.getExampleMap();
        exampleMap.put(Currency.getInstance("GBP"), "GBP_TEXT");
        exampleMap.put(Currency.getInstance("AUD"), "AUD_TEXT");

        final ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true);

        objectMapper.writeValueAsString(entity);
    }

Expected behavior

No response

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    2.19Issues planned at 2.19 or laterhas-failing-testIndicates that there exists a test case (under `failing/`) to reproduce the issue

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions