将我的修复配置为全局
@Configuration
public class MyAppConfiguration implements WebMvcConfigurer {
/**
* allows POJOs using json filtering to serialize all properties when not being filtered
*/
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
for (HttpMessageConverter<?> converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter) {
((MappingJackson2HttpMessageConverter) converter)
.getObjectMapper()
// not related to this thread, this is for property filtering when serializing POJOs
.setFilterProvider(new SimpleFilterProvider().setFailOnUnknownId(false))
// these modules are related to to this thread
.registerModule(
new SimpleModule().addDeserializer(Set.class, new CollectionDeserializer())
)
.registerModule(
new SimpleModule().addDeserializer(List.class, new CollectionDeserializer())
)
.registerModule(
new SimpleModule().addDeserializer(Map.class, new CustomMapDeserializer())
);
}
}
}
@Slf4j
@NoArgsConstructor
@AllArgsConstructor
private static class CollectionDeserializer<T>
extends JsonDeserializer<Collection<T>>
implements ContextualDeserializer {
private JavaType type;
private final ObjectMapper mapper = new ObjectMapper();
@Override
public JsonDeserializer<?> createContextual(
DeserializationContext deserializationContext,
BeanProperty beanProperty) {
final JavaType javaType = deserializationContext.getContextualType() != null
? deserializationContext.getContextualType()
: beanProperty.getMember().getType();
return new CollectionDeserializer<>(javaType);
}
@Override
public Collection<T> deserialize(
JsonParser jsonParser,
DeserializationContext deserializationContext) throws IOException {
try {
if (Set.class.isAssignableFrom(type.getRawClass())) {
return deserializeJsonToCollection(jsonParser, new HashSet<>());
}
if (List.class.isAssignableFrom(type.getRawClass())) {
return deserializeJsonToCollection(jsonParser, new ArrayList<>());
}
} catch (IllegalArgumentException e) {
log.warn("unable to deserialize array property in request");
throw e;
}
final String message = "unable to deserialize collection";
log.warn(message);
throw new CollectionDeserializationException(message);
}
private Collection<T> deserializeJsonToCollection(
JsonParser jsonParser,
Collection<T> collection) throws IOException {
((ArrayNode) jsonParser
.getCodec()
.readTree(jsonParser))
.forEach(item -> {
try {
final T value = mapper.readValue(
String.format("\"%s\"", item.textValue()),
type.getContentType()
);
collection.add(value);
} catch (IOException e) {
final String message = String.format(
"unable to deserialize value [%s] to type [%]",
item.textValue(),
type.getContentType()
);
log.warn(message);
throw new CollectionDeserializationException(message);
}
});
return collection;
}
@Override
public Collection<T> getNullValue(DeserializationContext ctxt) {
if (Set.class.isAssignableFrom(type.getRawClass())) {
return new HashSet<>();
}
if (List.class.isAssignableFrom(type.getRawClass())) {
return new ArrayList<>();
}
return null;
}
}
@Slf4j
private static class CustomMapDeserializer<T, K> extends JsonDeserializer<Map<T, K>> {
private final ObjectMapper mapper = new ObjectMapper();
private final TypeReference<Map<T, K>> mapTypeReference = new TypeReference<>() {
};
@Override
public Map<T, K> deserialize(
JsonParser jsonParser,
DeserializationContext deserializationContext) {
try {
return mapper.readValue(jsonParser, mapTypeReference);
} catch (Exception e) {
final String message = "unable to deserialize map";
log.warn(message);
throw new MapDeserializationException(message, e);
}
}
@Override
public Map<T, K> getNullValue(DeserializationContext ctxt) {
return new HashMap<>();
}
}