edu.isi.nlp.serialization.jackson.MultimapEntries Maven / Gradle / Ivy
package edu.isi.nlp.serialization.jackson;
import static com.google.common.base.Preconditions.checkNotNull;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.util.StdConverter;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Multimap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
/**
* Allows serialization of {@link Multimap}s with non-{@link String} keys using Jackson. By default,
* Jackson coerces all multimap keys to be {@code String}s when serializing, breaking round-tripping
* when there are non-{@code String} keys.
*
* This class can be used with Jackson's converter mechanism to work around this problem by
* supplying a substitute object for serialization which does not trigger the special key behavior
* (at some cost to performance/memory...). On the problematic multimap field, add the following
* annotations (assuming it is an {@link ImmutableListMultimap}:
*
*
{@code
* @JsonSerialize(converter=MultimapEntries.FromMultimap}
* @JsonDeserialize(converter=MapEntries.ToImmutableListMultimap}
* private final ImmutableListMap myMultimap();
*
* }
*
* There is also a converter to an {@link ImmutableSetMultimap}: {@link ToImmutableSetMultimap}.
*
* Please do not use this class for any purpose other than that described above.
*/
public final class MultimapEntries {
@JsonProperty("d")
private List> entries;
private MultimapEntries(@JsonProperty("d") List> entries) {
// we do not make a defensive copy for speed and because Jackson seems to implicitly promise
// it will not hold onto the reference
this.entries = entries;
}
static MultimapEntries fromMap(Multimap multimap) {
final List> entries = new ArrayList<>();
for (final Map.Entry> e : multimap.asMap().entrySet()) {
// we do not defensively copy the key or the value since this should only be used in
// Jackson converters, where it is not necessary
entries.add(new MultimapEntry<>(e.getKey(), e.getValue()));
}
return new MultimapEntries<>(entries);
}
// exception on null desirable
@SuppressWarnings("ConstantConditions")
ImmutableListMultimap toImmutableListMultimap() {
final ImmutableListMultimap.Builder ret = ImmutableListMultimap.builder();
for (final MultimapEntry entry : entries) {
ret.putAll(entry.key, entry.values);
}
return ret.build();
}
// exception on null desirable
@SuppressWarnings("ConstantConditions")
ImmutableSetMultimap toImmutableSetMultimap() {
final ImmutableSetMultimap.Builder ret = ImmutableSetMultimap.builder();
for (final MultimapEntry entry : entries) {
ret.putAll(entry.key, entry.values);
}
return ret.build();
}
public static final class MultimapEntry {
@JsonProperty("k")
@Nullable
private final K key;
@JsonProperty("v")
private final Collection values;
@JsonCreator
private MultimapEntry(@JsonProperty("k") K key, @JsonProperty("v") Collection values) {
this.key = key;
this.values = checkNotNull(values);
}
}
public static class FromMultimap
extends StdConverter, MultimapEntries