From b0149b842b9fda8faa38edd8b096ba586f561f25 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Wed, 19 Aug 2026 10:40:50 +0300 Subject: [PATCH] Polishing in Protobuf encoding support See gh-37154 --- .../http/codec/protobuf/ProtobufEncoder.java | 1 + .../codec/protobuf/ProtobufJsonEncoder.java | 17 ++++++++++---- .../protobuf/ProtobufJsonEncoderTests.java | 22 ++++++++++++------- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/http/codec/protobuf/ProtobufEncoder.java b/spring-web/src/main/java/org/springframework/http/codec/protobuf/ProtobufEncoder.java index 6230591ba2a..227c57c5f8b 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/protobuf/ProtobufEncoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/protobuf/ProtobufEncoder.java @@ -54,6 +54,7 @@ import org.springframework.util.MimeType; * * @author Sebastien Deleuze * @since 5.1 + * @see ProtobufHttpMessageWriter * @see ProtobufDecoder */ public class ProtobufEncoder extends ProtobufCodecSupport implements HttpMessageEncoder { diff --git a/spring-web/src/main/java/org/springframework/http/codec/protobuf/ProtobufJsonEncoder.java b/spring-web/src/main/java/org/springframework/http/codec/protobuf/ProtobufJsonEncoder.java index 73ef356bd1a..820b3d08ba1 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/protobuf/ProtobufJsonEncoder.java +++ b/spring-web/src/main/java/org/springframework/http/codec/protobuf/ProtobufJsonEncoder.java @@ -49,6 +49,7 @@ import org.springframework.util.MimeType; * * @author Brian Clozel * @since 6.2 + * @see ProtobufHttpMessageWriter * @see ProtobufJsonDecoder */ public class ProtobufJsonEncoder implements HttpMessageEncoder { @@ -106,7 +107,10 @@ public class ProtobufJsonEncoder implements HttpMessageEncoder { } @Override - public Flux encode(Publisher inputStream, DataBufferFactory bufferFactory, ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map hints) { + public Flux encode( + Publisher inputStream, DataBufferFactory bufferFactory, + ResolvableType elementType, @Nullable MimeType mimeType, @Nullable Map hints) { + if (inputStream instanceof Mono) { return Mono.from(inputStream) .map(value -> encodeValue(value, bufferFactory, elementType, mimeType, hints)) @@ -120,17 +124,21 @@ public class ProtobufJsonEncoder implements HttpMessageEncoder { .map(value -> { byte[] prefix = helper.getPrefix(); byte[] delimiter = helper.getDelimiter(); + DataBuffer delimiterBuffer = bufferFactory.wrap(delimiter); DataBuffer dataBuffer = encodeValue(value, bufferFactory, MESSAGE_TYPE, mimeType, hints); return (prefix.length > 0 ? - bufferFactory.join(List.of(bufferFactory.wrap(prefix), bufferFactory.wrap(delimiter), dataBuffer)) : - bufferFactory.join(List.of(bufferFactory.wrap(delimiter), dataBuffer))); + bufferFactory.join(List.of(bufferFactory.wrap(prefix), delimiterBuffer, dataBuffer)) : + bufferFactory.join(List.of(delimiterBuffer, dataBuffer))); }) .switchIfEmpty(Mono.fromCallable(() -> bufferFactory.wrap(helper.getPrefix()))) .concatWith(Mono.fromCallable(() -> bufferFactory.wrap(helper.getSuffix()))); } @Override - public DataBuffer encodeValue(Message message, DataBufferFactory bufferFactory, ResolvableType valueType, @Nullable MimeType mimeType, @Nullable Map hints) { + public DataBuffer encodeValue( + Message message, DataBufferFactory bufferFactory, ResolvableType valueType, + @Nullable MimeType mimeType, @Nullable Map hints) { + FastByteArrayOutputStream bos = new FastByteArrayOutputStream(); OutputStreamWriter writer = new OutputStreamWriter(bos, StandardCharsets.UTF_8); try { @@ -144,6 +152,7 @@ public class ProtobufJsonEncoder implements HttpMessageEncoder { } } + private static class JsonArrayJoinHelper { private static final byte[] COMMA_SEPARATOR = {','}; diff --git a/spring-web/src/test/java/org/springframework/http/codec/protobuf/ProtobufJsonEncoderTests.java b/spring-web/src/test/java/org/springframework/http/codec/protobuf/ProtobufJsonEncoderTests.java index ed7b6d4224a..477e8ba97d7 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/protobuf/ProtobufJsonEncoderTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/protobuf/ProtobufJsonEncoderTests.java @@ -43,16 +43,22 @@ import static org.springframework.core.ResolvableType.forClass; */ class ProtobufJsonEncoderTests extends AbstractEncoderTests { - private Msg msg1 = - Msg.newBuilder().setFoo("Foo").setBlah(SecondMsg.newBuilder().setBlah(123).build()).build(); + private Msg msg1 = Msg.newBuilder() + .setFoo("Foo") + .setBlah(SecondMsg.newBuilder().setBlah(123).build()) + .build(); + + private Msg msg2 = Msg.newBuilder() + .setFoo("Bar") + .setBlah(SecondMsg.newBuilder().setBlah(456).build()) + .build(); - private Msg msg2 = - Msg.newBuilder().setFoo("Bar").setBlah(SecondMsg.newBuilder().setBlah(456).build()).build(); public ProtobufJsonEncoderTests() { super(new ProtobufJsonEncoder(JsonFormat.printer().omittingInsignificantWhitespace())); } + @Override @Test protected void canEncode() throws Exception { @@ -69,7 +75,7 @@ class ProtobufJsonEncoderTests extends AbstractEncoderTests ResolvableType inputType = forClass(Msg.class); testEncode(input, inputType, MediaType.APPLICATION_JSON, null, step -> step - .assertNext(dataBuffer -> assertBufferEqualsJson(dataBuffer, "{\"foo\":\"Foo\",\"blah\":{\"blah\":123}}")) + .assertNext(buffer -> assertBufferEqualsJson(buffer, "{\"foo\":\"Foo\",\"blah\":{\"blah\":123}}")) .verifyComplete()); testEncodeError(input, inputType, MediaType.APPLICATION_JSON, null); testEncodeCancel(input, inputType, MediaType.APPLICATION_JSON, null); @@ -91,9 +97,9 @@ class ProtobufJsonEncoderTests extends AbstractEncoderTests ResolvableType inputType = forClass(Msg.class); testEncode(input, inputType, MediaType.APPLICATION_JSON, null, step -> step - .assertNext(dataBuffer -> assertBufferEqualsJson(dataBuffer, "[{\"foo\":\"Foo\",\"blah\":{\"blah\":123}}")) - .assertNext(dataBuffer -> assertBufferEqualsJson(dataBuffer, ",{\"foo\":\"Bar\",\"blah\":{\"blah\":456}}")) - .assertNext(dataBuffer -> assertBufferEqualsJson(dataBuffer, "]")) + .assertNext(buffer -> assertBufferEqualsJson(buffer, "[{\"foo\":\"Foo\",\"blah\":{\"blah\":123}}")) + .assertNext(buffer -> assertBufferEqualsJson(buffer, ",{\"foo\":\"Bar\",\"blah\":{\"blah\":456}}")) + .assertNext(buffer -> assertBufferEqualsJson(buffer, "]")) .verifyComplete()); }