gpt4 book ai didi

java - 使用 gson 为 WebClient 设置自定义编码器/解码器或 typeAdapter

转载 作者:行者123 更新时间:2023-12-01 17:46:46 25 4
gpt4 key购买 nike

我很难找到与此特定场景相关的任何内容

我配置了 Spring Boot,并且在其中使用响应式(Reactive) WebClient 来使用 REST Api。我已将其配置为使用 gson,但想知道如何为更复杂的对象添加自定义 TypeAdapters

我发现的只是对 WebClient.Builder.codecs() 的引用,它似乎只使用使用 ObjectMapper 的 Jackson 转换器。

这根本不可能吗?

最佳答案

这似乎是对我有用的方法。它主要基于适用于 Gson 的 Jackson 代码。这绝不是优化的,可能有一些遗漏的极端情况,但它应该处理基本的 json 解析

帮助类:

class GsonEncoding {
static final List<MimeType> mimeTypes = Stream.of(new MimeType("application", "json"),
new MimeType("application", "*+json"))
.collect(Collectors.toUnmodifiableList());

static final byte[] NEWLINE_SEPARATOR = {'\n'};
static final Map<MediaType, byte[]> STREAM_SEPARATORS;

static {
STREAM_SEPARATORS = new HashMap<>();
STREAM_SEPARATORS.put(MediaType.APPLICATION_STREAM_JSON, NEWLINE_SEPARATOR);
STREAM_SEPARATORS.put(MediaType.parseMediaType("application/stream+x-jackson-smile"), new byte[0]);
}

static void logValue(final Logger log, @Nullable Map<String, Object> hints, Object value) {
if (!Hints.isLoggingSuppressed(hints)) {
if (log.isLoggable(Level.FINE)) {
boolean traceEnabled = log.isLoggable(Level.FINEST);
String message = Hints.getLogPrefix(hints) + "Encoding [" + LogFormatUtils.formatValue(value, !traceEnabled) + "]";
if (traceEnabled) {
log.log(Level.FINEST, message);
} else {
log.log(Level.FINE, message);
}
}
}
}

static boolean supportsMimeType(@Nullable MimeType mimeType) {
return (mimeType == null || GsonEncoding.mimeTypes.stream().anyMatch(m -> m.isCompatibleWith(mimeType)));
}

static boolean isTypeAdapterAvailable(Gson gson, Class<?> clazz) {
try {
gson.getAdapter(clazz);
return true;
} catch(final IllegalArgumentException e) {
return false;
}
}

}

编码器:

@Log
@RequiredArgsConstructor
@Component
public class GsonEncoder implements HttpMessageEncoder<Object> {

private final Gson gson;

@Override
public List<MediaType> getStreamingMediaTypes() {
return Collections.singletonList(MediaType.APPLICATION_STREAM_JSON);
}

@Override
public boolean canEncode(final ResolvableType elementType, final MimeType mimeType) {
Class<?> clazz = elementType.toClass();
if (!GsonEncoding.supportsMimeType(mimeType)) {
return false;
}
if (Object.class == clazz) {
return true;
}
if (!String.class.isAssignableFrom(elementType.resolve(clazz))) {
return GsonEncoding.isTypeAdapterAvailable(gson, clazz);
}
return false;
}

@Override
public Flux<DataBuffer> encode(final Publisher<?> inputStream, final DataBufferFactory bufferFactory, final ResolvableType elementType, final MimeType mimeType, final Map<String, Object> hints) {
Assert.notNull(inputStream, "'inputStream' must not be null");
Assert.notNull(bufferFactory, "'bufferFactory' must not be null");
Assert.notNull(elementType, "'elementType' must not be null");

if (inputStream instanceof Mono) {
return Mono.from(inputStream)
.map(value -> encodeValue(value, bufferFactory, elementType, mimeType, hints))
.flux();
} else {
byte[] separator = streamSeparator(mimeType);
if (separator != null) { // streaming
try {
return Flux.from(inputStream)
.map(value -> encodeStreamingValue(value, bufferFactory, hints, separator));
} catch (Exception ex) {
return Flux.error(ex);
}
} else { // non-streaming
ResolvableType listType = ResolvableType.forClassWithGenerics(List.class, elementType);
return Flux.from(inputStream)
.collectList()
.map(list -> encodeValue(list, bufferFactory, listType, mimeType, hints))
.flux();
}

}
}

@Nullable
private byte[] streamSeparator(@Nullable MimeType mimeType) {
for (MediaType streamingMediaType : this.getStreamingMediaTypes()) {
if (streamingMediaType.isCompatibleWith(mimeType)) {
return GsonEncoding.STREAM_SEPARATORS.getOrDefault(streamingMediaType, GsonEncoding.NEWLINE_SEPARATOR);
}
}
return null;
}


@Override
public List<MimeType> getEncodableMimeTypes() {
return GsonEncoding.mimeTypes;
}


@Override
public DataBuffer encodeValue(Object value, DataBufferFactory bufferFactory, ResolvableType valueType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
GsonEncoding.logValue(log, hints, value);
byte[] bytes = gson.toJson(value).getBytes();
DataBuffer buffer = bufferFactory.allocateBuffer(bytes.length);
buffer.write(bytes);
return buffer;
}


private DataBuffer encodeStreamingValue(Object value, DataBufferFactory bufferFactory, @Nullable Map<String, Object> hints, byte[] separator) {
GsonEncoding.logValue(log, hints, value);
byte[] bytes = gson.toJson(value).getBytes();
int offset;
int length;
offset = 0;
length = bytes.length;
DataBuffer buffer = bufferFactory.allocateBuffer(length + separator.length);
buffer.write(bytes, offset, length);
buffer.write(separator);
return buffer;
}

}

解码器:

@Log
@RequiredArgsConstructor
@Component
public class GsonDecoder implements HttpMessageDecoder<Object> {

private static final int MAX_IN_MEMORY_SIZE = 2000 * 1000000;

private final Gson gson;

@Override
public Map<String, Object> getDecodeHints(final ResolvableType resolvableType, final ResolvableType elementType, final ServerHttpRequest request, final ServerHttpResponse response) {
return Hints.none();
}

@Override
public boolean canDecode(final ResolvableType elementType, final MimeType mimeType) {
if (CharSequence.class.isAssignableFrom(elementType.toClass())) {
return false;
}
if (!GsonEncoding.supportsMimeType(mimeType)) {
return false;
}
return GsonEncoding.isTypeAdapterAvailable(gson, elementType.getRawClass());
}

@Override
public Object decode(DataBuffer dataBuffer, ResolvableType targetType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) throws DecodingException {

return decodeInternal(dataBuffer, targetType, hints);
}

private Object decodeInternal(final DataBuffer dataBuffer, final ResolvableType targetType, @Nullable Map<String, Object> hints) {
try {
final Object value = gson.fromJson(new InputStreamReader(dataBuffer.asInputStream()), targetType.getRawClass());
GsonEncoding.logValue(log, hints, value);
return value;
} finally {
DataBufferUtils.release(dataBuffer);
}
}


@Override
public Flux<Object> decode(Publisher<DataBuffer> input, ResolvableType elementType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
return Flux.from(input).map(d -> decodeInternal(d, elementType, hints));
}

@Override
public Mono<Object> decodeToMono(final Publisher<DataBuffer> inputStream, final ResolvableType elementType, final MimeType mimeType, final Map<String, Object> hints) {
return DataBufferUtils.join(inputStream, MAX_IN_MEMORY_SIZE)
.flatMap(dataBuffer -> Mono.justOrEmpty(decode(dataBuffer, elementType, mimeType, hints)));
}

@Override
public List<MimeType> getDecodableMimeTypes() {
return GsonEncoding.mimeTypes;
}
}

应用程序配置:

@Configuration
public class ApplicationConfiguration {

@Bean
public Gson gson(){
final GsonBuilder gsonBuilder = new GsonBuilder();
// for each of your TypeAdapters here call gsonBuilder.registerTypeAdapter()
return gsonBuilder.create();
}
}

以及我的网络客户端的初始化:

@Service
@RequiredArgsConstructor
@Log
public class MyApiClient {


private final GsonEncoder encoder;
private final GsonDecoder decoder;

private static final int CONNECTION_TIMEOUT = 5000;

@PostConstruct
public void init() {
client = WebClient.builder()
.baseUrl("http://myresource.com")
.clientConnector(new ReactorClientHttpConnector(HttpClient.from(TcpClient.create()
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, CONNECTION_TIMEOUT)
.doOnConnected(connection -> {
connection.addHandlerLast(new ReadTimeoutHandler(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS));
connection.addHandlerLast(new WriteTimeoutHandler(CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS));
})
)))
.defaultHeaders(h -> h.setBasicAuth(username, password))
.defaultHeader(HttpHeaders.CONTENT_TYPE, "application/json")
.defaultHeader(HttpHeaders.ACCEPT, "application/json")
.defaultHeader(HttpHeaders.ACCEPT_CHARSET, "UTF-8")
.codecs(clientCodecConfigurer -> {
clientCodecConfigurer.customCodecs().register(encoder);
clientCodecConfigurer.customCodecs().register(decoder);
})
.build();
}
}

关于java - 使用 gson 为 WebClient 设置自定义编码器/解码器或 typeAdapter,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60854660/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com