package io.leangen.graphql.module.common.jackson; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.BigIntegerNode; import com.fasterxml.jackson.databind.node.BinaryNode; import com.fasterxml.jackson.databind.node.BooleanNode; import com.fasterxml.jackson.databind.node.DecimalNode; import com.fasterxml.jackson.databind.node.DoubleNode; import com.fasterxml.jackson.databind.node.FloatNode; import com.fasterxml.jackson.databind.node.IntNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.NumericNode; import com.fasterxml.jackson.databind.node.ShortNode; import com.fasterxml.jackson.databind.node.TextNode; import graphql.schema.GraphQLInputType; import graphql.schema.GraphQLOutputType; import io.leangen.geantyref.GenericTypeReflector; import io.leangen.graphql.execution.GlobalEnvironment; import io.leangen.graphql.execution.ResolutionEnvironment; import io.leangen.graphql.generator.mapping.InputConverter; import io.leangen.graphql.generator.mapping.OutputConverter; import io.leangen.graphql.generator.mapping.TypeMapper; import io.leangen.graphql.generator.mapping.TypeMappingEnvironment; import io.leangen.graphql.metadata.strategy.value.ValueMapper; import io.leangen.graphql.util.ClassUtils; import java.lang.reflect.AnnotatedElement; import java.lang.reflect.AnnotatedType; import java.lang.reflect.Type; import java.math.BigDecimal; import java.math.BigInteger; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.function.Function; public class JsonNodeAdapter implements TypeMapper, InputConverter<JsonNode, Object>, OutputConverter<JsonNode, Object> { @Override @SuppressWarnings("unchecked") public JsonNode convertInput(Object substitute, AnnotatedType type, GlobalEnvironment environment, ValueMapper valueMapper) { return (JsonNode) mappings.get(ClassUtils.getRawType(type.getType())).deserializer.apply(substitute); } @Override public AnnotatedType getSubstituteType(AnnotatedType original) { return GenericTypeReflector.annotate(mappings.get(ClassUtils.getRawType(original.getType())).substituteClass, original.getAnnotations()); } @Override @SuppressWarnings("unchecked") public Object convertOutput(JsonNode original, AnnotatedType type, ResolutionEnvironment resolutionEnvironment) { return mappings.get(ClassUtils.getRawType(type.getType())).serializer.apply(original); } @Override public GraphQLOutputType toGraphQLType(AnnotatedType javaType, Set<Class<? extends TypeMapper>> mappersToSkip, TypeMappingEnvironment env) { return env.operationMapper.toGraphQLType(getSubstituteType(javaType), env); } @Override public GraphQLInputType toGraphQLInputType(AnnotatedType javaType, Set<Class<? extends TypeMapper>> mappersToSkip, TypeMappingEnvironment env) { return env.operationMapper.toGraphQLInputType(getSubstituteType(javaType), env); } @Override public boolean supports(AnnotatedType type) { return mappings.containsKey(ClassUtils.getRawType(type.getType())); } @Override public boolean supports(AnnotatedElement element, AnnotatedType type) { return supports(type); } @SuppressWarnings("rawtypes") private static final Map<Class, JsonNodeDescriptor> mappings; static { Map<Class<?>, JsonNodeDescriptor<?, ?>> typeMapping = new HashMap<>(); typeMapping.put(TextNode.class, new JsonNodeDescriptor<>(String.class, JsonNodeFactory.instance::textNode, TextNode::textValue)); typeMapping.put(BooleanNode.class, new JsonNodeDescriptor<>(Boolean.class, JsonNodeFactory.instance::booleanNode, BooleanNode::booleanValue)); typeMapping.put(BinaryNode.class, new JsonNodeDescriptor<>(byte[].class, BinaryNode::new, BinaryNode::binaryValue)); typeMapping.put(DecimalNode.class, new JsonNodeDescriptor<>(BigDecimal.class, DecimalNode::new, DecimalNode::decimalValue)); typeMapping.put(BigIntegerNode.class, new JsonNodeDescriptor<>(BigInteger.class, BigIntegerNode::new, BigIntegerNode::bigIntegerValue)); typeMapping.put(IntNode.class, new JsonNodeDescriptor<>(Integer.class, IntNode::new, IntNode::intValue)); typeMapping.put(DoubleNode.class, new JsonNodeDescriptor<>(Double.class, DoubleNode::new, DoubleNode::doubleValue)); typeMapping.put(FloatNode.class, new JsonNodeDescriptor<>(Float.class, FloatNode::new, FloatNode::floatValue)); typeMapping.put(ShortNode.class, new JsonNodeDescriptor<>(Short.class, ShortNode::new, ShortNode::shortValue)); typeMapping.put(NumericNode.class, new JsonNodeDescriptor<>(BigDecimal.class, DecimalNode::new, DecimalNode::decimalValue)); mappings = Collections.unmodifiableMap(typeMapping); } private static class JsonNodeDescriptor<T extends JsonNode, S> { final Type substituteClass; final Function<S, T> deserializer; final Function<T, S> serializer; private JsonNodeDescriptor(Class<S> substituteClass, Function<S, T> deserializer, Function<T, S> serializer) { this.substituteClass = substituteClass; this.deserializer = deserializer; this.serializer = serializer; } } }