问题
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 Customer
s
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