Return HashMap<String, Object> from GraphQL-Java

别等时光非礼了梦想. 提交于 2019-11-30 17:01:50

问题


I tried few variant and had no luck to return a map in GraphQL. So I have the following two objects:

public class Customer {

    private String name, age;
    // getters & setters
}

public class Person {

   private String type;
   private Map<String, Customer> customers;
   // getters & setters
}

My schema looks like this:

type Customer {
   name: String!
   age:  String!
}

type Person {
  type: String!
  customers: [Customer!] // Here I tried all combination but had no luck, is there a Map type support for GQL?
}

Can someone please tell me how to achieve this so that GraphQL magically process this or an alternative approach.

Many thanks!


回答1:


Just in case - you can always represent map object as a JSON string (in my case it was helpful).

public class Person {

    private String type;
    private Map<String, Customer> customers;
    // getters & setters
}

Would be

type Person {
  type: String!
  customers: String!
}

After that don't forget to add data fetcher to convert it to the JSON.

public DataFetcher<String> fetchCustomers() {
        return environment -> {
            Person person = environment.getSource();
            try {
                ObjectMapper objectMapper = new ObjectMapper();
                return objectMapper.writeValueAsString(person.getCustomers());
            } catch (JsonProcessingException e) {
                log.error("There was a problem fetching the person!");
                throw new RuntimeException(e);
            }
        };
    }

It'll return:

"person": {
    "type": "2",
    "customers": "{\"VIP\":{\"name\":\"John\",\"age\":\"19\"},\"Platinum VIP\":{\"name\":\"Peter\",\"age\":\"65\"}}"
  }

After that, you can operate with customers as with typical JSON string in your client.




回答2:


As you yourself noted, there's no map type in GraphQL, mostly because maps are basically untyped data (or data with a dynamic structure) and, as such, do not translate well into the static types that GraphQL expects. Still, you have a few options.

1) You could change the value type so it includes the key, and give up on the map and use a list instead. This is the approach you took in your own answer. I won't go into detail here as you've already exemplified it.

2) As long as the key and value Java types are known (and not e.g. Object), you can treat a map as list of key-value pairs. You can create a type to represent the pair:

type Person {
  type: String!
  customers: [CustomerEntry!]
}

type CustomerEntry {
  key: String!
  value: Customer!
}

On the down side, you now have uglier queries:

{
   person {
     type
     customers {
       key
       value {
         name
       }
     }
   }
}

On the up side, you keep type safety and (mostly) the semantics. It is possible to keep nesting this approach to e.g. represent a Map<String, Map<Long, Customer>>.

3) If you ever have a completely unknown type, i.e. Object, the only option is to treat it as a complex scalar. In JavaScript, this approach is known as JSON scalar as it boils down to stuffing an arbitrary JSON structure in and treating it as a scalar. The same approach can be implemented in Java. graphql-java now has a project for extended scalars. Here's their ObjectScalar (aliased as JsonScalar) implementation.

Now, if you want to represent a type such as Map<String, Object>, you can opt to represent it using the key-value pair approach from above, with only the value type being the JSON scalar, or you can represent the entire map as a JSON scalar.

As a matter of fact, you can decide to represent any map (well, any type really, but that's not useful) as a JSON scalar.

type MapEntry {
  key: String!
  value: [ObjectScalar!]
}

scalar ObjectScalar

On the upside, you can now keep any dynamic structure's shape exactly. On the downside, since it is a scalar, it is impossible to make sub-selections, and you're stuck fetching it all, without knowing what's inside in advance.




回答3:


There is no map type in GraphQL (Discussion on GitHub).

An alternative approach would be to have customers as a List of Customers

public class Person {
   private String type;
   private List<Customer> customers;
}

and include the key for the map inside the Customer class

public class Customer {
    private String key; // or another meaningful name
    private String name, age;
}

Schema would mostly remain the same.

type Customer {
   key: String! // or another meaningful name
   name: String!
   age: String!
}

type Person {
  type: String!
  customers: [Customer!]!
}


来源:https://stackoverflow.com/questions/47674558/return-hashmapstring-object-from-graphql-java

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!