java.time.LocalDate 的 GraphQL 标量将日期解析为 StringValue

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

我尝试解析日期,例如“2022-02-02”为 java.time.LocalDate.

为什么传递给标量 class 的值被包裹在花括号中?我该如何解决这个问题?

我为 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());
        }
        });
    }
}

这是查询:

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
    }
}
}

错误表明无法在索引 0 处解析 文本“StringValue{value='2018-09-03'}”。 我真的不明白为什么这仍然用大括号括起来。

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.parseValue(DateScalar.java:21) ~[classes/:na] at com.projects.calendar.scalar.DateScalar.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(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(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]

如果需要,这是 graphqls 文件:

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

GraphQL 以全文形式发送日期字符串,因为您已将其声明为日期。或者,您可以将 start 声明为 String 并将字符串解析为 Java end.

处的 Date
input Duration {
start: String,
end: String,
entireDay:Boolean      ,
recurringDate:Boolean

}

parseLiteral方法参数实际上是解析GraphQL查询生成的抽象语法树的一个节点。方法实现应该从节点中提取文字值:

@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());
  }
}