Search code examples
javagraphqlmarshalling

Marshal GraphQL execution results to java object


I searched a lot into SO, but unfortunately any of the proposed solutions fits to me.

I am going to describe the key points:

  1. My REST project is built upon OpenAPI and the OpenAPI maven generator plugin.
  2. By the jaxrs-spec generator I produce the application DTOs.
  3. By the graphql-nodejs-express-server generator I produce the GraphQL schema files.
  4. The REST web service accepts, as optional, a GraphQL query in order to filter/reduce the verbosity of the answer.

The GraphQL ExecutionResult.getData() method returns an instance of a LinkedHashMap.

The question is: How can I marshall the above LinkedHashMap to the corresponding DTO?

Thank you so much in advance!

---- Edit ----

I wish to improve and simplify my question.

I have a DTO such the following one:

public class ResponseDTO {
    private Integer id;
    private String description;
}

I filter it by GraphQL:

GraphQLSchema graphQLSchema = getGraphQLSchema();
String        graphqlQuery  = "{ ResponseDTO { id } }";

ExecutionInput builder = ExecutionInput
    .newExecutionInput()
    .query(graphqlQuery)
    .build();

ExecutionResult executionResult = GraphQL
    .newGraphQL(graphQLSchema)
    .build()
    .execute(builder);
    
LinkedHashMap graphQLData = executionResult.getData();

//TODO How can I convert 'graphQLData' to a `ResponseDTO` instance?

ResponseDTO responseDTO = ???;

Solution

  • I finally found the solution:

    LinkedHashMap<String, Object> dataMap      = executionResult.getData();
    String                        dataKey      = dataMap.keySet().toArray()[0].toString();
    HashMap<String, Object>       dataValue    = (HashMap<String, Object>)dataMap.get(dataKey);
    
    fixTypeId(
        dataContext.getClass(),
        dataValue
    );
    
    ObjectMapper             objectMapper = new ObjectMapper();
    
    objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);            objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    objectMapper.registerModule(new JavaTimeModule());
    objectMapper.registerModule(new JaxbAnnotationModule());
    objectMapper.setDateFormat(new StdDateFormat().withColonInTimeZone(true));           objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    
    String dataJSON = objectMapper.writeValueAsString(dataValue);
    
    ResponseDTO responseDTO = objectMapper.readValue(
        dataJSON,
        ResponseDTO.class
    );
    

    Where fixTypeId is:

    @SuppressWarnings("unchecked")
    private void fixTypeId(Class<?> refClass, Map<String, Object> dataMap) {
        JsonTypeInfo typeInfoAnnotation = refClass.getDeclaredAnnotation(JsonTypeInfo.class);
        JsonTypeName typeNameAnnotation = refClass.getDeclaredAnnotation(JsonTypeName.class);
    
        if ((null != typeInfoAnnotation) && (null != typeNameAnnotation)) {
            String typeInfo = typeInfoAnnotation.property();
            String typeName = typeNameAnnotation.value();
    
            log.trace(
                "Adding \"{} = {}\" type identifier.",
                typeInfo,
                typeName
            );
    
            dataMap.put(typeInfo, typeName);
        }
    
        dataMap
            .entrySet()
            .stream()
            .filter(entry -> (entry.getValue() instanceof Map))
            .forEach(entry -> {
                Field field = null;
    
                try {
                    field = refClass.getDeclaredField(entry.getKey());
                } catch (NoSuchFieldException exception) {
                    log.error(exception.getMessage(), exception);
                    throw new RuntimeException(exception);
                }
    
                fixTypeId(
                    field.getType(),
                    (Map<String, Object>)entry.getValue()
                );
            });
    }