Skip to content

@Client doesn't support java.util.Map return types #634

@benalexau

Description

@benalexau

I have put the following JSON file in this GitHub Gist:

{
  "USD": [
    {
      "symbol": "USD.SGD",
      "ccyPair": "SGD"
    },
    {
      "symbol": "USD.RON",
      "ccyPair": "RON"
    }
  ]
}

Java types as follows were attempted to map this:

@Json
public record TestRecord(String symbol, String ccyPair) {}
@Client
public interface TestClient {

  @Get(
      "benalexau/5b6944b31c8a937dc4fc0268a069bb64/raw/2de03fbff0bf2fd171cc3796d40bc7bca63ceb1d/currency-mapping-example.json")
  Map<String, List<TestRecord>> get();
}

With a test method:

  @Test
  void fails() {
    final HttpClient httpClient =
        HttpClient.builder().baseUrl("https://gist.githubusercontent.com").build();

    TestClient client = httpClient.create(TestClient.class);
    Map<String, List<TestRecord>> result = client.get();
    System.out.println(result);
  }

This yields:

No JsonAdapter for java.util.Map<java.util.List, testing.TestRecord>
Possible Causes: 
1. Missing @Json or @Json.Import annotation.
2. The avaje-jsonb-generator dependency was not available during compilation

The generated TestClientImpl has an erroneous type mapping:

  @Override
  public Map<String,List<TestRecord>> get() {
    return client.request()
      .path("benalexau/5b6944b31c8a937dc4fc0268a069bb64/raw/2de03fbff0bf2fd171cc3796d40bc7bca63ceb1d/currency-mapping-example.json")
      .GET()
      .bean(Types.newParameterizedType(Map.class, List.class, TestRecord.class));
  }

This can be worked around by detecting the invalid type and replacing it:

  @Test
  void success() {
    BodyAdapter fakeAdapter =
        new BodyAdapter() {
          private JsonbBodyAdapter delegate = new JsonbBodyAdapter();

          @Override
          public <T> BodyWriter<T> beanWriter(Class<?> type) {
            return delegate.beanWriter(type);
          }

          @Override
          public <T> BodyReader<T> beanReader(Class<T> type) {
            return delegate.beanReader(type);
          }

          @Override
          public <T> BodyReader<T> beanReader(Type type) {
            if (type.toString().contains("java.util.List, testing.TestRecord")) {
              ParameterizedType adjusted = Types.mapOf(Types.listOf(TestRecord.class));
              return delegate.beanReader(adjusted);
            }
            return delegate.beanReader(type);
          }

          @Override
          public <T> BodyReader<List<T>> listReader(Class<T> type) {
            return delegate.listReader(type);
          }
        };
    final HttpClient httpClient =
        HttpClient.builder()
            .baseUrl("https://gist.githubusercontent.com")
            .bodyAdapter(fakeAdapter)
            .build();

    TestClient client = httpClient.create(TestClient.class);
    Map<String, List<TestRecord>> result = client.get();
    System.out.println(result);
  }

Which gives the expected result:

{USD=[TestRecord[symbol=USD.SGD, ccyPair=SGD], TestRecord[symbol=USD.RON, ccyPair=RON]]}

It would be preferable if the generator would support Map.

I will attach the 3 test files to make it easier to reproduce.

MappingTest.java

TestClient.java

TestRecord.java

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions