Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions jackson3/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Jackson 3 Codec
===================

This module adds support for encoding and decoding JSON via Jackson 3.

**Note:** Jackson 3 requires Java 17 or higher.

Add `Jackson3Encoder` and/or `Jackson3Decoder` to your `Feign.Builder` like so:

```java
GitHub github = Feign.builder()
.encoder(new Jackson3Encoder())
.decoder(new Jackson3Decoder())
.target(GitHub.class, "https://api.github.com");
```

If you want to customize the `JsonMapper` that is used, provide it to the `Jackson3Encoder` and `Jackson3Decoder`:

```java
JsonMapper mapper = JsonMapper.builder()
.changeDefaultPropertyInclusion(incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL))
.enable(SerializationFeature.INDENT_OUTPUT)
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.build();

GitHub github = Feign.builder()
.encoder(new Jackson3Encoder(mapper))
.decoder(new Jackson3Decoder(mapper))
.target(GitHub.class, "https://api.github.com");
```

## Migration from Jackson 2 to Jackson 3

The main differences are:

- Package changes: `com.fasterxml.jackson` → `tools.jackson` (except for `com.fasterxml.jackson.annotation`)
- GroupId changes: `com.fasterxml.jackson.core` → `tools.jackson.core`
- `ObjectMapper` is immutable and must be configured via `JsonMapper.builder()`
- Java 17 minimum requirement
55 changes: 55 additions & 0 deletions jackson3/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--

Copyright © 2012 The Feign Authors (feign@commonhaus.dev)

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.

-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-parent</artifactId>
<version>13.7-SNAPSHOT</version>
</parent>

<artifactId>feign-jackson3</artifactId>
<name>Feign Jackson 3</name>
<description>Feign Jackson 3</description>

<properties>
<!-- Jackson 3 requires Java 17 -->
<main.java.version>17</main.java.version>
</properties>

<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>feign-core</artifactId>
</dependency>

<dependency>
<groupId>tools.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>

<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>feign-core</artifactId>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies>
</project>
74 changes: 74 additions & 0 deletions jackson3/src/main/java/feign/jackson3/Jackson3Decoder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright © 2012 The Feign Authors (feign@commonhaus.dev)
*
* 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 feign.jackson3;

import feign.Response;
import feign.Util;
import feign.codec.Decoder;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Type;
import java.util.Collections;
import tools.jackson.core.JacksonException;
import tools.jackson.databind.DeserializationFeature;
import tools.jackson.databind.JacksonModule;
import tools.jackson.databind.json.JsonMapper;

public class Jackson3Decoder implements Decoder {

private final JsonMapper mapper;

public Jackson3Decoder() {
this(Collections.<JacksonModule>emptyList());
}

public Jackson3Decoder(Iterable<JacksonModule> modules) {
this(
JsonMapper.builder()
.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.addModules(modules)
.build());
}

public Jackson3Decoder(JsonMapper mapper) {
this.mapper = mapper;
}

@Override
public Object decode(Response response, Type type) throws IOException {
if (response.status() == 404 || response.status() == 204) return Util.emptyValueOf(type);
if (response.body() == null) return null;
Reader reader = response.body().asReader(response.charset());
if (!reader.markSupported()) {
reader = new BufferedReader(reader, 1);
}
try {
// Read the first byte to see if we have any data
reader.mark(1);
if (reader.read() == -1) {
return null; // Eagerly returning null avoids "No content to map due to end-of-input"
}
reader.reset();
return mapper.readValue(reader, mapper.constructType(type));
} catch (JacksonException e) {
if (e.getCause() != null && e.getCause() instanceof IOException) {
throw IOException.class.cast(e.getCause());
}
throw e;
}
}
}
62 changes: 62 additions & 0 deletions jackson3/src/main/java/feign/jackson3/Jackson3Encoder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright © 2012 The Feign Authors (feign@commonhaus.dev)
*
* 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 feign.jackson3;

import com.fasterxml.jackson.annotation.JsonInclude;
import feign.RequestTemplate;
import feign.Util;
import feign.codec.EncodeException;
import feign.codec.Encoder;
import java.lang.reflect.Type;
import java.util.Collections;
import tools.jackson.core.JacksonException;
import tools.jackson.databind.JacksonModule;
import tools.jackson.databind.JavaType;
import tools.jackson.databind.SerializationFeature;
import tools.jackson.databind.json.JsonMapper;

public class Jackson3Encoder implements Encoder {

private final JsonMapper mapper;

public Jackson3Encoder() {
this(Collections.<JacksonModule>emptyList());
}

public Jackson3Encoder(Iterable<JacksonModule> modules) {
this(
JsonMapper.builder()
.changeDefaultPropertyInclusion(
incl -> incl.withValueInclusion(JsonInclude.Include.NON_NULL))
.enable(SerializationFeature.INDENT_OUTPUT)
.addModules(modules)
.build());
}

public Jackson3Encoder(JsonMapper mapper) {
this.mapper = mapper;
}

@Override
public void encode(Object object, Type bodyType, RequestTemplate template) {
try {
JavaType javaType = mapper.getTypeFactory().constructType(bodyType);
template.body(mapper.writerFor(javaType).writeValueAsBytes(object), Util.UTF_8);
} catch (JacksonException e) {
throw new EncodeException(e.getMessage(), e);
}
}
}
Loading