Есть много способов добиться этого, если вы уверены, что в вашем случае требуется объединение объектов.
Во-первых, в Java уже реализовано Long
объединение объектов для небольшого диапазона от -128 до 127 включительно. См. исходный код Long.valueOf
.
Пусть у нас есть 2 объекта JSON, которые мы хотим десериализовать: map1
и map2
:
final String map1 = "{\"1\": \"Hello\", \"10000000\": \"world!\"}";
final String map2 = "{\"1\": \"You\", \"10000000\": \"rock!\"}";
Стандартная десериализация
Если мы используем стандартную десериализацию:
final ObjectMapper mapper = new ObjectMapper();
final TypeReference<Map<Long, String>> typeRef = new TypeReference<Map<Long, String>>() {};
final Map<Long, String> deserializedMap1 = mapper.readValue(map1, typeRef);
final Map<Long, String> deserializedMap2 = mapper.readValue(map2, typeRef);
printMap(deserializedMap1);
printMap(deserializedMap2);
Где printMap
определяется как
private static void printMap(Map<Long, String> longStringMap) {
longStringMap.forEach((Long k, String v) -> {
System.out.printf("key object id %d \t %s -> %s %n", System.identityHashCode(k), k, v);
});
}
мы получаем следующий вывод:
key object id 1635756693 1 -> Hello
key object id 504527234 10000000 -> world!
key object id 1635756693 1 -> You
key object id 101478235 10000000 -> rock!
Обратите внимание, что ключ 1
— это один и тот же объект с хэш-кодом 1635756693
на обеих картах. Это связано со встроенным пулом для диапазона [-128,127].
Решение 1: десериализация @JsonAnySetter
Мы можем определить объект-оболочку для карты и использовать аннотацию @JsonAnySetter
для перехвата всех десериализуемых пар ключ-значение. Затем мы можем интернировать каждый объект Long
, используя Гуава StrongInterner
:
static class CustomLongPoolingMap {
private static final Interner<Long> LONG_POOL = Interners.newStrongInterner();
private final Map<Long, String> map = new HashMap<>();
@JsonAnySetter
public void addEntry(String key, String value) {
map.put(LONG_POOL.intern(Long.parseLong(key)), value);
}
public Map<Long, String> getMap() {
return map;
}
}
Мы будем использовать это так:
final ObjectMapper mapper = new ObjectMapper();
final Map<Long, String> deserializedMap1 = mapper.readValue(map1, CustomLongPoolingMap.class).getMap();
final Map<Long, String> deserializedMap2 = mapper.readValue(map2, CustomLongPoolingMap.class).getMap();
Выход:
key object id 1635756693 1 -> Hello
key object id 1596467899 10000000 -> world!
key object id 1635756693 1 -> You
key object id 1596467899 10000000 -> rock!
Теперь вы можете видеть, что ключ 10000000
также является одним и тем же объектом на обеих картах с хэш-кодом 1596467899
.
Решение 2. Зарегистрируйте собственный KeyDeserializer
Определить пользовательский KeySerializer
:
public static class MyCustomKeyDeserializer extends KeyDeserializer {
private static final Interner<Long> LONG_POOL = Interners.newStrongInterner();
@Override
public Long deserializeKey(String key, DeserializationContext ctxt) {
return LONG_POOL.intern(Long.parseLong(key));
}
}
И зарегистрируйте его с помощью ObjectMapper
:
final SimpleModule module = new SimpleModule();
module.addKeyDeserializer(Long.class, new MyCustomKeyDeserializer());
final ObjectMapper mapper = new ObjectMapper().registerModule(module);
final TypeReference<Map<Long, String>> typeRef = new TypeReference<Map<Long, String>>() {};
final Map<Long, String> deserializedMap1 = mapper.readValue(map1, typeRef);
final Map<Long, String> deserializedMap2 = mapper.readValue(map2, typeRef);
Решение 3. Используйте собственный KeyDeserializer через аннотацию @JsonDeserialize
Определить объект-оболочку
static class MapWrapper {
@JsonDeserialize(keyUsing = MyCustomKeyDeserializer.class)
private Map<Long, String> map1;
@JsonDeserialize(keyUsing = MyCustomKeyDeserializer.class)
private Map<Long, String> map2;
}
И десериализовать его:
final ObjectMapper mapper = new ObjectMapper();
final String json = "{\"map1\": " + map1 + ", \"map2\": " + map2 + "}";
final MapWrapper wrapper = mapper.readValue(json, MapWrapper.class);
final Map<Long, String> deserializedMap1 = wrapper.map1;
final Map<Long, String> deserializedMap2 = wrapper.map2;
Решение 4. Используйте библиотеку Trove TLongObjectMap
, чтобы полностью избежать использования Long
объектов
Библиотека Trove реализует карты, которые используют примитивные типы для ключей, чтобы полностью исключить накладные расходы на упакованные объекты. Однако он находится в несколько спящем состоянии.
В вашем случае вам нужно TLongObjectHashMap.
Существует библиотека, которая определяет десериализатор для TIntObjectMap
: trove/deser/TIntObjectMapDeserializer.java?at=master&fileviewer=file-view-default" rel="nofollow noreferrer">https://bitbucket.org/marshallpierce/jackson-datatype-trove/src/d7386afab0eece6f34a0af69b76b478f417f0bd4/src/main/java /com/palominolabs/jackson/datatype/trove/deser/TIntObjectMapDeserializer.java?at=master&fileviewer=file-view-default
Думаю, адаптировать его под TLongObjectMap
будет совсем несложно.
Полный код этого ответа можно найти здесь: https://gist.github.com/shtratos/f0a81515d19b858dafb71e86b62cb474< /а>
Я использовал ответы на этот вопрос для решений 2 и 3: Десериализация нестроковых сопоставить ключи с Джексоном
person
Devstr
schedule
21.02.2018