Eclipse Vert.x:使用 jackson 解析 java.time 日期

Wil*_*iam 2 java rest jackson vert.x

我正在尝试通过创建一个小型 ReST API 来学习 Vert.x。一切都很顺利,直到我添加了数据库后端。我选择 Postgres 作为数据库。当 java.time LocalDate 被解析为 JSON 时,我的问题出现了。据我了解,我需要杰克逊后端来解析它。我通过 Maven 依赖项将其添加到我的类路径中。依赖关系存在,但框架抛出异常,因为它找不到名为 jackson-datatype-jsr310 的 jackson 插件。好吧,它就在那里,所以我有点卡住了......

我使用Vert.x 4java 16,我使用 IntelliJ IDEA 在 Windows 10 上进行开发。

openjdk 16.0.1 2021-04-20
OpenJDK Runtime Environment AdoptOpenJDK-16.0.1+9 (build 16.0.1+9)
OpenJDK 64-Bit Server VM AdoptOpenJDK-16.0.1+9 (build 16.0.1+9, mixed mode, sharing)
Run Code Online (Sandbox Code Playgroud)

我的球

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>myapi</artifactId>
  <version>1.0.0-SNAPSHOT</version>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

    <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
    <maven-shade-plugin.version>3.2.4</maven-shade-plugin.version>
    <maven-surefire-plugin.version>2.22.2</maven-surefire-plugin.version>
    <exec-maven-plugin.version>3.0.0</exec-maven-plugin.version>

    <vertx.version>4.1.4</vertx.version>
    <junit-jupiter.version>5.8.0</junit-jupiter.version>
    <log4j.version>2.14.1</log4j.version>
    <jackson.version>2.13.0-rc2</jackson.version>

    <main.verticle>dev.schaw.myapi.MainVerticle</main.verticle>
    <launcher.class>io.vertx.core.Launcher</launcher.class>
  </properties>

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>io.vertx</groupId>
        <artifactId>vertx-stack-depchain</artifactId>
        <version>${vertx.version}</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

  <dependencies>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.datatype</groupId>
      <artifactId>jackson-datatype-jsr310</artifactId>
      <version>${jackson.version}</version>
    </dependency>
    <dependency>
      <groupId>io.vertx</groupId>
      <artifactId>vertx-web</artifactId>
    </dependency>

    <dependency>
      <groupId>io.vertx</groupId>
      <artifactId>vertx-junit5</artifactId>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-api</artifactId>
      <version>${junit-jupiter.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.junit.jupiter</groupId>
      <artifactId>junit-jupiter-engine</artifactId>
      <version>${junit-jupiter.version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>io.vertx</groupId>
      <artifactId>vertx-config</artifactId>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-api</artifactId>
      <version>${log4j.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-core</artifactId>
      <version>${log4j.version}</version>
    </dependency>
    <dependency>
      <groupId>org.apache.logging.log4j</groupId>
      <artifactId>log4j-slf4j-impl</artifactId>
      <version>${log4j.version}</version>
    </dependency>
    <dependency>
      <groupId>io.vertx</groupId>
      <artifactId>vertx-pg-client</artifactId>
      <version>${vertx.version}</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>${maven-compiler-plugin.version}</version>
        <configuration>
          <release>11</release>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-shade-plugin</artifactId>
        <version>${maven-shade-plugin.version}</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <transformers>
                <transformer
                  implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                  <manifestEntries>
                    <Main-Class>${launcher.class}</Main-Class>
                    <Main-Verticle>${main.verticle}</Main-Verticle>
                  </manifestEntries>
                </transformer>
                <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
              </transformers>
              <outputFile>${project.build.directory}/${project.artifactId}-${project.version}-fat.jar
              </outputFile>
            </configuration>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>${maven-surefire-plugin.version}</version>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>exec-maven-plugin</artifactId>
        <version>${exec-maven-plugin.version}</version>
        <configuration>
          <mainClass>io.vertx.core.Launcher</mainClass>
          <arguments>
            <argument>run</argument>
            <argument>${main.verticle}</argument>
          </arguments>
        </configuration>
      </plugin>
    </plugins>
  </build>


</project>
Run Code Online (Sandbox Code Playgroud)

我的垂直

package dev.schaw.myapi;

import io.vertx.config.ConfigRetriever;
import io.vertx.config.ConfigRetrieverOptions;
import io.vertx.config.ConfigStoreOptions;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.AsyncResult;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServer;
import io.vertx.core.http.HttpServerResponse;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.Router;
import io.vertx.pgclient.PgConnectOptions;
import io.vertx.pgclient.PgPool;
import io.vertx.sqlclient.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MainVerticle extends AbstractVerticle {

  private HttpServer server;
  private Router router;
  private Logger logger;
  private SqlClient sqlClient;

  @Override
  public void start() {

    server = vertx.createHttpServer();
    router = Router.router(vertx);
    logger = LoggerFactory.getLogger(MainVerticle.class);

    ConfigRetriever retriever = ConfigRetriever.create(vertx, new ConfigRetrieverOptions()
      .addStore(new ConfigStoreOptions()
        .setType("file")
        .setConfig(new JsonObject().put("path", "config.json"))));
    retriever.getConfig(this::onConfigRetrieved);
  }

  public void onConfigRetrieved(AsyncResult<JsonObject> configAsyncResult){
    JsonObject config = configAsyncResult.result();

    JsonObject dataConnection = config.getJsonObject("myapi").getJsonObject("dataConnection");
    PgConnectOptions connectOptions = new PgConnectOptions()
      .setHost(dataConnection.getString("host"))
      .setPort(dataConnection.getInteger("port"))
      .setDatabase(dataConnection.getString("database"))
      .setUser(dataConnection.getString("user"))
      .setPassword(dataConnection.getString("password"));

    PoolOptions poolOptions = new PoolOptions().setMaxSize(5);

    sqlClient = PgPool.pool(vertx, connectOptions, poolOptions);

    router.route(HttpMethod.GET, "/").handler(ctx -> {
      logger.info("received a request");
      HttpServerResponse response = ctx.response();
      response.putHeader("content-type", "text/plain");
      response.end("Hello World!\n");
    });

    router.get("/data").handler(ctx -> {
        sqlClient.preparedQuery("SELECT * FROM sample_data WHERE id=$1")
          .execute(Tuple.of(1), queryAsyncResult -> {
            RowSet<Row> set = queryAsyncResult.result();
            if(set.rowCount() != 1){
              ctx.fail(404);
            }
            Row r = set.iterator().next();
            JsonObject body = new JsonObject().put("id", r.getInteger("id"))
              .put("name", r.getString("name"))
              .put("date", r.getLocalDate("date"));
            ctx.response().putHeader("content-type", "application/json").end(body.encode());
          });
      });

    int port = config
      .getJsonObject("myapi")
      .getJsonObject("server")
      .getInteger("port");
    server.requestHandler(router).listen(port);
  }
}
Run Code Online (Sandbox Code Playgroud)

数据库初始化脚本

create table sample_data (
  id serial primary key,
  name varchar(80) not null,
  date date not null
);
Run Code Online (Sandbox Code Playgroud)

这是例外

你可以在这里检查类路径。

"C:\Program Files\AdoptOpenJDK\jdk-16.0.1.9-hotspot\bin\java.exe" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:53414,suspend=y,server=n -javaagent:C:\Users\willi\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\212.5284.40\plugins\java\lib\rt\debugger-agent.jar -Dfile.encoding=UTF-8 -classpath C:\Users\willi\Projects\myapi\target\classes;C:\Users\willi\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.13.0-rc2\jackson-core-2.13.0-rc2.jar;C:\Users\willi\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.13.0-rc2\jackson-databind-2.13.0-rc2.jar;C:\Users\willi\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.13.0-rc2\jackson-annotations-2.13.0-rc2.jar;C:\Users\willi\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.13.0-rc2\jackson-datatype-jsr310-2.13.0-rc2.jar;C:\Users\willi\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.13.0-rc2\jackson-datatype-jdk8-2.13.0-rc2.jar;C:\Users\willi\.m2\repository\io\vertx\vertx-web\4.1.4\vertx-web-4.1.4.jar;C:\Users\willi\.m2\repository\io\vertx\vertx-web-common\4.1.4\vertx-web-common-4.1.4.jar;C:\Users\willi\.m2\repository\io\vertx\vertx-auth-common\4.1.4\vertx-auth-common-4.1.4.jar;C:\Users\willi\.m2\repository\io\vertx\vertx-bridge-common\4.1.4\vertx-bridge-common-4.1.4.jar;C:\Users\willi\.m2\repository\io\vertx\vertx-core\4.1.4\vertx-core-4.1.4.jar;C:\Users\willi\.m2\repository\io\netty\netty-common\4.1.67.Final\netty-common-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-buffer\4.1.67.Final\netty-buffer-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-transport\4.1.67.Final\netty-transport-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-handler\4.1.67.Final\netty-handler-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-codec\4.1.67.Final\netty-codec-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-handler-proxy\4.1.67.Final\netty-handler-proxy-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-codec-socks\4.1.67.Final\netty-codec-socks-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-codec-http\4.1.67.Final\netty-codec-http-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-codec-http2\4.1.67.Final\netty-codec-http2-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-resolver\4.1.67.Final\netty-resolver-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-resolver-dns\4.1.67.Final\netty-resolver-dns-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\netty\netty-codec-dns\4.1.67.Final\netty-codec-dns-4.1.67.Final.jar;C:\Users\willi\.m2\repository\io\vertx\vertx-config\4.1.4\vertx-config-4.1.4.jar;C:\Users\willi\.m2\repository\org\apache\logging\log4j\log4j-api\2.14.1\log4j-api-2.14.1.jar;C:\Users\willi\.m2\repository\org\apache\logging\log4j\log4j-core\2.14.1\log4j-core-2.14.1.jar;C:\Users\willi\.m2\repository\org\apache\logging\log4j\log4j-slf4j-impl\2.14.1\log4j-slf4j-impl-2.14.1.jar;C:\Users\willi\.m2\repository\org\slf4j\slf4j-api\1.7.25\slf4j-api-1.7.25.jar;C:\Users\willi\.m2\repository\io\vertx\vertx-pg-client\4.1.4\vertx-pg-client-4.1.4.jar;C:\Users\willi\.m2\repository\io\vertx\vertx-sql-client\4.1.4\vertx-sql-client-4.1.4.jar;C:\Users\willi\AppData\Local\JetBrains\Toolbox\apps\IDEA-U\ch-0\212.5284.40\lib\idea_rt.jar io.vertx.core.Launcher run dev.schaw.myapi.MainVerticle
Connected to the target VM, address: '127.0.0.1:53414', transport: 'socket'
OpenJDK 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
16:06:01.100 [vert.x-eventloop-thread-0] INFO  io.vertx.core.impl.launcher.commands.VertxIsolatedDeployer - Succeeded in deploying verticle
16:06:03.393 [vert.x-eventloop-thread-1] ERROR io.vertx.core.impl.ContextImpl - Unhandled exception
io.vertx.core.json.EncodeException: Failed to encode as JSON: Java 8 date/time type `java.time.LocalDate` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling (through reference chain: java.util.LinkedHashMap["date"])
    at io.vertx.core.json.jackson.DatabindCodec.toString(DatabindCodec.java:163) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.json.JsonObject.encode(JsonObject.java:728) ~[vertx-core-4.1.4.jar:4.1.4]
    at dev.schaw.myapi.MainVerticle.lambda$onConfigRetrieved$1(MainVerticle.java:73) ~[classes/:?]
    at io.vertx.core.impl.future.FutureImpl$3.onSuccess(FutureImpl.java:141) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.future.FutureImpl.tryComplete(FutureImpl.java:211) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.future.PromiseImpl.tryComplete(PromiseImpl.java:23) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.sqlclient.impl.QueryResultBuilder.tryComplete(QueryResultBuilder.java:102) ~[vertx-sql-client-4.1.4.jar:4.1.4]
    at io.vertx.sqlclient.impl.QueryResultBuilder.tryComplete(QueryResultBuilder.java:35) ~[vertx-sql-client-4.1.4.jar:4.1.4]
    at io.vertx.core.Promise.complete(Promise.java:66) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.Promise.handle(Promise.java:51) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.Promise.handle(Promise.java:29) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.future.FutureImpl$3.onSuccess(FutureImpl.java:141) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:60) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.future.FutureImpl.tryComplete(FutureImpl.java:211) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.future.PromiseImpl.tryComplete(PromiseImpl.java:23) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.future.PromiseImpl.onSuccess(PromiseImpl.java:49) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.future.FutureImpl$ListenerArray.onSuccess(FutureImpl.java:262) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.future.FutureBase.lambda$emitSuccess$0(FutureBase.java:54) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.EventLoopContext.execute(EventLoopContext.java:81) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.DuplicatedContext.execute(DuplicatedContext.java:173) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.future.FutureBase.emitSuccess(FutureBase.java:51) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.future.FutureImpl.tryComplete(FutureImpl.java:211) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.future.PromiseImpl.tryComplete(PromiseImpl.java:23) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.future.PromiseImpl.onSuccess(PromiseImpl.java:49) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.future.PromiseImpl.handle(PromiseImpl.java:41) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.future.PromiseImpl.handle(PromiseImpl.java:23) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.sqlclient.impl.command.CommandResponse.fire(CommandResponse.java:46) ~[vertx-sql-client-4.1.4.jar:4.1.4]
    at io.vertx.sqlclient.impl.SocketConnectionBase.handleMessage(SocketConnectionBase.java:287) ~[vertx-sql-client-4.1.4.jar:4.1.4]
    at io.vertx.pgclient.impl.PgSocketConnection.handleMessage(PgSocketConnection.java:96) ~[vertx-pg-client-4.1.4.jar:4.1.4]
    at io.vertx.sqlclient.impl.SocketConnectionBase.lambda$init$0(SocketConnectionBase.java:99) ~[vertx-sql-client-4.1.4.jar:4.1.4]
    at io.vertx.core.net.impl.NetSocketImpl.lambda$new$1(NetSocketImpl.java:97) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.streams.impl.InboundBuffer.handleEvent(InboundBuffer.java:240) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.streams.impl.InboundBuffer.write(InboundBuffer.java:130) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.net.impl.NetSocketImpl.lambda$handleMessage$9(NetSocketImpl.java:390) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.EventLoopContext.emit(EventLoopContext.java:50) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.ContextImpl.emit(ContextImpl.java:274) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.impl.EventLoopContext.emit(EventLoopContext.java:22) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.net.impl.NetSocketImpl.handleMessage(NetSocketImpl.java:389) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.net.impl.ConnectionBase.read(ConnectionBase.java:155) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.vertx.core.net.impl.VertxHandler.channelRead(VertxHandler.java:154) ~[vertx-core-4.1.4.jar:4.1.4]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.67.Final.jar:4.1.67.Final]
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.67.Final.jar:4.1.67.Final]
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.67.Final.jar:4.1.67.Final]
    at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436) ~[netty-transport-4.1.67.Final.jar:4.1.67.Final]
    at io.vertx.pgclient.impl.codec.PgEncoder.lambda$write$0(PgEncoder.java:87) ~[vertx-pg-client-

ray*_*ray 8

幕后Vertx使用Jackson's ObjectMapper内部encode()encodePrettily()方法。您需要做的就是JavaTimeModule在这些网站上注册ObjectMappers.

将这些代码片段添加到应用程序的主方法中。

ObjectMapper mapper = DatabindCodec.mapper();
mapper.registerModule(new JavaTimeModule());

ObjectMapper prettyMapper = DatabindCodec.prettyMapper();
prettyMapper.registerModule(new JavaTimeModule());
Run Code Online (Sandbox Code Playgroud)

这是类的完整包DatabindCodecio.vertx.core.json.jackson.DatabindCodec

更新

public class MainVerticle extends AbstractVerticle {

  // code already exists

  @Override
  public void start() {
      ObjectMapper mapper = DatabindCodec.mapper();
      mapper.registerModule(new JavaTimeModule());
 
      ObjectMapper prettyMapper = DatabindCodec.prettyMapper();
      prettyMapper.registerModule(new JavaTimeModule());
  
  
  // code already exists
Run Code Online (Sandbox Code Playgroud)