diff --git a/spring-web/src/main/java/org/springframework/http/codec/cbor/Jackson2CborDecoder.java b/spring-web/src/main/java/org/springframework/http/codec/cbor/Jackson2CborDecoder.java
new file mode 100644
index 00000000000..8d045c36b5b
--- /dev/null
+++ b/spring-web/src/main/java/org/springframework/http/codec/cbor/Jackson2CborDecoder.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2002-2019 the original author or authors.
+ *
+ * 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
+ *
+ * https://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 org.springframework.http.codec.cbor;
+
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.cbor.CBORFactory;
+import org.reactivestreams.Publisher;
+import reactor.core.publisher.Flux;
+
+import org.springframework.core.ResolvableType;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.http.MediaType;
+import org.springframework.http.codec.json.AbstractJackson2Decoder;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+import org.springframework.util.Assert;
+import org.springframework.util.MimeType;
+
+/**
+ * Decode bytes into CBOR and convert to Object's with Jackson.
+ * Stream decoding is not supported yet.
+ *
+ * @author Sebastien Deleuze
+ * @since 5.2
+ * @see Jackson2CborEncoder
+ * @see Add CBOR support to WebFlux
+ */
+public class Jackson2CborDecoder extends AbstractJackson2Decoder {
+
+ public Jackson2CborDecoder() {
+ this(Jackson2ObjectMapperBuilder.cbor().build(), new MediaType("application", "cbor"));
+ }
+
+ public Jackson2CborDecoder(ObjectMapper mapper, MimeType... mimeTypes) {
+ super(mapper, mimeTypes);
+ Assert.isAssignable(CBORFactory.class, mapper.getFactory().getClass());
+ }
+
+ @Override
+ public Flux decode(Publisher input, ResolvableType elementType, MimeType mimeType, Map hints) {
+ throw new UnsupportedOperationException("Does not support stream decoding yet");
+ }
+}
diff --git a/spring-web/src/main/java/org/springframework/http/codec/cbor/Jackson2CborEncoder.java b/spring-web/src/main/java/org/springframework/http/codec/cbor/Jackson2CborEncoder.java
new file mode 100644
index 00000000000..f5449be6c61
--- /dev/null
+++ b/spring-web/src/main/java/org/springframework/http/codec/cbor/Jackson2CborEncoder.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2002-2019 the original author or authors.
+ *
+ * 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
+ *
+ * https://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 org.springframework.http.codec.cbor;
+
+import java.util.Map;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.cbor.CBORFactory;
+import org.reactivestreams.Publisher;
+import reactor.core.publisher.Flux;
+
+import org.springframework.core.ResolvableType;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.core.io.buffer.DataBufferFactory;
+import org.springframework.http.MediaType;
+import org.springframework.http.codec.json.AbstractJackson2Encoder;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+import org.springframework.util.Assert;
+import org.springframework.util.MimeType;
+
+/**
+ * Encode from an {@code Object} to bytes of CBOR objects using Jackson.
+ * Stream encoding is not supported yet.
+ *
+ * @author Sebastien Deleuze
+ * @since 5.2
+ * @see Jackson2CborDecoder
+ * @see Add CBOR support to WebFlux
+ */
+public class Jackson2CborEncoder extends AbstractJackson2Encoder {
+
+ public Jackson2CborEncoder() {
+ this(Jackson2ObjectMapperBuilder.cbor().build(), new MediaType("application", "cbor"));
+ }
+
+ public Jackson2CborEncoder(ObjectMapper mapper, MimeType... mimeTypes) {
+ super(mapper, mimeTypes);
+ Assert.isAssignable(CBORFactory.class, mapper.getFactory().getClass());
+ }
+
+ @Override
+ public Flux encode(Publisher> inputStream, DataBufferFactory bufferFactory, ResolvableType elementType, MimeType mimeType, Map hints) {
+ throw new UnsupportedOperationException("Does not support stream encoding yet");
+ }
+}
diff --git a/spring-web/src/test/java/org/springframework/http/codec/cbor/Jackson2CborDecoderTests.java b/spring-web/src/test/java/org/springframework/http/codec/cbor/Jackson2CborDecoderTests.java
new file mode 100644
index 00000000000..04350796662
--- /dev/null
+++ b/spring-web/src/test/java/org/springframework/http/codec/cbor/Jackson2CborDecoderTests.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2002-2019 the original author or authors.
+ *
+ * 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
+ *
+ * https://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 org.springframework.http.codec.cbor;
+
+import java.util.Arrays;
+import java.util.List;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Test;
+import reactor.core.publisher.Flux;
+
+import org.springframework.core.ResolvableType;
+import org.springframework.core.codec.AbstractDecoderTestCase;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.http.codec.Pojo;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+import org.springframework.util.MimeType;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.springframework.core.ResolvableType.forClass;
+import static org.springframework.http.MediaType.APPLICATION_JSON;
+
+/**
+ * Unit tests for {@link Jackson2CborDecoder}.
+ *
+ * @author Sebastien Deleuze
+ */
+public class Jackson2CborDecoderTests extends AbstractDecoderTestCase {
+
+ private final static MimeType CBOR_MIME_TYPE = new MimeType("application", "cbor");
+
+ private Pojo pojo1 = new Pojo("f1", "b1");
+
+ private Pojo pojo2 = new Pojo("f2", "b2");
+
+ private ObjectMapper mapper = Jackson2ObjectMapperBuilder.cbor().build();
+
+ public Jackson2CborDecoderTests() {
+ super(new Jackson2CborDecoder());
+ }
+
+ @Override
+ @Test
+ public void canDecode() {
+ assertTrue(decoder.canDecode(forClass(Pojo.class), CBOR_MIME_TYPE));
+ assertTrue(decoder.canDecode(forClass(Pojo.class), null));
+
+ assertFalse(decoder.canDecode(forClass(String.class), null));
+ assertFalse(decoder.canDecode(forClass(Pojo.class), APPLICATION_JSON));
+ }
+
+ @Override
+ @Test(expected = UnsupportedOperationException.class)
+ public void decode() {
+ Flux input = Flux.just(this.pojo1, this.pojo2)
+ .map(this::writeObject)
+ .flatMap(this::dataBuffer);
+
+ testDecodeAll(input, Pojo.class, step -> step
+ .expectNext(pojo1)
+ .expectNext(pojo2)
+ .verifyComplete());
+
+ }
+
+ private byte[] writeObject(Object o) {
+ try {
+ return this.mapper.writer().writeValueAsBytes(o);
+ }
+ catch (JsonProcessingException e) {
+ throw new AssertionError(e);
+ }
+
+ }
+
+ @Override
+ public void decodeToMono() {
+ List expected = Arrays.asList(pojo1, pojo2);
+
+ Flux input = Flux.just(expected)
+ .map(this::writeObject)
+ .flatMap(this::dataBuffer);
+
+ ResolvableType elementType = ResolvableType.forClassWithGenerics(List.class, Pojo.class);
+ testDecodeToMono(input, elementType, step -> step
+ .expectNext(expected)
+ .expectComplete()
+ .verify(), null, null);
+ }
+}
diff --git a/spring-web/src/test/java/org/springframework/http/codec/cbor/Jackson2CborEncoderTests.java b/spring-web/src/test/java/org/springframework/http/codec/cbor/Jackson2CborEncoderTests.java
new file mode 100644
index 00000000000..2f9d8190f16
--- /dev/null
+++ b/spring-web/src/test/java/org/springframework/http/codec/cbor/Jackson2CborEncoderTests.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2002-2019 the original author or authors.
+ *
+ * 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
+ *
+ * https://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 org.springframework.http.codec.cbor;
+
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.function.Consumer;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.Test;
+import reactor.core.publisher.Flux;
+
+import org.springframework.core.ResolvableType;
+import org.springframework.core.io.buffer.AbstractLeakCheckingTestCase;
+import org.springframework.core.io.buffer.DataBuffer;
+import org.springframework.core.io.buffer.support.DataBufferTestUtils;
+import org.springframework.http.codec.Pojo;
+import org.springframework.http.codec.ServerSentEvent;
+import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
+import org.springframework.util.MimeType;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.springframework.core.io.buffer.DataBufferUtils.release;
+import static org.springframework.http.MediaType.APPLICATION_XML;
+
+/**
+ * Unit tests for {@link Jackson2CborEncoder}.
+ *
+ * @author Sebastien Deleuze
+ */
+public class Jackson2CborEncoderTests extends AbstractLeakCheckingTestCase {
+
+ private final static MimeType CBOR_MIME_TYPE = new MimeType("application", "cbor");
+
+ private final ObjectMapper mapper = Jackson2ObjectMapperBuilder.cbor().build();
+
+ private final Jackson2CborEncoder encoder = new Jackson2CborEncoder();
+
+ private Consumer pojoConsumer(Pojo expected) {
+ return dataBuffer -> {
+ try {
+ Pojo actual = this.mapper.reader().forType(Pojo.class)
+ .readValue(DataBufferTestUtils.dumpBytes(dataBuffer));
+ assertEquals(expected, actual);
+ release(dataBuffer);
+ }
+ catch (IOException ex) {
+ throw new UncheckedIOException(ex);
+ }
+ };
+ }
+
+ @Test
+ public void canEncode() {
+ ResolvableType pojoType = ResolvableType.forClass(Pojo.class);
+ assertTrue(this.encoder.canEncode(pojoType, CBOR_MIME_TYPE));
+ assertTrue(this.encoder.canEncode(pojoType, null));
+
+ // SPR-15464
+ assertTrue(this.encoder.canEncode(ResolvableType.NONE, null));
+ }
+
+ @Test
+ public void canNotEncode() {
+ assertFalse(this.encoder.canEncode(ResolvableType.forClass(String.class), null));
+ assertFalse(this.encoder.canEncode(ResolvableType.forClass(Pojo.class), APPLICATION_XML));
+
+ ResolvableType sseType = ResolvableType.forClass(ServerSentEvent.class);
+ assertFalse(this.encoder.canEncode(sseType, CBOR_MIME_TYPE));
+ }
+
+ @Test
+ public void encode() {
+ Pojo value = new Pojo("foo", "bar");
+ DataBuffer result = encoder.encodeValue(value, this.bufferFactory, ResolvableType.forClass(Pojo.class), CBOR_MIME_TYPE, null);
+ pojoConsumer(value).accept(result);
+ }
+
+ @Test(expected = UnsupportedOperationException.class)
+ public void encodeStream() {
+ Pojo pojo1 = new Pojo("foo", "bar");
+ Pojo pojo2 = new Pojo("foofoo", "barbar");
+ Pojo pojo3 = new Pojo("foofoofoo", "barbarbar");
+ Flux input = Flux.just(pojo1, pojo2, pojo3);
+ ResolvableType type = ResolvableType.forClass(Pojo.class);
+ encoder.encode(input, this.bufferFactory, type, CBOR_MIME_TYPE, null);
+ }
+}