使用@XmlJavaTypeAdapter序列化含Instant属性的TokenVO类时遭遇重复属性非法注解异常
嘿,我看你碰到的这个问题其实是JAXB的属性识别逻辑在搞鬼——它同时把类里的字段和对应的getter方法当成了同一个属性的两个独立定义,所以才抛出了重复属性的非法注解异常。咱们来一步步解决它:
问题根源
错误信息里明确指出created和expiration这两个属性各有重复定义,分别指向类的私有字段和对应的公共getter方法。这是因为JAXB默认采用混合访问模式:既会扫描字段,也会扫描getter/setter方法来识别属性。当你在字段上添加@XmlJavaTypeAdapter注解时,JAXB会把这个字段标记为一个属性;同时,对应的getter方法又会被它识别成另一个同名属性,两者冲突就触发了异常。
解决方案
这里有几种靠谱的解决方式,你可以根据自己的代码习惯选:
方案1:指定JAXB访问类型为字段(推荐)
在TokenVO类上添加@XmlAccessorType(XmlAccessType.FIELD)注解,告诉JAXB只通过字段来识别属性,忽略getter/setter方法。这样字段上的@XmlJavaTypeAdapter注解就能正常生效,不会和方法冲突。
修改后的TokenVO代码:
import java.io.Serializable; import java.time.Instant; import com.vitorbionic.integrationtests.controller.withxml.adapter.InstantAdapter; import jakarta.xml.bind.annotation.XmlAccessorType; import jakarta.xml.bind.annotation.XmlRootElement; import jakarta.xml.bind.annotation.XmlAccessType; import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter; @XmlRootElement(name = "TokenVO") @XmlAccessorType(XmlAccessType.FIELD) // 添加这个注解指定访问类型 public class TokenVO implements Serializable { private static final long serialVersionUID = 1L; private String username; private Boolean authenticated; @XmlJavaTypeAdapter(InstantAdapter.class) private Instant created; @XmlJavaTypeAdapter(InstantAdapter.class) private Instant expiration; private String accessToken; private String refreshToken; // 构造方法、Getters、Setters、Equals和hashCode }
方案2:把适配器注解移到getter方法上
如果你不想修改类的访问类型,也可以把@XmlJavaTypeAdapter注解从字段移到对应的getter方法上。这样JAXB会以getter方法作为属性的定义来源,不会和字段产生冲突。
示例代码片段:
// 字段上移除注解 private Instant created; // 在getter方法上添加注解 @XmlJavaTypeAdapter(InstantAdapter.class) public Instant getCreated() { return created; } // expiration字段同理 private Instant expiration; @XmlJavaTypeAdapter(InstantAdapter.class) public Instant getExpiration() { return expiration; }
方案3:用@XmlTransient标记字段(不推荐)
这种方式是在字段上添加@XmlTransient注解,让JAXB忽略这个字段,只识别getter方法。不过这种做法有点冗余,因为你本来就在用适配器注解,不如前两种方案整洁,所以只作为备选。
验证修改
修改完之后,再尝试序列化/反序列化TokenVO对象,应该就能正常处理Instant类型的属性,不会再抛出重复属性的异常了。
备注:内容来源于stack exchange,提问作者VitorBionic

