Skip to content

Commit fe5e8d1

Browse files
davidmc24adriancole
authored andcommitted
Simplify usage of Gson from Feign.Builder
The logic in GsonCodec was split into GsonEncoder and GsonDecoder, each of which can now be used separately. GsonCodec was deprecated, and can be removed in the next major version. To facilitate use outside of Dagger, the double-to-int map type adapter was broken into its own class, and is included by default when using the default constructors of either the encoder or decoder. The examples have been updated to use the new encoder/decoder instead of the codec.
1 parent 2615cf2 commit fe5e8d1

File tree

10 files changed

+182
-75
lines changed

10 files changed

+182
-75
lines changed

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
### Version 5.3.0
2+
* Split `GsonCodec` into `GsonEncoder` and `GsonDecoder`, which are easy to use with `Feign.Builder`
3+
* Deprecate `GsonCodec`
4+
15
### Version 5.2.0
26
* Support usage of `GsonCodec` via `Feign.Builder`
37

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ static class Contributor {
2626

2727
public static void main(String... args) {
2828
GitHub github = Feign.builder()
29-
.decoder(new GsonCodec())
29+
.decoder(new GsonDecoder())
3030
.target(GitHub.class, "https://api.github.com");
3131

3232
// Fetch and print a list of the contributors to this library.
@@ -83,13 +83,13 @@ Feign intends to work well within Netflix and other Open Source communities. Mo
8383
### Gson
8484
[GsonModule](https://github.com/Netflix/feign/tree/master/gson) adds default encoders and decoders so you get get started with a JSON api.
8585

86-
Add `GsonCodec` to your `Feign.Builder` like so:
86+
Add `GsonEncoder` and/or `GsonDecoder` to your `Feign.Builder` like so:
8787

8888
```java
8989
GsonCodec codec = new GsonCodec();
9090
GitHub github = Feign.builder()
91-
.encoder(codec)
92-
.decoder(codec)
91+
.encoder(new GsonEncoder())
92+
.decoder(new GsonDecoder())
9393
.target(GitHub.class, "https://api.github.com");
9494
```
9595

@@ -126,13 +126,13 @@ MyService api = Feign.create(MyService.class, "https://myAppProd", new RibbonMod
126126
### Decoders
127127
`Feign.builder()` allows you to specify additional configuration such as how to decode a response.
128128

129-
If any methods in your interface return types besides `Response` or `void`, you'll need to configure a `Decoder`.
129+
If any methods in your interface return types besides `Response`, `String` or `void`, you'll need to configure a `Decoder`.
130130

131131
Here's how to configure json decoding (using the `feign-gson` extension):
132132

133133
```java
134134
GitHub github = Feign.builder()
135-
.decoder(new GsonCodec())
135+
.decoder(new GsonDecoder())
136136
.target(GitHub.class, "https://api.github.com");
137137
```
138138

@@ -162,7 +162,7 @@ Where possible, Feign configuration uses normal Dagger conventions. For example
162162
You can log the http messages going to and from the target by setting up a `Logger`. Here's the easiest way to do that:
163163
```java
164164
GitHub github = Feign.builder()
165-
.decoder(new GsonCodec())
165+
.decoder(new GsonDecoder())
166166
.logger(new Logger.JavaLogger().appendToFile("logs/http.log"))
167167
.logLevel(Logger.Level.FULL)
168168
.target(GitHub.class, "https://api.github.com");

gson/README.md

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
Gson Codec
22
===================
33

4-
This module adds support for encoding and decoding json via the Gson library.
4+
This module adds support for encoding and decoding JSON via the Gson library.
55

6-
Add `GsonCodec` to your `Feign.Builder` like so:
6+
Add `GsonEncoder` and/or `GsonDecoder` to your `Feign.Builder` like so:
77

88
```java
9-
GsonCodec codec = new GsonCodec();
109
GitHub github = Feign.builder()
11-
.encoder(codec)
12-
.decoder(codec)
10+
.encoder(new GsonEncoder())
11+
.decoder(new GsonDecoder())
1312
.target(GitHub.class, "https://api.github.com");
1413
```
1514

16-
Or.. to your object graph like so:
15+
Or add them to your Dagger object graph like so:
1716

1817
```java
1918
GitHub github = Feign.create(GitHub.class, "https://api.github.com", new GsonModule());
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2013 Netflix, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package feign.gson;
17+
18+
import com.google.gson.Gson;
19+
import com.google.gson.InstanceCreator;
20+
import com.google.gson.TypeAdapter;
21+
import com.google.gson.internal.ConstructorConstructor;
22+
import com.google.gson.internal.bind.MapTypeAdapterFactory;
23+
import com.google.gson.reflect.TypeToken;
24+
import com.google.gson.stream.JsonReader;
25+
import com.google.gson.stream.JsonWriter;
26+
27+
import java.io.IOException;
28+
import java.lang.reflect.Type;
29+
import java.util.Collections;
30+
import java.util.Map;
31+
32+
/**
33+
* Deals with scenario where Gson Object type treats all numbers as doubles.
34+
*/
35+
public class DoubleToIntMapTypeAdapter extends TypeAdapter<Map<String, Object>> {
36+
final static TypeToken<Map<String, Object>> token = new TypeToken<Map<String, Object>>() {};
37+
38+
private final TypeAdapter<Map<String, Object>> delegate = new MapTypeAdapterFactory(new ConstructorConstructor(
39+
Collections.<Type, InstanceCreator<?>>emptyMap()), false).create(new Gson(), token);
40+
41+
@Override public void write(JsonWriter out, Map<String, Object> value) throws IOException {
42+
delegate.write(out, value);
43+
}
44+
45+
@Override public Map<String, Object> read(JsonReader in) throws IOException {
46+
Map<String, Object> map = delegate.read(in);
47+
for (Map.Entry<String, Object> entry : map.entrySet()) {
48+
if (entry.getValue() instanceof Double) {
49+
entry.setValue(Double.class.cast(entry.getValue()).intValue());
50+
}
51+
}
52+
return map;
53+
}
54+
}
Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,37 @@
11
package feign.gson;
22

33
import com.google.gson.Gson;
4-
import com.google.gson.JsonIOException;
54
import feign.RequestTemplate;
65
import feign.Response;
76
import feign.codec.Decoder;
87
import feign.codec.Encoder;
98

109
import javax.inject.Inject;
1110
import java.io.IOException;
12-
import java.io.Reader;
1311
import java.lang.reflect.Type;
1412

15-
import static feign.Util.ensureClosed;
16-
13+
/**
14+
* @deprecated use {@link GsonEncoder} and {@link GsonDecoder} instead
15+
*/
16+
@Deprecated
1717
public class GsonCodec implements Encoder, Decoder {
18-
private final Gson gson;
18+
private final GsonEncoder encoder;
19+
private final GsonDecoder decoder;
1920

2021
public GsonCodec() {
2122
this(new Gson());
2223
}
2324

2425
@Inject public GsonCodec(Gson gson) {
25-
this.gson = gson;
26+
this.encoder = new GsonEncoder(gson);
27+
this.decoder = new GsonDecoder(gson);
2628
}
2729

2830
@Override public void encode(Object object, RequestTemplate template) {
29-
template.body(gson.toJson(object));
31+
encoder.encode(object, template);
3032
}
3133

3234
@Override public Object decode(Response response, Type type) throws IOException {
33-
if (response.body() == null) {
34-
return null;
35-
}
36-
Reader reader = response.body().asReader();
37-
try {
38-
return gson.fromJson(reader, type);
39-
} catch (JsonIOException e) {
40-
if (e.getCause() != null && e.getCause() instanceof IOException) {
41-
throw IOException.class.cast(e.getCause());
42-
}
43-
throw e;
44-
} finally {
45-
ensureClosed(reader);
46-
}
35+
return decoder.decode(response, type);
4736
}
4837
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2013 Netflix, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package feign.gson;
17+
18+
import com.google.gson.Gson;
19+
import com.google.gson.JsonIOException;
20+
import feign.Response;
21+
import feign.codec.Decoder;
22+
23+
import java.io.IOException;
24+
import java.io.Reader;
25+
import java.lang.reflect.Type;
26+
27+
import static feign.Util.ensureClosed;
28+
29+
public class GsonDecoder implements Decoder {
30+
private final Gson gson;
31+
32+
public GsonDecoder() {
33+
this(new Gson());
34+
}
35+
36+
public GsonDecoder(Gson gson) {
37+
this.gson = gson;
38+
}
39+
40+
@Override public Object decode(Response response, Type type) throws IOException {
41+
if (response.body() == null) {
42+
return null;
43+
}
44+
Reader reader = response.body().asReader();
45+
try {
46+
return gson.fromJson(reader, type);
47+
} catch (JsonIOException e) {
48+
if (e.getCause() != null && e.getCause() instanceof IOException) {
49+
throw IOException.class.cast(e.getCause());
50+
}
51+
throw e;
52+
} finally {
53+
ensureClosed(reader);
54+
}
55+
}
56+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2013 Netflix, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package feign.gson;
17+
18+
import com.google.gson.Gson;
19+
import feign.RequestTemplate;
20+
import feign.codec.Encoder;
21+
22+
public class GsonEncoder implements Encoder {
23+
private final Gson gson;
24+
25+
public GsonEncoder() {
26+
this(new Gson());
27+
}
28+
29+
public GsonEncoder(Gson gson) {
30+
this.gson = gson;
31+
}
32+
33+
@Override public void encode(Object object, RequestTemplate template) {
34+
template.body(gson.toJson(object));
35+
}
36+
}

gson/src/main/java/feign/gson/GsonModule.java

Lines changed: 6 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,15 @@
1717

1818
import com.google.gson.Gson;
1919
import com.google.gson.GsonBuilder;
20-
import com.google.gson.InstanceCreator;
2120
import com.google.gson.TypeAdapter;
22-
import com.google.gson.internal.ConstructorConstructor;
23-
import com.google.gson.internal.bind.MapTypeAdapterFactory;
24-
import com.google.gson.reflect.TypeToken;
25-
import com.google.gson.stream.JsonReader;
26-
import com.google.gson.stream.JsonWriter;
2721
import dagger.Provides;
2822
import feign.Feign;
2923
import feign.codec.Decoder;
3024
import feign.codec.Encoder;
3125

3226
import javax.inject.Singleton;
33-
import java.io.IOException;
3427
import java.lang.reflect.Type;
3528
import java.util.Collections;
36-
import java.util.Map;
3729
import java.util.Set;
3830

3931
import static feign.Util.resolveLastTypeParameter;
@@ -77,12 +69,12 @@
7769
@dagger.Module(injects = Feign.class, addsTo = Feign.Defaults.class)
7870
public final class GsonModule {
7971

80-
@Provides Encoder encoder(GsonCodec codec) {
81-
return codec;
72+
@Provides Encoder encoder(Gson gson) {
73+
return new GsonEncoder(gson);
8274
}
8375

84-
@Provides Decoder decoder(GsonCodec codec) {
85-
return codec;
76+
@Provides Decoder decoder(Gson gson) {
77+
return new GsonDecoder(gson);
8678
}
8779

8880
@Provides @Singleton Gson gson(Set<TypeAdapter> adapters) {
@@ -94,30 +86,7 @@ public final class GsonModule {
9486
return builder.create();
9587
}
9688

97-
// deals with scenario where gson Object type treats all numbers as doubles.
98-
@Provides(type = Provides.Type.SET) TypeAdapter doubleToInt() {
99-
return new TypeAdapter<Map<String, Object>>() {
100-
TypeAdapter<Map<String, Object>> delegate = new MapTypeAdapterFactory(new ConstructorConstructor(
101-
Collections.<Type, InstanceCreator<?>>emptyMap()), false).create(new Gson(), token);
102-
103-
@Override
104-
public void write(JsonWriter out, Map<String, Object> value) throws IOException {
105-
delegate.write(out, value);
106-
}
107-
108-
@Override
109-
public Map<String, Object> read(JsonReader in) throws IOException {
110-
Map<String, Object> map = delegate.read(in);
111-
for (Map.Entry<String, Object> entry : map.entrySet()) {
112-
if (entry.getValue() instanceof Double) {
113-
entry.setValue(Double.class.cast(entry.getValue()).intValue());
114-
}
115-
}
116-
return map;
117-
}
118-
}.nullSafe();
89+
@Provides(type = Provides.Type.SET_VALUES) Set<TypeAdapter> noDefaultTypeAdapters() {
90+
return Collections.emptySet();
11991
}
120-
121-
private final static TypeToken<Map<String, Object>> token = new TypeToken<Map<String, Object>>() {
122-
};
12392
}

gson/src/test/java/feign/gson/GsonModuleTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ static class EncoderAndDecoderBindings {
5252
EncoderAndDecoderBindings bindings = new EncoderAndDecoderBindings();
5353
ObjectGraph.create(bindings).inject(bindings);
5454

55-
assertEquals(bindings.encoder.getClass(), GsonCodec.class);
56-
assertEquals(bindings.decoder.getClass(), GsonCodec.class);
55+
assertEquals(bindings.encoder.getClass(), GsonEncoder.class);
56+
assertEquals(bindings.decoder.getClass(), GsonDecoder.class);
5757
}
5858

5959
@Module(includes = GsonModule.class, injects = EncoderBindings.class)

gson/src/test/java/feign/gson/examples/GitHubExample.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
import feign.Feign;
1919
import feign.RequestLine;
20-
import feign.gson.GsonCodec;
20+
import feign.gson.GsonDecoder;
2121

2222
import javax.inject.Named;
2323
import java.util.List;
@@ -38,7 +38,7 @@ static class Contributor {
3838
}
3939

4040
public static void main(String... args) throws InterruptedException {
41-
GitHub github = Feign.builder().decoder(new GsonCodec()).target(GitHub.class, "https://api.github.com");
41+
GitHub github = Feign.builder().decoder(new GsonDecoder()).target(GitHub.class, "https://api.github.com");
4242

4343
System.out.println("Let's fetch and print a list of the contributors to this library.");
4444
List<Contributor> contributors = github.contributors("netflix", "feign");

0 commit comments

Comments
 (0)