Skip to content

Commit

Permalink
Merge pull request #3411 from istvankovacs-priv/CBOR-support-in-jacks…
Browse files Browse the repository at this point in the history
…on-converter-factory

Binary support implemented in JacksonConverterFactory
  • Loading branch information
JakeWharton committed Feb 21, 2024
2 parents 0e45300 + 48b98a9 commit 921de4a
Show file tree
Hide file tree
Showing 6 changed files with 109 additions and 11 deletions.
4 changes: 3 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ robovm = "2.3.14"
kotlinx-serialization = "1.6.3"
autoService = "1.1.1"
incap = "1.0.0"
jackson = "2.16.1"

[libraries]
androidPlugin = { module = "com.android.tools.build:gradle", version = "8.2.2" }
Expand Down Expand Up @@ -67,7 +68,8 @@ rxjava3 = { module = "io.reactivex.rxjava3:rxjava", version = "3.1.8" }
reactiveStreams = { module = "org.reactivestreams:reactive-streams", version = "1.0.4" }
scalaLibrary = { module = "org.scala-lang:scala-library", version = "2.13.12" }
gson = { module = "com.google.code.gson:gson", version = "2.10.1" }
jacksonDatabind = { module = "com.fasterxml.jackson.core:jackson-databind", version = "2.16.1" }
jacksonDatabind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson" }
jacksonDataformatCbor = { module = "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor", version.ref = "jackson" }
jaxbApi = { module = "javax.xml.bind:jaxb-api", version = "2.3.1" }
jaxbImpl = { module = "org.glassfish.jaxb:jaxb-runtime", version = "4.0.4" }
jaxb3Api = { module = "jakarta.xml.bind:jakarta.xml.bind-api", version = "3.0.1" }
Expand Down
1 change: 1 addition & 0 deletions retrofit-converters/jackson/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ dependencies {
testImplementation libs.junit
testImplementation libs.truth
testImplementation libs.mockwebserver
testImplementation libs.jacksonDataformatCbor
}

jar {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.fasterxml.jackson.databind.ObjectWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Converter;
Expand All @@ -35,22 +36,33 @@
* instance} last to allow the other converters a chance to see their types.
*/
public final class JacksonConverterFactory extends Converter.Factory {
private static final MediaType DEFAULT_MEDIA_TYPE =
MediaType.get("application/json; charset=UTF-8");

/** Create an instance using a default {@link ObjectMapper} instance for conversion. */
public static JacksonConverterFactory create() {
return create(new ObjectMapper());
return new JacksonConverterFactory(new ObjectMapper(), DEFAULT_MEDIA_TYPE);
}

/** Create an instance using {@code mapper} for conversion. */
@SuppressWarnings("ConstantConditions") // Guarding public API nullability.
public static JacksonConverterFactory create(ObjectMapper mapper) {
return create(mapper, DEFAULT_MEDIA_TYPE);
}

/** Create an instance using {@code mapper} and {@code mediaType} for conversion. */
@SuppressWarnings("ConstantConditions") // Guarding public API nullability.
public static JacksonConverterFactory create(ObjectMapper mapper, MediaType mediaType) {
if (mapper == null) throw new NullPointerException("mapper == null");
return new JacksonConverterFactory(mapper);
if (mediaType == null) throw new NullPointerException("mediaType == null");
return new JacksonConverterFactory(mapper, mediaType);
}

private final ObjectMapper mapper;
private final MediaType mediaType;

private JacksonConverterFactory(ObjectMapper mapper) {
private JacksonConverterFactory(ObjectMapper mapper, MediaType mediaType) {
this.mapper = mapper;
this.mediaType = mediaType;
}

@Override
Expand All @@ -69,6 +81,6 @@ public Converter<?, RequestBody> requestBodyConverter(
Retrofit retrofit) {
JavaType javaType = mapper.getTypeFactory().constructType(type);
ObjectWriter writer = mapper.writerFor(javaType);
return new JacksonRequestBodyConverter<>(writer);
return new JacksonRequestBodyConverter<>(writer, mediaType);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,17 @@
import retrofit2.Converter;

final class JacksonRequestBodyConverter<T> implements Converter<T, RequestBody> {
private static final MediaType MEDIA_TYPE = MediaType.get("application/json; charset=UTF-8");

private final ObjectWriter adapter;
private final MediaType mediaType;

JacksonRequestBodyConverter(ObjectWriter adapter) {
JacksonRequestBodyConverter(ObjectWriter adapter, MediaType mediaType) {
this.adapter = adapter;
this.mediaType = mediaType;
}

@Override
public RequestBody convert(T value) throws IOException {
byte[] bytes = adapter.writeValueAsBytes(value);
return RequestBody.create(MEDIA_TYPE, bytes);
return RequestBody.create(mediaType, bytes);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ final class JacksonResponseBodyConverter<T> implements Converter<ResponseBody, T
@Override
public T convert(ResponseBody value) throws IOException {
try {
return adapter.readValue(value.charStream());
return adapter.readValue(value.byteStream());
} finally {
value.close();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright (C) 2024 Square, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package retrofit2.converter.jackson;

import static com.google.common.truth.Truth.assertThat;

import com.fasterxml.jackson.dataformat.cbor.databind.CBORMapper;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import okio.Buffer;
import okio.ByteString;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import retrofit2.Call;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.http.Body;
import retrofit2.http.POST;

public class JacksonCborConverterFactoryTest {
static class IntWrapper {
public int value;

public IntWrapper(int v) {
value = v;
}

protected IntWrapper() {}
}

interface Service {
@POST("/")
Call<IntWrapper> post(@Body IntWrapper person);
}

@Rule public final MockWebServer server = new MockWebServer();

private Service service;

@Before
public void setUp() {
Retrofit retrofit =
new Retrofit.Builder()
.baseUrl(server.url("/"))
.addConverterFactory(
JacksonConverterFactory.create(new CBORMapper(), MediaType.get("application/cbor")))
.build();
service = retrofit.create(Service.class);
}

@Test
public void post() throws IOException, InterruptedException {
server.enqueue(
new MockResponse()
.setBody(new Buffer().write(ByteString.decodeHex("bf6576616c7565182aff"))));

Call<IntWrapper> call = service.post(new IntWrapper(12));
Response<IntWrapper> response = call.execute();
assertThat(response.body().value).isEqualTo(42);

RecordedRequest request = server.takeRequest();
assertThat(request.getBody().readByteString())
.isEqualTo(ByteString.decodeHex("bf6576616c75650cff"));
assertThat(request.getHeader("Content-Type")).isEqualTo("application/cbor");
}
}

0 comments on commit 921de4a

Please sign in to comment.