我正在使用Spring for GraphQL项目和GraphQL Java Extended Scalars项目来实现GraphQL API,以处理JSON属性,因为我拥有的数据属性是动态的,服务器不知道其结构。
这个JSON属性是突变输入中有效载荷的一部分,POJO看起来像这样:
@Accessors(fluent = true)
@Builder
@Data
@JsonDeserialize(builder = MutationInputModel.MutationInputModelBuilder.class)
public class MutationInputModel {
private JsonNode data;
...
}
如上所述,我使用Lombok注释进行反序列化和访问器生成。data
属性是Jackson的JsonNode
类型。
然而,当调用这种突变时,我得到了以下异常:
org.springframework.beans.BeanInstanceException:无法实例化[com.fasterxml.jjackson.databind.JsonNode]:它是抽象类吗?;嵌套异常是java.lang.InstructionException
在使用此库的先前GraphQL API实现中,相同的模型也可以正常工作。
我的问题是如何在POJO上正确设置JSON标量类型?我试过杰克逊的ObjectNode
和Map<String, Object>
,但都没有成功。
我最终实现了自己的自定义强制方法,所以我将在这里分享以供进一步参考:
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.Collections;
import java.util.Map;
import graphql.schema.Coercing;
import graphql.schema.CoercingParseLiteralException;
import graphql.schema.CoercingParseValueException;
import graphql.schema.CoercingSerializeException;
import static graphql.scalars.ExtendedScalars.Object;
/**
* Custom Coercing implementation in order to parse GQL Objects,
* which get mapped as LinkedHashMap instances by {@link graphql.scalars.object.ObjectScalar}
* into Jackson's JsonNode objects.
*/
public class JsonNodeCoercing implements Coercing<JsonNode, Object> {
private static final Coercing<?, ?> COERCING = Object.getCoercing();
private final ObjectMapper objectMapper;
public JsonNodeCoercing(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public Object serialize(Object input) throws CoercingSerializeException {
return input;
}
@Override
public JsonNode parseValue(Object input) throws CoercingParseValueException {
return objectMapper.valueToTree(input);
}
@Override
public JsonNode parseLiteral(Object input) throws CoercingParseLiteralException {
return parseLiteral(input, Collections.emptyMap());
}
@Override
public JsonNode parseLiteral(Object input, Map<String, Object> variables) throws CoercingParseLiteralException {
return objectMapper.valueToTree(COERCING.parseLiteral(input, variables));
}
}
这就是标量bean配置的样子:
@Bean
public RuntimeWiringConfigurer runtimeWiringConfigurer(ObjectMapper objectMapper) {
GraphQLScalarType jsonScalarType = GraphQLScalarType.newScalar()
.name("JSON")
.description("A JSON scalar")
.coercing(new JsonNodeCoercing(objectMapper))
.build();
return wiringBuilder -> wiringBuilder
.scalar(jsonScalarType);
}
如果有人有其他/更简单的方法,请分享。