Browse Source

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.
pull/80/head
David M. Carr 12 years ago committed by adriancole
parent
commit
fe5e8d150b
  1. 4
      CHANGES.md
  2. 14
      README.md
  3. 11
      gson/README.md
  4. 54
      gson/src/main/java/feign/gson/DoubleToIntMapTypeAdapter.java
  5. 31
      gson/src/main/java/feign/gson/GsonCodec.java
  6. 56
      gson/src/main/java/feign/gson/GsonDecoder.java
  7. 36
      gson/src/main/java/feign/gson/GsonEncoder.java
  8. 43
      gson/src/main/java/feign/gson/GsonModule.java
  9. 4
      gson/src/test/java/feign/gson/GsonModuleTest.java
  10. 4
      gson/src/test/java/feign/gson/examples/GitHubExample.java

4
CHANGES.md

@ -1,3 +1,7 @@ @@ -1,3 +1,7 @@
### Version 5.3.0
* Split `GsonCodec` into `GsonEncoder` and `GsonDecoder`, which are easy to use with `Feign.Builder`
* Deprecate `GsonCodec`
### Version 5.2.0
* Support usage of `GsonCodec` via `Feign.Builder`

14
README.md

@ -26,7 +26,7 @@ static class Contributor { @@ -26,7 +26,7 @@ static class Contributor {
public static void main(String... args) {
GitHub github = Feign.builder()
.decoder(new GsonCodec())
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
// 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 @@ -83,13 +83,13 @@ Feign intends to work well within Netflix and other Open Source communities. Mo
### Gson
[GsonModule](https://github.com/Netflix/feign/tree/master/gson) adds default encoders and decoders so you get get started with a JSON api.
Add `GsonCodec` to your `Feign.Builder` like so:
Add `GsonEncoder` and/or `GsonDecoder` to your `Feign.Builder` like so:
```java
GsonCodec codec = new GsonCodec();
GitHub github = Feign.builder()
.encoder(codec)
.decoder(codec)
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
```
@ -126,13 +126,13 @@ MyService api = Feign.create(MyService.class, "https://myAppProd", new RibbonMod @@ -126,13 +126,13 @@ MyService api = Feign.create(MyService.class, "https://myAppProd", new RibbonMod
### Decoders
`Feign.builder()` allows you to specify additional configuration such as how to decode a response.
If any methods in your interface return types besides `Response` or `void`, you'll need to configure a `Decoder`.
If any methods in your interface return types besides `Response`, `String` or `void`, you'll need to configure a `Decoder`.
Here's how to configure json decoding (using the `feign-gson` extension):
```java
GitHub github = Feign.builder()
.decoder(new GsonCodec())
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
```
@ -162,7 +162,7 @@ Where possible, Feign configuration uses normal Dagger conventions. For example @@ -162,7 +162,7 @@ Where possible, Feign configuration uses normal Dagger conventions. For example
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:
```java
GitHub github = Feign.builder()
.decoder(new GsonCodec())
.decoder(new GsonDecoder())
.logger(new Logger.JavaLogger().appendToFile("logs/http.log"))
.logLevel(Logger.Level.FULL)
.target(GitHub.class, "https://api.github.com");

11
gson/README.md

@ -1,19 +1,18 @@ @@ -1,19 +1,18 @@
Gson Codec
===================
This module adds support for encoding and decoding json via the Gson library.
This module adds support for encoding and decoding JSON via the Gson library.
Add `GsonCodec` to your `Feign.Builder` like so:
Add `GsonEncoder` and/or `GsonDecoder` to your `Feign.Builder` like so:
```java
GsonCodec codec = new GsonCodec();
GitHub github = Feign.builder()
.encoder(codec)
.decoder(codec)
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
```
Or.. to your object graph like so:
Or add them to your Dagger object graph like so:
```java
GitHub github = Feign.create(GitHub.class, "https://api.github.com", new GsonModule());

54
gson/src/main/java/feign/gson/DoubleToIntMapTypeAdapter.java

@ -0,0 +1,54 @@ @@ -0,0 +1,54 @@
/*
* Copyright 2013 Netflix, 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 feign.gson;
import com.google.gson.Gson;
import com.google.gson.InstanceCreator;
import com.google.gson.TypeAdapter;
import com.google.gson.internal.ConstructorConstructor;
import com.google.gson.internal.bind.MapTypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.Map;
/**
* Deals with scenario where Gson Object type treats all numbers as doubles.
*/
public class DoubleToIntMapTypeAdapter extends TypeAdapter<Map<String, Object>> {
final static TypeToken<Map<String, Object>> token = new TypeToken<Map<String, Object>>() {};
private final TypeAdapter<Map<String, Object>> delegate = new MapTypeAdapterFactory(new ConstructorConstructor(
Collections.<Type, InstanceCreator<?>>emptyMap()), false).create(new Gson(), token);
@Override public void write(JsonWriter out, Map<String, Object> value) throws IOException {
delegate.write(out, value);
}
@Override public Map<String, Object> read(JsonReader in) throws IOException {
Map<String, Object> map = delegate.read(in);
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (entry.getValue() instanceof Double) {
entry.setValue(Double.class.cast(entry.getValue()).intValue());
}
}
return map;
}
}

31
gson/src/main/java/feign/gson/GsonCodec.java

@ -1,7 +1,6 @@ @@ -1,7 +1,6 @@
package feign.gson;
import com.google.gson.Gson;
import com.google.gson.JsonIOException;
import feign.RequestTemplate;
import feign.Response;
import feign.codec.Decoder;
@ -9,40 +8,30 @@ import feign.codec.Encoder; @@ -9,40 +8,30 @@ import feign.codec.Encoder;
import javax.inject.Inject;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Type;
import static feign.Util.ensureClosed;
/**
* @deprecated use {@link GsonEncoder} and {@link GsonDecoder} instead
*/
@Deprecated
public class GsonCodec implements Encoder, Decoder {
private final Gson gson;
private final GsonEncoder encoder;
private final GsonDecoder decoder;
public GsonCodec() {
this(new Gson());
}
@Inject public GsonCodec(Gson gson) {
this.gson = gson;
this.encoder = new GsonEncoder(gson);
this.decoder = new GsonDecoder(gson);
}
@Override public void encode(Object object, RequestTemplate template) {
template.body(gson.toJson(object));
encoder.encode(object, template);
}
@Override public Object decode(Response response, Type type) throws IOException {
if (response.body() == null) {
return null;
}
Reader reader = response.body().asReader();
try {
return gson.fromJson(reader, type);
} catch (JsonIOException e) {
if (e.getCause() != null && e.getCause() instanceof IOException) {
throw IOException.class.cast(e.getCause());
}
throw e;
} finally {
ensureClosed(reader);
}
return decoder.decode(response, type);
}
}

56
gson/src/main/java/feign/gson/GsonDecoder.java

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
/*
* Copyright 2013 Netflix, 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 feign.gson;
import com.google.gson.Gson;
import com.google.gson.JsonIOException;
import feign.Response;
import feign.codec.Decoder;
import java.io.IOException;
import java.io.Reader;
import java.lang.reflect.Type;
import static feign.Util.ensureClosed;
public class GsonDecoder implements Decoder {
private final Gson gson;
public GsonDecoder() {
this(new Gson());
}
public GsonDecoder(Gson gson) {
this.gson = gson;
}
@Override public Object decode(Response response, Type type) throws IOException {
if (response.body() == null) {
return null;
}
Reader reader = response.body().asReader();
try {
return gson.fromJson(reader, type);
} catch (JsonIOException e) {
if (e.getCause() != null && e.getCause() instanceof IOException) {
throw IOException.class.cast(e.getCause());
}
throw e;
} finally {
ensureClosed(reader);
}
}
}

36
gson/src/main/java/feign/gson/GsonEncoder.java

@ -0,0 +1,36 @@ @@ -0,0 +1,36 @@
/*
* Copyright 2013 Netflix, 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 feign.gson;
import com.google.gson.Gson;
import feign.RequestTemplate;
import feign.codec.Encoder;
public class GsonEncoder implements Encoder {
private final Gson gson;
public GsonEncoder() {
this(new Gson());
}
public GsonEncoder(Gson gson) {
this.gson = gson;
}
@Override public void encode(Object object, RequestTemplate template) {
template.body(gson.toJson(object));
}
}

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

@ -17,23 +17,15 @@ package feign.gson; @@ -17,23 +17,15 @@ package feign.gson;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.InstanceCreator;
import com.google.gson.TypeAdapter;
import com.google.gson.internal.ConstructorConstructor;
import com.google.gson.internal.bind.MapTypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import dagger.Provides;
import feign.Feign;
import feign.codec.Decoder;
import feign.codec.Encoder;
import javax.inject.Singleton;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import static feign.Util.resolveLastTypeParameter;
@ -77,12 +69,12 @@ import static feign.Util.resolveLastTypeParameter; @@ -77,12 +69,12 @@ import static feign.Util.resolveLastTypeParameter;
@dagger.Module(injects = Feign.class, addsTo = Feign.Defaults.class)
public final class GsonModule {
@Provides Encoder encoder(GsonCodec codec) {
return codec;
@Provides Encoder encoder(Gson gson) {
return new GsonEncoder(gson);
}
@Provides Decoder decoder(GsonCodec codec) {
return codec;
@Provides Decoder decoder(Gson gson) {
return new GsonDecoder(gson);
}
@Provides @Singleton Gson gson(Set<TypeAdapter> adapters) {
@ -94,30 +86,7 @@ public final class GsonModule { @@ -94,30 +86,7 @@ public final class GsonModule {
return builder.create();
}
// deals with scenario where gson Object type treats all numbers as doubles.
@Provides(type = Provides.Type.SET) TypeAdapter doubleToInt() {
return new TypeAdapter<Map<String, Object>>() {
TypeAdapter<Map<String, Object>> delegate = new MapTypeAdapterFactory(new ConstructorConstructor(
Collections.<Type, InstanceCreator<?>>emptyMap()), false).create(new Gson(), token);
@Override
public void write(JsonWriter out, Map<String, Object> value) throws IOException {
delegate.write(out, value);
}
@Override
public Map<String, Object> read(JsonReader in) throws IOException {
Map<String, Object> map = delegate.read(in);
for (Map.Entry<String, Object> entry : map.entrySet()) {
if (entry.getValue() instanceof Double) {
entry.setValue(Double.class.cast(entry.getValue()).intValue());
}
@Provides(type = Provides.Type.SET_VALUES) Set<TypeAdapter> noDefaultTypeAdapters() {
return Collections.emptySet();
}
return map;
}
}.nullSafe();
}
private final static TypeToken<Map<String, Object>> token = new TypeToken<Map<String, Object>>() {
};
}

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

@ -52,8 +52,8 @@ public class GsonModuleTest { @@ -52,8 +52,8 @@ public class GsonModuleTest {
EncoderAndDecoderBindings bindings = new EncoderAndDecoderBindings();
ObjectGraph.create(bindings).inject(bindings);
assertEquals(bindings.encoder.getClass(), GsonCodec.class);
assertEquals(bindings.decoder.getClass(), GsonCodec.class);
assertEquals(bindings.encoder.getClass(), GsonEncoder.class);
assertEquals(bindings.decoder.getClass(), GsonDecoder.class);
}
@Module(includes = GsonModule.class, injects = EncoderBindings.class)

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

@ -17,7 +17,7 @@ package feign.gson.examples; @@ -17,7 +17,7 @@ package feign.gson.examples;
import feign.Feign;
import feign.RequestLine;
import feign.gson.GsonCodec;
import feign.gson.GsonDecoder;
import javax.inject.Named;
import java.util.List;
@ -38,7 +38,7 @@ public class GitHubExample { @@ -38,7 +38,7 @@ public class GitHubExample {
}
public static void main(String... args) throws InterruptedException {
GitHub github = Feign.builder().decoder(new GsonCodec()).target(GitHub.class, "https://api.github.com");
GitHub github = Feign.builder().decoder(new GsonDecoder()).target(GitHub.class, "https://api.github.com");
System.out.println("Let's fetch and print a list of the contributors to this library.");
List<Contributor> contributors = github.contributors("netflix", "feign");

Loading…
Cancel
Save