UpdateDeserializer extends JsonDeserializer<Update<?>> implements ContextualDeserializer { @Override public Update<?> deserialize(JsonParser parser, DeserializationContext ctx) throws IOException { ObjectMapper mapper = (ObjectMapper) parser.getCodec(); JsonNode node = mapper.readTree(parser); ImmutableSet.Builder<String> setFields = ImmutableSet.builder(); ImmutableSet.Builder<String> removeFields = ImmutableSet.builder(); Map<String,JsonNode> setNodes = new HashMap<>(); Iterator<String> iter = node.fieldNames(); while (iter.hasNext()) { String field = iter.next(); JsonNode value = node.get(field); if (value instanceof NullNode) { removeFields.add(field); } else if (value instanceof TextNode && value.toString().equals("\"\"")) { removeFields.add(field); } else { setFields.add(field); setNodes.put(field, value); } } Object setObject = mapper.convertValue(setNodes, valueType); return Update.of(setObject, setFields.build(), removeFields.build()); }  @Override JsonDeserializer<?> createContextual(DeserializationContext ctx, BeanProperty property); @Override Update<?> deserialize(JsonParser parser, DeserializationContext ctx);  }
@Test public void testDeserialize() throws Exception { ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); String json = "{\"name\":\"Marty\",\"birthday\":\"1985-10-26\",\"email\":null,\"country\":\"\"}"; Update<TestModel> update = mapper.readValue(json, new TypeReference<Update<TestModel>>() {}); assertEquals("Marty", update.getPartial().getName()); assertEquals(LocalDate.of(1985, 10, 26), update.getPartial().getBirthday()); assertNull(update.getPartial().getEmail()); assertNull(update.getPartial().getCountry()); assertEquals(ImmutableSet.of("email", "country"), update.getRemoveFields()); }