CLOVER🍀

That was when it all began.

WildFly 27.0.0.Finalから、RESTEasyがJSONを扱う時のデフォルトモジュールがJakarta JSON Bindingになっていたという話

これは、なにをしたくて書いたもの?

最近のWildFlyを使っていて、Jakarta RESTful Web Services(JAX-RS)の実装であるRESTEasyがJSONを扱う際に使っているモジュールが、
Jacksonではなくなっていることに気づきまして。

ちょっと確認してみようかなと。

WildFlyのドキュメントを確認してみる

WildFly 33のドキュメントを確認すると、デフォルトではJSONの処理にJakarta JSON Processingが使われることが書かれています。

By default, for JSON processing the Jakarta JSON Processing API is used. However, you can use Jackson instead.

Developer Guide / Jakarta RESTful Web Services Reference Guide / Using Jackson for serialization

説明としては、JSON-P(というかJakarta JSON Binding、JSON-B)からJacksonに切り替える場合の説明なんですけどね。

そして、web.xmlWildFlyのサブシステムの設定で切り替えられるとも書いています。また、デプロイしたアプリケーションで
Jacksonのアノテーションが使われていることを検出した場合も切り替えられるようです。

This can be achieved by setting the resteasy.preferJacksonOverJsonB property as a system property or as a context property in the deployment’s web.xml. There is also a subsystem attribute, resteasy-prefer-jackson-over-jsonb, which can be set to true. Finally, if no value is set for the context and system properties, the subsystem scans Jakarta RESTful Web Services deployments for Jackson annotations and sets the property to true if any of those annotations are found.

実際、Jacksonがデフォルトで使われないようになっていることに気づいたのは、WildFly 33.0.2.Finalにアプリケーションをデプロイした時に、
以下のような警告が出力されていたのに気づいたからです。

23:58:10,012 WARN  [org.jboss.as.jaxrs] (MSC service thread 1-6) WFLYRS0018: Explicit usage of Jackson annotation in a Jakarta RESTful Web Services deployment; the system will disable Jakarta JSON Binding processing for the current deployment. Consider setting the 'resteasy.preferJacksonOverJsonB' property to 'false' to restore Jakarta JSON Binding.

では、いつからJSON-Bに切り替わったんでしょう?

WildFly 26.1のドキュメントを見ると、JSON-BからJacksonに切り替える説明がありません。

Developer Guide / Jakarta RESTful Web Services Reference Guide

WildFly 27のドキュメントで登場します。

Developer Guide / Jakarta RESTful Web Services Reference Guide / Using Jackson for serialization

WildFlyJakarta EE 10に対応したのがWildFly 27.0.0.Finalなので、このタイミングで切り替わったと見てよさそうですね。
リリースブログなどを見返してみましたが、わかりませんでしたけど。

では、実際にJSON-Bに切り替わっているか確認してみたいと思います。

環境

今回の環境はこちら。

$ java --version
openjdk 21.0.4 2024-07-16
OpenJDK Runtime Environment (build 21.0.4+7-Ubuntu-1ubuntu222.04)
OpenJDK 64-Bit Server VM (build 21.0.4+7-Ubuntu-1ubuntu222.04, mixed mode, sharing)


$ mvn --version
Apache Maven 3.9.9 (8e8579a9e76f7d015ee5ec7bfcdc97d260186937)
Maven home: $HOME/.sdkman/candidates/maven/current
Java version: 21.0.4, vendor: Ubuntu, runtime: /usr/lib/jvm/java-21-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.15.0-124-generic", arch: "amd64", family: "unix"

WildFlyは33.0.2.Finalを使います。

試してみる

実際に動かして確認してみましょう。

Maven依存関係など。

    <properties>
        <maven.compiler.release>21</maven.compiler.release>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>jakarta.platform</groupId>
            <artifactId>jakarta.jakartaee-web-api</artifactId>
            <version>10.0.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>ROOT</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.4.0</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.wildfly.plugins</groupId>
                <artifactId>wildfly-maven-plugin</artifactId>
                <version>5.0.1.Final</version>
                <executions>
                    <execution>
                        <id>package</id>
                        <goals>
                            <goal>package</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <overwrite-provisioned-server>true</overwrite-provisioned-server>
                    <discover-provisioning-info>
                        <version>33.0.2.Final</version>
                    </discover-provisioning-info>
                </configuration>
            </plugin>
        </plugins>
    </build>

JSON-Bは、Jakarta EEの各プロファイルに含まれているようです。

JAX-RSの有効化。

src/main/java/org/littlewings/wildfly/jaxrs/RestApplication.java

package org.littlewings.wildfly.jaxrs;

import jakarta.ws.rs.ApplicationPath;

@ApplicationPath("/")
public class RestApplication extends jakarta.ws.rs.core.Application {
}

リクエストとレスポンスに使うクラス。

src/main/java/org/littlewings/wildfly/jaxrs/Echo.java

package org.littlewings.wildfly.jaxrs;

public class Echo {
    private String message;

    public Echo() {
        System.out.println("constructor");
        Thread.dumpStack();
    }

    public Echo(String message) {
        this.message = message;
    }

    String message() {
        return message;
    }

    public String getMessage() {
        System.out.println("getter");
        Thread.dumpStack();

        return message;
    }

    public void setMessage(String message) {
        System.out.println("setter");
        Thread.dumpStack();

        this.message = message;
    }
}

コンストラクターやgetter、setterを使うと、スタックトレースを出力するようにしています。

JAX-RSリソースクラス。

src/main/java/org/littlewings/wildfly/jaxrs/EchoResource.java

package org.littlewings.wildfly.jaxrs;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/echo")
public class EchoResource {
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Echo echo(Echo request) {
        return new Echo("★" + request.message() + "★");
    }
}

ここでリクエストやレスポンスを扱う時にはスタックトレースが出力されないような処理にしています。

WildFlyを起動してみます。

$ mvn wildfly:run

確認。

$ curl -XPOST -H 'Content-Type: application/json' localhost:8080/echo -d '{"message": "Hello"}'
{"message":"★Hello★"}

この時のスタックトレースを見てみます。

## JSONからのデシリアライズ時のインスタンス作成

00:08:38,676 INFO  [stdout] (default task-1) constructor
00:08:38,677 ERROR [stderr] (default task-1) java.lang.Exception: Stack trace
00:08:38,677 ERROR [stderr] (default task-1)    at java.base/java.lang.Thread.dumpStack(Thread.java:2209)
00:08:38,677 ERROR [stderr] (default task-1)    at deployment.ROOT.war//org.littlewings.wildfly.jaxrs.Echo.<init>(Echo.java:8)
00:08:38,677 ERROR [stderr] (default task-1)    at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
00:08:38,678 ERROR [stderr] (default task-1)    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502)
00:08:38,678 ERROR [stderr] (default task-1)    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
00:08:38,678 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.ReflectionUtils.createNoArgConstructorInstance(ReflectionUtils.java:274)
00:08:38,678 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.DefaultObjectInstanceCreator.deserialize(DefaultObjectInstanceCreator.java:55)
00:08:38,678 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.DefaultObjectInstanceCreator.deserialize(DefaultObjectInstanceCreator.java:29)
00:08:38,678 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.PositionChecker.deserialize(PositionChecker.java:85)
00:08:38,678 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.PositionChecker.deserialize(PositionChecker.java:34)
00:08:38,679 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.NullCheckDeserializer.deserialize(NullCheckDeserializer.java:46)
00:08:38,679 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.NullCheckDeserializer.deserialize(NullCheckDeserializer.java:26)
00:08:38,679 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.DeserializationContextImpl.deserializeItem(DeserializationContextImpl.java:138)
00:08:38,679 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.DeserializationContextImpl.deserialize(DeserializationContextImpl.java:127)
00:08:38,679 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.JsonBinding.deserialize(JsonBinding.java:55)
00:08:38,679 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.JsonBinding.fromJson(JsonBinding.java:95)
00:08:38,679 ERROR [stderr] (default task-1)    at org.jboss.resteasy.resteasy-json-binding-provider@6.2.10.Final//org.jboss.resteasy.plugins.providers.jsonb.ManagedJsonb.fromJson(ManagedJsonb.java:73)
00:08:38,680 ERROR [stderr] (default task-1)    at org.jboss.resteasy.resteasy-json-binding-provider@6.2.10.Final//org.jboss.resteasy.plugins.providers.jsonb.JsonBindingProvider.readFrom(JsonBindingProvider.java:71)

〜省略〜


## setter呼び出し時

00:08:38,692 INFO  [stdout] (default task-1) setter
00:08:38,692 ERROR [stderr] (default task-1) java.lang.Exception: Stack trace
00:08:38,693 ERROR [stderr] (default task-1)    at java.base/java.lang.Thread.dumpStack(Thread.java:2209)
00:08:38,693 ERROR [stderr] (default task-1)    at deployment.ROOT.war//org.littlewings.wildfly.jaxrs.Echo.setMessage(Echo.java:28)
00:08:38,693 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.ValueSetterDeserializer.deserialize(ValueSetterDeserializer.java:37)
00:08:38,693 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.types.TypeDeserializer.deserialize(TypeDeserializer.java:37)
00:08:38,693 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.ValueExtractor.deserialize(ValueExtractor.java:47)
00:08:38,693 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.ValueExtractor.deserialize(ValueExtractor.java:24)
00:08:38,693 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.PositionChecker.deserialize(PositionChecker.java:85)
00:08:38,694 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.PositionChecker.deserialize(PositionChecker.java:34)
00:08:38,694 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.NullCheckDeserializer.deserialize(NullCheckDeserializer.java:46)
00:08:38,694 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.NullCheckDeserializer.deserialize(NullCheckDeserializer.java:26)
00:08:38,694 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.ObjectDeserializer.deserialize(ObjectDeserializer.java:78)
00:08:38,694 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.ObjectDeserializer.deserialize(ObjectDeserializer.java:31)
00:08:38,694 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.DefaultObjectInstanceCreator.deserialize(DefaultObjectInstanceCreator.java:57)
00:08:38,695 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.DefaultObjectInstanceCreator.deserialize(DefaultObjectInstanceCreator.java:29)
00:08:38,695 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.PositionChecker.deserialize(PositionChecker.java:85)
00:08:38,695 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.PositionChecker.deserialize(PositionChecker.java:34)
00:08:38,695 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.NullCheckDeserializer.deserialize(NullCheckDeserializer.java:46)
00:08:38,695 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.deserializer.NullCheckDeserializer.deserialize(NullCheckDeserializer.java:26)
00:08:38,695 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.DeserializationContextImpl.deserializeItem(DeserializationContextImpl.java:138)
00:08:38,695 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.DeserializationContextImpl.deserialize(DeserializationContextImpl.java:127)
00:08:38,695 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.JsonBinding.deserialize(JsonBinding.java:55)
00:08:38,696 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.JsonBinding.fromJson(JsonBinding.java:95)
00:08:38,696 ERROR [stderr] (default task-1)    at org.jboss.resteasy.resteasy-json-binding-provider@6.2.10.Final//org.jboss.resteasy.plugins.providers.jsonb.ManagedJsonb.fromJson(ManagedJsonb.java:73)
00:08:38,696 ERROR [stderr] (default task-1)    at org.jboss.resteasy.resteasy-json-binding-provider@6.2.10.Final//org.jboss.resteasy.plugins.providers.jsonb.JsonBindingProvider.readFrom(JsonBindingProvider.java:71)

〜省略〜


## JSONへのシリアライズ時のgetter呼び出し

00:08:38,742 INFO  [stdout] (default task-1) getter
00:08:38,743 ERROR [stderr] (default task-1) java.lang.Exception: Stack trace
00:08:38,743 ERROR [stderr] (default task-1)    at java.base/java.lang.Thread.dumpStack(Thread.java:2209)
00:08:38,744 ERROR [stderr] (default task-1)    at deployment.ROOT.war//org.littlewings.wildfly.jaxrs.Echo.getMessage(Echo.java:21)
00:08:38,744 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.ValueGetterSerializer.serialize(ValueGetterSerializer.java:39)
00:08:38,744 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.ObjectSerializer.lambda$serialize$0(ObjectSerializer.java:41)
00:08:38,744 ERROR [stderr] (default task-1)    at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:986)
00:08:38,745 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.ObjectSerializer.serialize(ObjectSerializer.java:38)
00:08:38,745 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.RecursionChecker.serialize(RecursionChecker.java:38)
00:08:38,745 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.KeyWriter.serialize(KeyWriter.java:41)
00:08:38,745 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.NullVisibilitySwitcher.serialize(NullVisibilitySwitcher.java:40)
00:08:38,745 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.serializer.NullSerializer.serialize(NullSerializer.java:67)
00:08:38,745 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.SerializationContextImpl.serializeObject(SerializationContextImpl.java:197)
00:08:38,746 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.SerializationContextImpl.marshall(SerializationContextImpl.java:133)
00:08:38,746 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.SerializationContextImpl.marshall(SerializationContextImpl.java:159)
00:08:38,746 ERROR [stderr] (default task-1)    at org.eclipse.yasson//org.eclipse.yasson.internal.JsonBinding.toJson(JsonBinding.java:121)
00:08:38,746 ERROR [stderr] (default task-1)    at org.jboss.resteasy.resteasy-json-binding-provider@6.2.10.Final//org.jboss.resteasy.plugins.providers.jsonb.ManagedJsonb.toJson(ManagedJsonb.java:78)
00:08:38,747 ERROR [stderr] (default task-1)    at org.jboss.resteasy.resteasy-json-binding-provider@6.2.10.Final//org.jboss.resteasy.plugins.providers.jsonb.JsonBindingProvider.asyncWriteTo(JsonBindingProvider.java:217)

〜省略〜

というわけで、スタックトレースを見るとJSON-Bが使われていることが確認できました。

JSON-Bの実装としては、Eclipse Yassonが使われているようです。

Eclipse Yasson | projects.eclipse.org

では、ちょっと気になるところとして日時を扱ってみましょう。今回はLocalDateにしておきます。

src/main/java/org/littlewings/wildfly/jaxrs/DateRequest.java

package org.littlewings.wildfly.jaxrs;

import java.time.LocalDate;

public record DateRequest(LocalDate date) {
}

src/main/java/org/littlewings/wildfly/jaxrs/DateResponse.java

package org.littlewings.wildfly.jaxrs;

import java.time.LocalDate;

public record DateResponse(LocalDate date) {
}

JAX-RSリソースクラスでは、受け取った日付に1を足して返すことにします。

src/main/java/org/littlewings/wildfly/jaxrs/DateResource.java

package org.littlewings.wildfly.jaxrs;

import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/date")
public class DateResource {
    @POST
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public DateResponse plus(DateRequest request) {
        return new DateResponse(request.date().plusDays(1));
    }
}

確認。

$ curl -XPOST -H 'Content-Type: application/json' localhost:8080/date -d '{"date": "2024-10-16"}'
{"date":"2024-10-17"}

これはパースできるんですね。

フォーマットを変えてみましょう。

$ curl -XPOST -H 'Content-Type: application/json' localhost:8080/date -d '{"date": "2024/10/16"}'
RESTEASY008200: JSON Binding deserialization error: jakarta.json.bind.JsonbException: Unable to deserialize property 'date' because of: Error parsing class java.time.LocalDate from value: 2024/10/16. Check your @JsonbDateFormat has all time units for class java.time.LocalDate type, or consider using org.eclipse.yasson.YassonConfig#ZERO_TIME_PARSE_DEFAULTING.

さすがにダメでした。@JsonbDateFormatを指定するか、Yassonの設定をするかのどちらかを指示していますね。

このメッセージですね。

https://github.com/eclipse-ee4j/yasson/blob/3.0.2/src/main/resources/yasson-messages.properties#L51-L52

パースに失敗していたのはこちら。

https://github.com/eclipse-ee4j/yasson/blob/3.0.2/src/main/java/org/eclipse/yasson/internal/deserializer/types/LocalDateDeserializer.java#L34-L37

今回は@JsonbDateFormatを使うことにします。まずはリクエストの方でフォーマットを指定してみます。

import jakarta.json.bind.annotation.JsonbDateFormat;

public record DateRequest(@JsonbDateFormat("uuuu/MM/dd") LocalDate date) {
}

確認。

$ curl -XPOST -H 'Content-Type: application/json' localhost:8080/date -d '{"date": "2024/10/16"}'
{"date":"2024-10-17"}

OKですね。

レスポンスの方も変更してみます。

import jakarta.json.bind.annotation.JsonbDateFormat;

public record DateResponse(@JsonbDateFormat("uuuu/MM/dd") LocalDate date) {
}

反映されました。

$ curl -XPOST -H 'Content-Type: application/json' localhost:8080/date -d '{"date": "2024/10/16"}'
{"date":"2024/10/17"}

Jacksonに切り替えてみる

最後にJacksonに切り替えてみます。Jacksonのアノテーションを使うと切り替わるはずなので、依存関係を追加。

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.15.4</version>
            <scope>provided</scope>
        </dependency>

リクエストとレスポンスをこのように変更。

import com.fasterxml.jackson.annotation.JsonFormat;

public record DateRequest(@JsonFormat(pattern = "uuuu/MM/dd") LocalDate date) {
}



import com.fasterxml.jackson.annotation.JsonFormat;

public record DateResponse(@JsonFormat(pattern = "uuuu/MM/dd") LocalDate date) {
}

アノテーションを使っただけなので、やっぱり警告されます。

00:26:44,712 WARN  [org.jboss.as.jaxrs] (MSC service thread 1-7) WFLYRS0018: Explicit usage of Jackson annotation in a Jakarta RESTful Web Services deployment; the system will disable Jakarta JSON Binding processing for the current deployment. Consider setting the 'resteasy.preferJacksonOverJsonB' property to 'false' to restore Jakarta JSON Binding.

確認。

$ curl -XPOST -H 'Content-Type: application/json' localhost:8080/date -d '{"date": "2024/10/16"}'
Not able to deserialize data provided.

モジュールを追加しないといけないんでした…。

00:27:06,965 ERROR [org.jboss.resteasy.plugins.providers.jackson] (default task-1) RESTEASY-JACKSON000100: Not able to deserialize data provided: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type `java.time.LocalDate` not supported by default: add Module "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" to enable handling

依存関係をこうして

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.15.4</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.15.4</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
            <version>2.15.4</version>
            <scope>provided</scope>
        </dependency>

こちらを追加。

src/main/java/org/littlewings/wildfly/jaxrs/ObjectMapperProvider.java

package org.littlewings.wildfly.jaxrs;

import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.ext.ContextResolver;
import jakarta.ws.rs.ext.Provider;

@Provider
@Produces(MediaType.APPLICATION_JSON)
public class ObjectMapperProvider implements ContextResolver<ObjectMapper> {
    @Override
    public ObjectMapper getContext(Class<?> type) {
        return new ObjectMapper().findAndRegisterModules();
    }
}

確認。

$ curl -XPOST -H 'Content-Type: application/json' localhost:8080/date -d '{"date": "2024/10/16"}'
{"date":"2024/10/17"}

OKですね。

おわりに

WildFlyのRESTEasyがJSONを扱う時は、デフォルトでJSON-Bになっていることを確認しました。

Jacksonは便利なのですが、それ以外の方法も押さえておいた方がいいかなとは思っていたので、よい機会でしょう。
ちょっとずつ慣れていこうと思います。