Skip to content

JAXB @XmlJavaTypeAdapter not working in conjunction with java.util.Optional #588

@toellrich

Description

@toellrich

@XmlJavaTypeAdapter doesn't seem to work in conjunction with JDK's Optional class. I have written a unit test in order to replicate this issue:

package org.example;

import static org.example.OptionalWithTypeAdapterTest.TestEnum.A;
import static org.junit.jupiter.api.Assertions.assertEquals;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.module.jakarta.xmlbind.JakartaXmlBindAnnotationModule;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.adapters.XmlAdapter;
import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.util.Optional;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

public class OptionalWithTypeAdapterTest {

  public enum TestEnum {
    A
  }

  public static class TestEnumAdapter extends XmlAdapter<String, TestEnum> {

    @Override
    public TestEnum unmarshal(String aString) {
      return TestEnum.valueOf(aString.toUpperCase());
    }

    @Override
    public String marshal(TestEnum testEnum) {
      return testEnum.name().toLowerCase();
    }
  }

  public record TestObject1(
      @XmlElement @XmlJavaTypeAdapter(TestEnumAdapter.class) TestEnum testEnum) {
  }

  public record TestObject2(
      @XmlElement Optional<TestEnum> testEnum) {
  }

  public record TestObject3(
      @XmlElement @XmlJavaTypeAdapter(TestEnumAdapter.class) Optional<TestEnum> testEnum) {
  }

  private XmlMapper xmlMapper;

  @BeforeEach
  public void setUp() {
    xmlMapper = new XmlMapper();
    xmlMapper.registerModule(new JakartaXmlBindAnnotationModule());
    xmlMapper.registerModule(new Jdk8Module());
  }

  @Test
  public void writeValueAsStringWithTypeAdapter() throws JsonProcessingException {
    TestObject1 testObject = new TestObject1(A);
    String actual = xmlMapper.writeValueAsString(testObject);
    String expected = "<TestObject1><testEnum>a</testEnum></TestObject1>";
    assertEquals(expected, actual);
  }

  @Test
  public void writeValueAsStringWithOptional() throws JsonProcessingException {
    TestObject2 testObject = new TestObject2(Optional.of(A));
    String actual = xmlMapper.writeValueAsString(testObject);
    String expected = "<TestObject2><testEnum>A</testEnum></TestObject2>";
    assertEquals(expected, actual);
  }

  /**
   * This test fails
   */
  @Disabled
  @Test
  public void writeValueAsStringWithTypeAdapterAndOptional() throws JsonProcessingException {
    TestObject3 testObject = new TestObject3(Optional.of(A));
    String actual = xmlMapper.writeValueAsString(testObject);
    String expected = "<TestObject3><testEnum>a</testEnum></TestObject3>";
    assertEquals(expected, actual);
  }
}

This doesn't seem to have anything to do with the fact that I'm using records. I tried the same test with plain classes and the third test still fails. I'm using java 17 and jackson 2.15.0-rc1.

test.zip

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions