'GraphQL Scalar for java.time.LocalDate parses dates as StringValue

I try to parse a date, e.g. "2022-02-02" as java.time.LocalDate.

Why the values passed to the scalar class are wrapped into curly braces? And how do I fix this?

I configured the scalar for java.time.LocalDate:

@Component
public class DateScalar extends GraphQLScalarType {

public DateScalar() {
    super("Date", "java.time.LocalDate", new Coercing() {
        @Override
        public Object serialize(Object o) throws CoercingSerializeException {
            return DateTimeFormatter.ofPattern("yyyy-MM-dd").format((LocalDate) o);
        }

        @Override
        public Object parseValue(Object o) throws CoercingParseValueException {
            return LocalDate.parse(o.toString());
        }

        @Override
        public Object parseLiteral(Object o) throws CoercingParseLiteralException {
            if (o == null) {
                return null;
            }
            return parseValue(o.toString());
        }
        });
    }
}

This is the query:

mutation {
createEvent(createEventRequest: {
    name: "test",
    description: "test description",
    userId: 1,
    participants: [{
        firstName: "first",
        lastName: "last"
    }],
  location: {
    street: "street",
    streetNumber: "streetNumber",
    country: "country"
  },
  duration: {
    entireDay: false,
    recurringDate: false,
    start: "2018-09-03"
  }
}) {
    id
    name
    description
    participantResponses {
        id
        firstName
        lastName
    }
    locationResponse {
        id
        street
        streetNumber
        country
    }
    durationResponse {
        id
        start
        end
        entireDay
        recurringDate
    }
    userResponse {
        id
        firstName
        lastName
        email
    }
}
}

The error says that Text 'StringValue{value='2018-09-03'}' could not be parsed at index 0. I do not really understand why this is still wrapped into curly brackets.

java.time.format.DateTimeParseException: Text 'StringValue{value='2018-09-03'}' could not be parsed at index 0 at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2052) ~[na:na] at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1954) ~[na:na] at java.base/java.time.LocalDate.parse(LocalDate.java:430) ~[na:na] at java.base/java.time.LocalDate.parse(LocalDate.java:415) ~[na:na] at com.projects.calendar.scalar.DateScalar$1.parseValue(DateScalar.java:21) ~[classes/:na] at com.projects.calendar.scalar.DateScalar$1.parseLiteral(DateScalar.java:29) ~[classes/:na] at graphql.validation.ValidationUtil.parseLiteral(ValidationUtil.java:113) ~[graphql-java-9.2.jar:na] at graphql.validation.ValidationUtil.isValidLiteralValue(ValidationUtil.java:90) ~[graphql-java-9.2.jar:na] at graphql.validation.ValidationUtil.isValidLiteralValue(ValidationUtil.java:141) ~[graphql-java-9.2.jar:na] at graphql.validation.ValidationUtil.isValidLiteralValue(ValidationUtil.java:107) ~[graphql-java-9.2.jar:na] at graphql.validation.ValidationUtil.isValidLiteralValue(ValidationUtil.java:141) ~[graphql-java-9.2.jar:na] at graphql.validation.ValidationUtil.isValidLiteralValue(ValidationUtil.java:107) ~[graphql-java-9.2.jar:na] at graphql.validation.rules.ArgumentsOfCorrectType.checkArgument(ArgumentsOfCorrectType.java:23) ~[graphql-java-9.2.jar:na] at graphql.validation.RulesVisitor.checkArgument(RulesVisitor.java:98) ~[graphql-java-9.2.jar:na] at graphql.validation.RulesVisitor.enter(RulesVisitor.java:65) ~[graphql-java-9.2.jar:na] at graphql.validation.LanguageTraversal.traverseImpl(LanguageTraversal.java:33) ~[graphql-java-9.2.jar:na] at graphql.validation.LanguageTraversal.traverseImpl(LanguageTraversal.java:38) ~[graphql-java-9.2.jar:na] at graphql.validation.LanguageTraversal.traverseImpl(LanguageTraversal.java:38) ~[graphql-java-9.2.jar:na] at graphql.validation.LanguageTraversal.traverseImpl(LanguageTraversal.java:38) ~[graphql-java-9.2.jar:na] at graphql.validation.LanguageTraversal.traverseImpl(LanguageTraversal.java:38) ~[graphql-java-9.2.jar:na] at graphql.validation.LanguageTraversal.traverse(LanguageTraversal.java:28) ~[graphql-java-9.2.jar:na] at graphql.validation.Validator.validateDocument(Validator.java:42) ~[graphql-java-9.2.jar:na] at graphql.GraphQL.validate(GraphQL.java:539) ~[graphql-java-9.2.jar:na] at graphql.GraphQL.parseAndValidate(GraphQL.java:509) ~[graphql-java-9.2.jar:na] at graphql.GraphQL.lambda$parseValidateAndExecute$3(GraphQL.java:490) ~[graphql-java-9.2.jar:na] at graphql.execution.preparsed.NoOpPreparsedDocumentProvider.get(NoOpPreparsedDocumentProvider.java:11) ~[graphql-java-9.2.jar:na] at graphql.GraphQL.parseValidateAndExecute(GraphQL.java:486) ~[graphql-java-9.2.jar:na] at graphql.GraphQL.executeAsync(GraphQL.java:470) ~[graphql-java-9.2.jar:na] at graphql.GraphQL.execute(GraphQL.java:401) ~[graphql-java-9.2.jar:na] at graphql.servlet.GraphQLQueryInvoker.query(GraphQLQueryInvoker.java:92) ~[graphql-java-servlet-6.1.2.jar:na] at graphql.servlet.GraphQLQueryInvoker.query(GraphQLQueryInvoker.java:88) ~[graphql-java-servlet-6.1.2.jar:na] at graphql.servlet.GraphQLQueryInvoker.query(GraphQLQueryInvoker.java:39) ~[graphql-java-servlet-6.1.2.jar:na] at graphql.servlet.AbstractGraphQLHttpServlet.query(AbstractGraphQLHttpServlet.java:265) ~[graphql-java-servlet-6.1.2.jar:na] at graphql.servlet.AbstractGraphQLHttpServlet.lambda$new$2(AbstractGraphQLHttpServlet.java:183) ~[graphql-java-servlet-6.1.2.jar:na] at graphql.servlet.AbstractGraphQLHttpServlet.doRequest(AbstractGraphQLHttpServlet.java:236) ~[graphql-java-servlet-6.1.2.jar:na] at graphql.servlet.AbstractGraphQLHttpServlet.doRequestAsync(AbstractGraphQLHttpServlet.java:227) ~[graphql-java-servlet-6.1.2.jar:na] at graphql.servlet.AbstractGraphQLHttpServlet.doPost(AbstractGraphQLHttpServlet.java:257) ~[graphql-java-servlet-6.1.2.jar:na] at javax.servlet.http.HttpServlet.service(HttpServlet.java:681) ~[tomcat-embed-core-9.0.56.jar:4.0.FR] at javax.servlet.http.HttpServlet.service(HttpServlet.java:764) ~[tomcat-embed-core-9.0.56.jar:4.0.FR] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.56.jar:9.0.56] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.springframework.web.filter.CorsFilter.doFilterInternal(CorsFilter.java:91) ~[spring-web-5.3.15.jar:5.3.15] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.15.jar:5.3.15] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.15.jar:5.3.15] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.15.jar:5.3.15] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.15.jar:5.3.15] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.15.jar:5.3.15] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.15.jar:5.3.15] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.15.jar:5.3.15] at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:540) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1732) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.56.jar:9.0.56] at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]

This is the graphqls file if needed:

type Query {
    user(id : Int) : UserResponse,
    userByEmail(email : String) : [UserResponse]
}

type Mutation {
    createUser(createUserRequest: CreateUserRequest) : UserResponse
    createEvent(createEventRequest: CreateEventRequest) : EventResponse
}

input CreateEventRequest {
    name: String,
    description: String,
    participants: [Participant],
    location: Location,
    duration: Duration,
    userId: Int
}

input Duration {
    start:Date,
    end:Date,
    entireDay:Boolean      ,
    recurringDate:Boolean
}

input Location {
    street:String ,
    streetNumber:String,
    country:String
}

input Participant {
    firstName: String,
    lastName: String
}

type UserResponse {
    id: Int,
    firstName: String,
    lastName: String,
    email: String
}

type EventResponse {
    id: Int,
    name: String,
    description: String,
    participantResponses: [ParticipantResponse],
    locationResponse: LocationResponse,
    durationResponse: DurationResponse,
    userResponse: UserResponse
}

type DurationResponse {
    id: Int
    start: Date
    end: Date
    entireDay: Boolean
    recurringDate: Boolean
}


type LocationResponse {
    id: Int,
    street: String ,
    streetNumber: String ,
    country: String
}

type ParticipantResponse {
    id: Int,
    firstName: String,
    lastName: String
}

input CreateUserRequest {
    firstName: String!,
    lastName: String!,
    email: String!
}

scalar Date


Solution 1:[1]

The parseLiteral method parameter is actually a node of the abstract syntax tree produced from parsing the GraphQL query. The method implementation should extract the literal value from the node:

@Override
public LocalDate parseLiteral(Object o) throws CoercingParseLiteralException {
  if (o instanceof StringValue) {
    var value = ((StringValue) o).getValue();
    try {
      return LocalDate.parse(value);
    } catch (DateTimeParseException e) {
      throw new CoercingParseLiteralException("Cannot parse [" + value + "] to LocalDate", e);
    }
  } else {
    throw new CoercingParseLiteralException(
        "Expected literal of type StringValue but was " + o.getClass());
  }
}

Solution 2:[2]

The GraphQL is sending the date string as full text as you have declared this as Date. Alternatively, you can declare start as String and parse the string to Date at Java end.

input Duration {
start: String,
end: String,
entireDay:Boolean      ,
recurringDate:Boolean

}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Code Sanjay