alibaba / alibaba/fastjson2

对 ObjectWriterCreatorASM 中 enumValueField == null 条件的疑问

Open
#1,347 2 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Java
Stars
4.4k
Forks
613
Avg merge
1d 22h
Merged PRs (30d)
6

Description

对下面代码块中的 enumValueField == null 条件判断有点疑问。

https://github.com/alibaba/fastjson2/blob/1ca0ab6f7e468cf8174ef048df3743665e9262bd/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreatorASM.java#L3399-L3419

我在使用 Dubbo 时,遇到一个枚举无法序列化问题,经过排查后发现,我的 enumValueField 得到了一个 `getValue()` 的 Method 对象,并且 `String[] enumAnnotationNames = BeanUtils.getEnumAnnotationNames(fieldClass);` 值为null,因此这个枚举最终使用了 `FieldWriterObject` 来处理

enumValueField 的 `getValue()` 拿到的是使用了 `@com.fasterxml.jackson.annotation.JsonValue` 注解的方法。

在我对此代码修改后进行本地覆盖运行项目时,一切正常,我修改后的代码如下,仅仅只是调整了条件判断,我改了后我项目中的枚举能够正常序列化,但是我不知道会不会有其他未知的负面问题:
```java
if (fieldClass.isEnum()) {
BeanInfo beanInfo = new BeanInfo();
provider.getBeanInfo(beanInfo, fieldClass);

boolean writeEnumAsJavaBean = beanInfo.writeEnumAsJavaBean;
if (!writeEnumAsJavaBean) {
ObjectWriter objectWriter = provider.cache.get(fieldClass);
if (objectWriter != null && !(objectWriter instanceof ObjectWriterImplEnum)) {
writeEnumAsJavaBean = true;
}
}

Member enumValueField = BeanUtils.getEnumValueField(fieldClass, provider);

if (!writeEnumAsJavaBean) {
if (enumValueField == null) {
String[] enumAnnotationNames = BeanUtils.getEnumAnnotationNames(fieldClass);
if (enumAnnotationNames == null) {
return new FieldWriterEnum(fieldName, ordinal, features, format, label, fieldType, (Class) fieldClass, field, null);
}
} else {
return new FieldWriterEnum(fieldName, ordinal, features, format, label, fieldType, (Class) fieldClass, field, null);
}
}
}
```

我的枚举相关类如下:
```java
@Data
@SuperBuilder
@EqualsAndHashCode(callSuper = true)
@FieldNameConstants
@NoArgsConstructor
@AllArgsConstructor
public class LoginAccountVo extends BaseEntityVo {
/**
* 主键
*/
private Long id;
/**
* 关联用户ID
*/
private Long userId;
/**
* 登录标识(系统规则):帐号(用户名、手机号、电子邮箱等)实际账号。
*/
private String identifier;
/**
* 登录标识(区分大小写):帐号(用户名、手机号、电子邮箱等)展示信息,用户实际输入内容
*/
private String identifierDisplay;
/**
* 登录凭据:密码
*/
private String credential;
/**
* 帐号类型:用户名、手机号、电子邮箱
*/
private LoginAccountType type;
/**
* 是否是帐内帐号
*/
private boolean local;
/**
* 最后一次登录IP
*/
private String lastLoginIp;
/**
* 最后一次登录时间
*/
private LocalDateTime lastLoginTime;

public void setType(final LoginAccountType type) {
this.type = type;
this.local = type.isLocal();
}
}
@Getter
@DictConverter
@AllArgsConstructor
public enum LoginAccountType implements DictEnum, IEnum, Serializable {
/**
* 用户名
*/
USERNAME(1, "用户名", true),
/**
* 手机号
*/
PHONE(2, "手机号", true),
/**
* 电子邮件
*/
EMAIL(3, "电子邮件", true)
;
private final Integer value;
private final String title;
// 是否是本地账号
private final boolean local;

@com.fasterxml.jackson.annotation.JsonCreator
public static LoginAccountType create(Integer value) {
return DictEnum.valueOf(values(), value);
}

/**
* 处理登录标识,统一转换大小写问题
*
* @param identifier 登录标识
* @return 结果
*/
public String handleIdentifier(final String identifier) {
if (!local) {
// 不是本地账号,第三方账号需要区分大小写
return identifier;
}
return identifier == null ? null : identifier.toLowerCase();
}
}
/**
* 数据字典枚举接口。系统字典枚举接口
*
* @author HouKunLin
*/
public interface DictEnum {
/**
* 通过枚举值从枚举列表中获取枚举对象
*
* @param values 枚举对象列表
* @param value 枚举值
* @param 枚举值类型
* @return 枚举对象
*/
static & DictEnum> E valueOf(E[] values, T value) {
for (final E enums : values) {
if (enums.getValue().equals(value)) {
return enums;
}
}
return null;
}

/**
* 字典值
*
* @return 字典值
*/
@com.fasterxml.jackson.annotation.JsonValue
T getValue();

/**
* 字典文本
*
* @return 字典文本
*/
String getTitle();

/**
* 判断字典值是否相等
*
* @param o 传入的值,可为当前的枚举对象
* @return 判断是否相等
*/
default boolean eq(Object o) {
return this == o || Objects.equal(o, getValue());
}
}

package com.baomidou.mybatisplus.annotation;
public interface IEnum {

/**
* 枚举数据库存储值
*/
T getValue();
}

```

Contributor guide

Open the contributing guide

Research direction

Start at core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreatorASM.java around lines 3399-3419, then inspect BeanUtils.getEnumValueField and getEnumAnnotationNames. Reproduce the case with an enum whose getValue method has Jackson's @JsonValue annotation and compare FieldWriterEnum with FieldWriterObject. Done means the intended enum serialization behavior is established and covered by a regression test.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.