How to serialise data structures with duplicate keys using Jackson?

Viewed 75

The use-case

Assuming I have a source data structure that allows duplicate keys, something like a Collection<Map.Entry<String, Object>>, i.e. a hypothetical super type of Map<String, Object>. That type's string representation might look like this:

+--+--+-----+
|ID|ID|VALUE|
+--+--+-----+
| 1| 2|    a|
| 1| 3|    b|
+--+--+-----+

Now I would like to use Jackson to serialise the above source data structure to the most logical JSON representation, given that JSON also allows for duplicate keys, e.g. like this:

[{
  "ID": 1,
  "ID": 2,
  "VALUE": "a"
}, {
  "ID": 1,
  "ID": 3,
  "VALUE": "b"
}]

A tempting, but unclean solution

A tempting approach here would be to let the source data structure just implement Map<String, Object>, violating the Map contract, which reads:

A map cannot contain duplicate keys; each key can map to at most one value.

And just make sure Map.entrySet() produces the duplicate keys and their values, hoping that Jackson uses Map.entrySet() instead of Map.keySet() and then Map.get(). This feels very fishy, because:

  • The Map contract is violated (as well as the Set contract for the sets returned by Map.entrySet() and Map.keySet())
  • By consequence, a few methods don't work as expected, including Map.get(), which can only produce one of the two conflicting values.
  • The Map could be mutable, but not resizable, just like Arrays.asList(), which seems rather unprecedented and thus confusing (e.g. remove() and clear() don't work, put(), putAll() only work to mutate existing key/value pairs, putIfAbsent() is weird, because it doesn't work when a key is absent)
  • While the above may be acceptable for the mere serialisation use-case, the source data structure is used for many other purposes, and users would now get a flawed Map representation of it, for all non-serialisation use-cases as well.

Hoping for a better solution

Is there a better out-of-the-box way to support duplicate keys when serialising data with Jackson, or do I have to implement a JsonSerializer for that purpose?

For context, the real-world source data structure is a jOOQ Record

0 Answers
Related