Skip to content

Commit 07850fc

Browse files
ashleyfriezevelo
andauthored
Rework the Jackson module as a Jackson Jr alternative (#1409)
Co-authored-by: Marvin Froeder <[email protected]>
1 parent 6003d64 commit 07850fc

File tree

11 files changed

+756
-0
lines changed

11 files changed

+756
-0
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,9 @@ public class Example {
360360
}
361361
```
362362
363+
For the lighter weight Jackson Jr, use `JacksonJrEncoder` and `JacksonJrDecoder` from
364+
the [Jackson Jr Module](./jackson-jr).
365+
363366
### Sax
364367
[SaxDecoder](./sax) allows you to decode XML in a way that is compatible with normal JVM and also Android environments.
365368

jackson-jr/README.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
Jackson Jr Codec
2+
===================
3+
4+
This module adds support for encoding and decoding JSON via Jackson Jr. This
5+
is a significantly smaller and faster version of Jackson, which may be of benefit
6+
in smaller runtime containers.
7+
8+
Add `JacksonJrEncoder` and/or `JacksonJrDecoder` to your `Feign.Builder` like so:
9+
10+
```java
11+
GitHub github = Feign.builder()
12+
.encoder(new JacksonJrEncoder())
13+
.decoder(new JacksonJrDecoder())
14+
.target(GitHub.class, "https://api.github.com");
15+
```
16+
17+
If you want to customize the `JSON` object that is used, provide it to the `JacksonJrEncoder` and `JacksonJrDecoder`:
18+
19+
```java
20+
JSON json = Json.builder()
21+
.register(JacksonAnnotationExtension.builder()
22+
.withVisibility(JsonAutoDetect.Value.defaultVisibility())
23+
.build())
24+
.build();
25+
26+
GitHub github = Feign.builder()
27+
.encoder(new JacksonJrEncoder(json))
28+
.decoder(new JacksonJrDecoder(json))
29+
.target(GitHub.class, "https://api.github.com");
30+
```
31+
32+
Customisation is also possible by passing `JacksonJrExtension` objects
33+
into the constructor of the `JacksonJrEncoder` or `JacksonJrDecoder`:
34+
35+
```java
36+
List<JacksonJrExtension> extensions = singletonList(JacksonAnnotationExtension.builder()
37+
.withVisibility(JsonAutoDetect.Value.defaultVisibility())
38+
.build());
39+
GitHub github = Feign.builder()
40+
.encoder(new JacksonJrEncoder(extensions))
41+
.decoder(new JacksonJrDecoder(extensions))
42+
.target(GitHub.class, "https://api.github.com");
43+
```

jackson-jr/pom.xml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
Copyright 2012-2021 The Feign Authors
5+
6+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
7+
in compliance with the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software distributed under the License
12+
is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
13+
or implied. See the License for the specific language governing permissions and limitations under
14+
the License.
15+
16+
-->
17+
<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">
18+
<modelVersion>4.0.0</modelVersion>
19+
20+
<parent>
21+
<groupId>io.github.openfeign</groupId>
22+
<artifactId>parent</artifactId>
23+
<version>11.3-SNAPSHOT</version>
24+
</parent>
25+
26+
<artifactId>feign-jackson-jr</artifactId>
27+
<name>Feign Jackson Jr</name>
28+
<description>Feign Jackson Jr</description>
29+
30+
<properties>
31+
<main.basedir>${project.basedir}/..</main.basedir>
32+
</properties>
33+
34+
<dependencies>
35+
<dependency>
36+
<groupId>${project.groupId}</groupId>
37+
<artifactId>feign-core</artifactId>
38+
</dependency>
39+
40+
<dependency>
41+
<groupId>com.fasterxml.jackson.jr</groupId>
42+
<artifactId>jackson-jr-objects</artifactId>
43+
</dependency>
44+
45+
<dependency>
46+
<groupId>com.fasterxml.jackson.jr</groupId>
47+
<artifactId>jackson-jr-annotation-support</artifactId>
48+
<scope>test</scope>
49+
</dependency>
50+
51+
<dependency>
52+
<groupId>${project.groupId}</groupId>
53+
<artifactId>feign-core</artifactId>
54+
<type>test-jar</type>
55+
<scope>test</scope>
56+
</dependency>
57+
</dependencies>
58+
</project>
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/**
2+
* Copyright 2012-2021 The Feign Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
package feign.jackson.jr;
15+
16+
import com.fasterxml.jackson.jr.ob.JSON;
17+
import com.fasterxml.jackson.jr.ob.JSONObjectException;
18+
import com.fasterxml.jackson.jr.ob.JacksonJrExtension;
19+
import feign.Response;
20+
import feign.codec.DecodeException;
21+
import feign.codec.Decoder;
22+
import java.io.BufferedReader;
23+
import java.io.IOException;
24+
import java.io.Reader;
25+
import java.lang.reflect.ParameterizedType;
26+
import java.lang.reflect.Type;
27+
import java.util.List;
28+
import java.util.Map;
29+
30+
/**
31+
* A {@link Decoder} that uses Jackson Jr to convert objects to String or byte representation.
32+
*/
33+
public class JacksonJrDecoder extends JacksonJrMapper implements Decoder {
34+
35+
@FunctionalInterface
36+
interface Transformer {
37+
Object apply(JSON mapper, Reader reader) throws IOException;
38+
}
39+
40+
public JacksonJrDecoder() {
41+
super();
42+
}
43+
44+
/**
45+
* Construct with a custom {@link JSON} to use for decoding
46+
*
47+
* @param mapper the mapper to use
48+
*/
49+
public JacksonJrDecoder(JSON mapper) {
50+
super(mapper);
51+
}
52+
53+
/**
54+
* Construct with a series of {@link JacksonJrExtension} objects that are registered into the
55+
* {@link JSON}
56+
*
57+
* @param iterable the source of the extensions
58+
*/
59+
public JacksonJrDecoder(Iterable<JacksonJrExtension> iterable) {
60+
super(iterable);
61+
}
62+
63+
@Override
64+
public Object decode(Response response, Type type) throws IOException {
65+
66+
Transformer transformer = findTransformer(response, type);
67+
68+
if (response.body() == null) {
69+
return null;
70+
}
71+
Reader reader = response.body().asReader(response.charset());
72+
if (!reader.markSupported()) {
73+
reader = new BufferedReader(reader, 1);
74+
}
75+
try {
76+
// Read the first byte to see if we have any data
77+
reader.mark(1);
78+
if (reader.read() == -1) {
79+
return null; // Eagerly returning null avoids "No content to map due to end-of-input"
80+
}
81+
reader.reset();
82+
return transformer.apply(mapper, reader);
83+
} catch (JSONObjectException e) {
84+
if (e.getCause() instanceof IOException) {
85+
throw (IOException) e.getCause();
86+
}
87+
throw e;
88+
}
89+
}
90+
91+
private static Transformer findTransformer(Response response, Type type) {
92+
if (type instanceof Class) {
93+
return (mapper, reader) -> mapper.beanFrom((Class<?>) type, reader);
94+
}
95+
if (type instanceof ParameterizedType) {
96+
Type rawType = ((ParameterizedType) type).getRawType();
97+
Type[] parameterType = ((ParameterizedType) type).getActualTypeArguments();
98+
if (rawType.equals(List.class)) {
99+
return (mapper, reader) -> mapper.listOfFrom((Class<?>) parameterType[0], reader);
100+
}
101+
if (rawType.equals(Map.class)) {
102+
return (mapper, reader) -> mapper.mapOfFrom((Class<?>) parameterType[1], reader);
103+
}
104+
}
105+
throw new DecodeException(500, "Cannot decode type: " + type.getTypeName(), response.request());
106+
}
107+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* Copyright 2012-2021 The Feign Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
package feign.jackson.jr;
15+
16+
import java.io.IOException;
17+
import java.lang.reflect.Type;
18+
import com.fasterxml.jackson.jr.ob.JSON;
19+
import com.fasterxml.jackson.jr.ob.JacksonJrExtension;
20+
import feign.RequestTemplate;
21+
import feign.codec.EncodeException;
22+
import feign.codec.Encoder;
23+
import static java.lang.String.format;
24+
25+
/**
26+
* A {@link Encoder} that uses Jackson Jr to convert objects to String or byte representation.
27+
*/
28+
public class JacksonJrEncoder extends JacksonJrMapper implements Encoder {
29+
30+
public JacksonJrEncoder() {
31+
super();
32+
}
33+
34+
/**
35+
* Construct with a custom {@link JSON} to use for encoding
36+
*
37+
* @param mapper the mapper to use
38+
*/
39+
public JacksonJrEncoder(JSON mapper) {
40+
super(mapper);
41+
}
42+
43+
/**
44+
* Construct with a series of {@link JacksonJrExtension} objects that are registered into the
45+
* {@link JSON}
46+
*
47+
* @param iterable the source of the extensions
48+
*/
49+
public JacksonJrEncoder(Iterable<JacksonJrExtension> iterable) {
50+
super(iterable);
51+
}
52+
53+
@Override
54+
public void encode(Object object, Type bodyType, RequestTemplate template) {
55+
try {
56+
if (bodyType == byte[].class) {
57+
template.body(mapper.asBytes(object), null);
58+
} else {
59+
template.body(mapper.asString(object));
60+
}
61+
} catch (IOException e) {
62+
throw new EncodeException(e.getMessage(), e);
63+
}
64+
}
65+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* Copyright 2012-2021 The Feign Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License
10+
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11+
* or implied. See the License for the specific language governing permissions and limitations under
12+
* the License.
13+
*/
14+
package feign.jackson.jr;
15+
16+
import com.fasterxml.jackson.jr.ob.JSON;
17+
import com.fasterxml.jackson.jr.ob.JacksonJrExtension;
18+
19+
/**
20+
* Common implementation of the holder of a {@link JSON}, DRYing out construction.
21+
*/
22+
abstract class JacksonJrMapper {
23+
protected final JSON mapper;
24+
25+
protected JacksonJrMapper() {
26+
this(JSON.std);
27+
}
28+
29+
/**
30+
* Construct with a custom {@link JSON} to use for decoding/encoding
31+
*
32+
* @param mapper the mapper to use
33+
*/
34+
protected JacksonJrMapper(JSON mapper) {
35+
this.mapper = mapper;
36+
}
37+
38+
/**
39+
* Construct with a series of {@link JacksonJrExtension} objects that are registered into the
40+
* {@link JSON}
41+
*
42+
* @param iterable the source of the extensions
43+
*/
44+
protected JacksonJrMapper(Iterable<JacksonJrExtension> iterable) {
45+
JSON.Builder builder = JSON.builder();
46+
iterable.forEach(builder::register);
47+
this.mapper = builder.build();
48+
}
49+
}

0 commit comments

Comments
 (0)