Revise type resolution for alignment with AbstractJsonHttpMessageConverter

See gh-21188
This commit is contained in:
Juergen Hoeller 2020-09-15 09:41:14 +02:00
parent cd6085a310
commit 801c8ed8ac
1 changed files with 35 additions and 24 deletions

View File

@ -27,6 +27,7 @@ import kotlinx.serialization.SerializationException;
import kotlinx.serialization.SerializersKt; import kotlinx.serialization.SerializersKt;
import kotlinx.serialization.json.Json; import kotlinx.serialization.json.Json;
import org.springframework.core.GenericTypeResolver;
import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage; import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
@ -46,6 +47,7 @@ import org.springframework.util.StreamUtils;
* *
* @author Andreas Ahlenstorf * @author Andreas Ahlenstorf
* @author Sebastien Deleuze * @author Sebastien Deleuze
* @author Juergen Hoeller
* @since 5.3 * @since 5.3
*/ */
public class KotlinSerializationJsonHttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> { public class KotlinSerializationJsonHttpMessageConverter extends AbstractGenericHttpMessageConverter<Object> {
@ -56,6 +58,7 @@ public class KotlinSerializationJsonHttpMessageConverter extends AbstractGeneric
private final Json json; private final Json json;
/** /**
* Construct a new {@code KotlinSerializationJsonHttpMessageConverter} with the default configuration. * Construct a new {@code KotlinSerializationJsonHttpMessageConverter} with the default configuration.
*/ */
@ -71,10 +74,11 @@ public class KotlinSerializationJsonHttpMessageConverter extends AbstractGeneric
this.json = json; this.json = json;
} }
@Override @Override
protected boolean supports(Class<?> clazz) { protected boolean supports(Class<?> clazz) {
try { try {
resolve(clazz); serializer(clazz);
return true; return true;
} }
catch (Exception ex) { catch (Exception ex) {
@ -83,17 +87,27 @@ public class KotlinSerializationJsonHttpMessageConverter extends AbstractGeneric
} }
@Override @Override
protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { public final Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage)
return this.read(clazz, null, inputMessage); throws IOException, HttpMessageNotReadableException {
return decode(serializer(GenericTypeResolver.resolveType(type, contextClass)), inputMessage);
} }
@Override @Override
public Object read(Type type, @Nullable Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { protected final Object readInternal(Class<?> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
return decode(serializer(clazz), inputMessage);
}
private Object decode(KSerializer<Object> serializer, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
MediaType contentType = inputMessage.getHeaders().getContentType(); MediaType contentType = inputMessage.getHeaders().getContentType();
String jsonText = StreamUtils.copyToString(inputMessage.getBody(), getCharsetToUse(contentType)); String jsonText = StreamUtils.copyToString(inputMessage.getBody(), getCharsetToUse(contentType));
try { try {
// TODO Use stream based API when available // TODO Use stream based API when available
return this.json.decodeFromString(resolve(type), jsonText); return this.json.decodeFromString(serializer, jsonText);
} }
catch (SerializationException ex) { catch (SerializationException ex) {
throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex, inputMessage); throw new HttpMessageNotReadableException("Could not read JSON: " + ex.getMessage(), ex, inputMessage);
@ -101,19 +115,17 @@ public class KotlinSerializationJsonHttpMessageConverter extends AbstractGeneric
} }
@Override @Override
protected void writeInternal(Object o, HttpOutputMessage outputMessage) throws HttpMessageNotWritableException { protected final void writeInternal(Object object, @Nullable Type type, HttpOutputMessage outputMessage)
try { throws IOException, HttpMessageNotWritableException {
this.writeInternal(o, o.getClass(), outputMessage);
} encode(object, serializer(type != null ? type : object.getClass()), outputMessage);
catch (IOException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
} }
@Override private void encode(Object object, KSerializer<Object> serializer, HttpOutputMessage outputMessage)
protected void writeInternal(Object o, @Nullable Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { throws IOException, HttpMessageNotWritableException {
try { try {
String json = this.json.encodeToString(resolve(type), o); String json = this.json.encodeToString(serializer, object);
MediaType contentType = outputMessage.getHeaders().getContentType(); MediaType contentType = outputMessage.getHeaders().getContentType();
outputMessage.getBody().write(json.getBytes(getCharsetToUse(contentType))); outputMessage.getBody().write(json.getBytes(getCharsetToUse(contentType)));
outputMessage.getBody().flush(); outputMessage.getBody().flush();
@ -134,16 +146,14 @@ public class KotlinSerializationJsonHttpMessageConverter extends AbstractGeneric
} }
/** /**
* Tries to find a serializer that can marshall or unmarshall instances of the given type using * Tries to find a serializer that can marshall or unmarshall instances of the given type
* kotlinx.serialization. If no serializer can be found, an exception is thrown. * using kotlinx.serialization. If no serializer can be found, an exception is thrown.
* <p> * <p>Resolved serializers are cached and cached results are returned on successive calls.
* Resolved serializers are cached and cached results are returned on successive calls. * @param type the type to find a serializer for
* * @return a resolved serializer for the given type
* @param type to find a serializer for. * @throws RuntimeException if no serializer supporting the given type can be found
* @return resolved serializer for the given type.
* @throws RuntimeException if no serializer supporting the given type can be found.
*/ */
private KSerializer<Object> resolve(Type type) { private KSerializer<Object> serializer(Type type) {
KSerializer<Object> serializer = serializerCache.get(type); KSerializer<Object> serializer = serializerCache.get(type);
if (serializer == null) { if (serializer == null) {
serializer = SerializersKt.serializer(type); serializer = SerializersKt.serializer(type);
@ -151,4 +161,5 @@ public class KotlinSerializationJsonHttpMessageConverter extends AbstractGeneric
} }
return serializer; return serializer;
} }
} }