我正在使用 Java 构建一个 REST 应用程序,该应用程序需要动态构建 GraphQL 有效负载以发送到 GraphQL API。
我们将收到一个
String entityType = "MyEntity"
和一个 Map<String, Object> myMap
,可能如下所示:
{
"blobUrl": "myurl.com",
"accountId": 12345,
"user": {
"id": 4,
"name": "username"
}
}
它可能有许多其他具有不同类型值的键,因此我无法事先创建一个模板来指示需要绑定哪些变量。
我们提前了解 GraphQL 有效负载的所有信息是,它将采用以下格式:
mutation {
Create%s(%s)
}
有了这些信息,我想生成以下 GraphQL 负载:
mutation {
CreateMyEntity(
blobUrl: "myurl.com"
accountId: 12345
user: {
id: 4
name: "username"
}
)
}
我可以通过手动循环
myMap
并使用 StringBuilder
构建有效负载来做到这一点,但我想知道是否有更好的或预先存在的方法可以做到这一点。
我已经查遍了,没有发现任何不涉及明确定义变量及其类型的模板的内容。
我相信您专注于自己构建查询字符串。
这是自定义序列化的示例:
这包含了 main 方法。
package org.example.graphql;
public class Runner {
public static void main(String[] args) throws Exception {
GraphQLSerializer entitySerializer = new GraphQLMutationSerializer("CreateMyEntity");
String payload = entitySerializer.serialize(jsonInput);
System.out.printf("Formatted properly? %b%n", payload.equals(expectedGraphQL));
}
private static final String jsonInput = """
{
"blobUrl": "myurl.com",
"accountId": 12345,
"user": {
"id": 4,
"name": "username"
}
}
""";
private static final String expectedGraphQL = """
mutation {
CreateMyEntity(
blobUrl: "myurl.com"
accountId: 12345
user: {
id: 4
name: "username"
}
)
}
""".trim();
}
这是一个用于序列化的接口。
package org.example.graphql;
import com.fasterxml.jackson.core.JsonProcessingException;
public interface GraphQLSerializer {
String serialize(String jsonInput) throws JsonProcessingException;
}
这里是我们解析 JSON 并格式化它的地方。
package org.example.graphql;
import java.util.Map;
import java.util.stream.Collectors;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class GraphQLMutationSerializer implements GraphQLSerializer {
private final ObjectMapper objectMapper = new ObjectMapper();
private final String mutationName;
private final int indentSize;
public GraphQLMutationSerializer(String mutationName, int indentSize) {
this.mutationName = mutationName;
this.indentSize = indentSize;
}
public GraphQLMutationSerializer(String mutationName) {
this(mutationName, 4);
}
@Override
@SuppressWarnings({ "unchecked" })
public String serialize(String jsonInput) throws JsonProcessingException {
// Parse the input JSON string into a Map using Jackson
Map<String, Object> inputMap = objectMapper.readValue(jsonInput, Map.class);
// Format the body and indentation
String body = buildArguments(inputMap, indentSize, indentSize);
String indentation = indent(indentSize);
return String.format("mutation {\n%s%s(\n%s\n%s)\n}", indentation, mutationName, body, indentation);
}
private static String buildArguments(Map<String, Object> map, int indentSize, int currentIndent) {
return map.entrySet().stream()
.map(entry -> String.format(
"%s%s: %s",
indent(currentIndent + indentSize),
entry.getKey(),
formatValue(entry.getValue(), indentSize, currentIndent + indentSize)))
.collect(Collectors.joining("\n"));
}
private static String formatValue(Object value, int indentSize, int currentIndent) {
if (value instanceof String) {
return "\"" + value + "\"";
} else if (value instanceof Number || value instanceof Boolean) {
return value.toString();
} else if (value instanceof Map) {
return String.format(
"{\n%s\n%s}",
buildArguments((Map<String, Object>) value, indentSize, currentIndent),
indent(currentIndent));
} else {
throw new IllegalArgumentException("Unsupported value type: " + value.getClass());
}
}
private static String indent(int level) {
return " ".repeat(level);
}
}