[BUG] 2.0.61版本、jdk8环境下序列化报错
- Dominant language
- Java
- Stars
- 4.4k
- Forks
- 613
- Avg merge
- 1d 22h
- Merged PRs (30d)
- 6
Description
### 问题描述
1、使用开源的datax进行数据迁移,将依赖的fastjson升级到2.0.61,执行任务时发生报错
2、fastjson调整为2.0.60,功能正常
### 环境信息
- OS信息: [e.g.:windows11]
- JDK信息: [e.g.:Openjdk 1.8.0_312]
- 版本信息:[e.g.:Fastjson2 2.0.61]
### 相关日志输出
java.lang.RuntimeException: Failed to create lambda for method: public
java.util.HashSet com.alibaba.datax.common.util.Configuration.getSecretKeyPathSet()
atcom.alibaba.fastjson2.introspect.PropertyAccessorFactoryLambda.getObject(PropertyAccessorFactoryLambda.java:513)
at com.alibaba.fastjson2.introspect.PropertyAccessorFactoryLambda.create(PropertyAccessorFactoryLambda.java:310)
at com.alibaba.fastjson2.introspect.PropertyAccessorFactory.create(PropertyAccessorFactory.java:2806)
at com.alibaba.fastjson2.introspect.PropertyAccessorFactory.create(PropertyAccessorFactory.java:2791)
at com.alibaba.fastjson2.writer.FieldWriter.createPropertyAccessor(FieldWriter.java:222)
at com.alibaba.fastjson2.writer.FieldWriter.(FieldWriter.java:194)
at com.alibaba.fastjson2.writer.FieldWriterObject.(FieldWriterObject.java:51)
at com.alibaba.fastjson2.writer.ObjectWriterCreator.createFieldWriter(ObjectWriterCreator.java:1449)
at com.alibaba.fastjson2.writer.ObjectWriterCreatorASM.lambda$createObjectWriter$1(ObjectWriterCreatorASM.java:338)
at com.alibaba.fastjson2.util.BeanUtils.getters(BeanUtils.java:1152)
at com.alibaba.fastjson2.writer.ObjectWriterCreatorASM.createObjectWriter(ObjectWriterCreatorASM.java:244)
at com.alibaba.fastjson2.writer.ObjectWriterProvider.getObjectWriterInternal(ObjectWriterProvider.java:720)
at com.alibaba.fastjson2.writer.ObjectWriterProvider.getObjectWriter(ObjectWriterProvider.java:582)
at com.alibaba.fastjson2.JSONWriter$Context.getObjectWriter(JSONWriter.java:3499)
at com.alibaba.fastjson2.writer.ObjectWriterImplList.write(ObjectWriterImplList.java:348)
at com.alibaba.fastjson2.JSON.toJSONString(JSON.java:3233)
at com.alibaba.datax.core.taskgroup.TaskGroupContainer.start(TaskGroupContainer.java:127)
at com.alibaba.datax.core.Engine.start(Engine.java:86)
at com.alibaba.datax.core.Engine.entry(Engine.java:168)
#### 附加信息
package com.alibaba.datax.common.util;
import com.alibaba.datax.common.exception.CommonErrorCode;
import com.alibaba.datax.common.exception.DataXException;
import com.alibaba.datax.common.spi.ErrorCode;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.CharUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import java.io.*;
import java.util.*;
/**
* Configuration 提供多级JSON配置信息无损存储
*
*
* 实例代码:
*
* 获取job的配置信息
* Configuration configuration = Configuration.from(new File("Config.json"));
* String jobContainerClass =
* configuration.getString("core.container.job.class");
*
*
* 设置多级List
* configuration.set("job.reader.parameter.jdbcUrl", Arrays.asList(new String[]
* {"jdbc", "jdbc"}));
*
*
*
*
* 合并Configuration:
* configuration.merge(another);
*
*
*
*
*
*
* Configuration 存在两种较好地实现方式
* 第一种是将JSON配置信息中所有的Key全部打平,用a.b.c的级联方式作为Map的Key,内部使用一个Map保存信息
* 第二种是将JSON的对象直接使用结构化树形结构保存
*
* 目前使用的第二种实现方式,使用第一种的问题在于:
* 1. 插入新对象,比较难处理,例如a.b.c="bazhen",此时如果需要插入a="bazhen",也即是根目录下第一层所有类型全部要废弃
* ,使用"bazhen"作为value,第一种方式使用字符串表示key,难以处理这类问题。
* 2. 返回树形结构,例如 a.b.c.d = "bazhen",如果返回"a"下的所有元素,实际上是一个Map,需要合并处理
* 3. 输出JSON,将上述对象转为JSON,要把上述Map的多级key转为树形结构,并输出为JSON
*/
public class Configuration {
/**
* 对于加密的keyPath,需要记录下来
* 为的是后面分布式情况下将该值加密后抛到DataXServer中
*/
private Set secretKeyPathSet =
new HashSet();
private Object root = null;
/**
* 初始化空白的Configuration
*/
public static Configuration newDefault() {
return Configuration.from("{}");
}
/**
* 从JSON字符串加载Configuration
*/
public static Configuration from(String json) {
json = StrUtil.replaceVariable(json);
checkJSON(json);
try {
return new Configuration(json);
} catch (Exception e) {
throw DataXException.asDataXException(CommonErrorCode.CONFIG_ERROR,
e);
}
}
/**
* 从包括json的File对象加载Configuration
*/
public static Configuration from(File file) {
try {
return Configuration.from(IOUtils
.toString(new FileInputStream(file)));
} catch (FileNotFoundException e) {
throw DataXException.asDataXException(CommonErrorCode.CONFIG_ERROR,
String.format("配置信息错误,您提供的配置文件[%s]不存在. 请检查您的配置文件.", file.getAbsolutePath()));
} catch (IOException e) {
throw DataXException.asDataXException(
CommonErrorCode.CONFIG_ERROR,
String.format("配置信息错误. 您提供配置文件[%s]读取失败,错误原因: %s. 请检查您的配置文件的权限设置.",
file.getAbsolutePath(), e));
}
}
/**
* 从包括json的InputStream对象加载Configuration
*/
public static Configuration from(InputStream is) {
try {
return Configuration.from(IOUtils.toString(is));
} catch (IOException e) {
throw DataXException.asDataXException(CommonErrorCode.CONFIG_ERROR,
String.format("请检查您的配置文件. 您提供的配置文件读取失败,错误原因: %s. 请检查您的配置文件的权限设置.", e));
}
}
/**
* 从Map对象加载Configuration
*/
public static Configuration from(final Map object) {
return Configuration.from(Configuration.toJSONString(object));
}
/**
* 从List对象加载Configuration
*/
public static Configuration from(final List object) {
return Configuration.from(Configuration.toJSONString(object));
}
public String getNecessaryValue(String key, ErrorCode errorCode) {
String value = this.getString(key, null);
if (StringUtils.isBlank(value)) {
throw DataXException.asDataXException(errorCode,
String.format("您提供配置文件有误,[%s]是必填参数,不允许为空或者留白 .", key));
}
return value;
}
public String getUnnecessaryValue(String key,String defaultValue,ErrorCode errorCode) {
String value = this.getString(key, defaultValue);
if (StringUtils.isBlank(value)) {
value = defaultValue;
}
return value;
}
public Boolean getNecessaryBool(String key, ErrorCode errorCode) {
Boolean value = this.getBool(key);
if (value == null) {
throw DataXException.asDataXException(errorCode,
String.format("您提供配置文件有误,[%s]是必填参数,不允许为空或者留白 .", key));
}
return value;
}
/**
* 根据用户提供的json path,寻址具体的对象。
*
*
*
* NOTE: 目前仅支持Map以及List下标寻址, 例如:
*
*
*
* 对于如下JSON
*
* {"a": {"b": {"c": [0,1,2,3]}}}
*
* config.get("") 返回整个Map
* config.get("a") 返回a下属整个Map
* config.get("a.b.c") 返回c对应的数组List
* config.get("a.b.c[0]") 返回数字0
*
* @return Java表示的JSON对象,如果path不存在或者对象不存在,均返回null。
*/
public Object get(final String path) {
this.checkPath(path);
try {
return this.findObject(path);
} catch (Exception e) {
return null;
}
}
/**
* 用户指定部分path,获取Configuration的子集
*
*
* 如果path获取的路径或者对象不存在,返回null
*/
public Configuration getConfiguration(final String path) {
Object object = this.get(path);
if (null == object) {
return null;
}
return Configuration.from(Configuration.toJSONString(object));
}
/**
* 根据用户提供的json path,寻址String对象
*
* @return String对象,如果path不存在或者String不存在,返回null
*/
public String getString(final String path) {
Object string = this.get(path);
if (null == string) {
return null;
}
return String.valueOf(string);
}
/**
* 根据用户提供的json path,寻址String对象,如果对象不存在,返回默认字符串
*
* @return String对象,如果path不存在或者String不存在,返回默认字符串
*/
public String getString(final String path, final String defaultValue) {
String result = this.getString(path);
if (null == result) {
return defaultValue;
}
return result;
}
/**
* 根据用户提供的json path,寻址Character对象
*
* @return Character对象,如果path不存在或者Character不存在,返回null
*/
public Character getChar(final String path) {
String result = this.getString(path);
if (null == result) {
return null;
}
try {
return CharUtils.toChar(result);
} catch (Exception e) {
throw DataXException.asDataXException(
CommonErrorCode.CONFIG_ERROR,
String.format("任务读取配置文件出错. 因为配置文件路径[%s] 值非法,期望是字符类型: %s. 请检查您的配置并作出修改.", path,
e.getMessage()));
}
}
/**
* 根据用户提供的json path,寻址Boolean对象,如果对象不存在,返回默认Character对象
*
* @return Character对象,如果path不存在或者Character不存在,返回默认Character对象
*/
public Character getChar(final String path, char defaultValue) {
Character result = this.getChar(path);
if (null == result) {
return defaultValue;
}
return result;
}
/**
* 根据用户提供的json path,寻址Boolean对象
*
* @return Boolean对象,如果path值非true,false ,将报错.特别注意:当 path 不存在时,会返回:null.
*/
public Boolean getBool(final String path) {
String result = this.getString(path);
if (null == result) {
return null;
} else if ("true".equalsIgnoreCase(result)) {
return Boolean.TRUE;
} else if ("false".equalsIgnoreCase(result)) {
return Boolean.FALSE;
} else {
throw DataXException.asDataXException(CommonErrorCode.CONFIG_ERROR,
String.format("您提供的配置信息有误,因为从[%s]获取的值[%s]无法转换为bool类型. 请检查源表的配置并且做出相应的修改.",
path, result));
}
}
/**
* 根据用户提供的json path,寻址Boolean对象,如果对象不存在,返回默认Boolean对象
*
* @return Boolean对象,如果path不存在或者Boolean不存在,返回默认Boolean对象
*/
public Boolean getBool(final String path, boolean defaultValue) {
Boolean result = this.getBool(path);
if (null == result) {
return defaultValue;
}
return result;
}
/**
* 根据用户提供的json path,寻址Integer对象
*
* @return Integer对象,如果path不存在或者Integer不存在,返回null
*/
public Integer getInt(final String path) {
String result = this.getString(path);
if (null == result) {
return null;
}
try {
return Integer.valueOf(result);
} catch (Exception e) {
throw DataXException.asDataXException(
CommonErrorCode.CONFIG_ERROR,
String.format("任务读取配置文件出错. 配置文件路径[%s] 值非法, 期望是整数类型: %s. 请检查您的配置并作出修改.", path,
e.getMessage()));
}
}
/**
* 根据用户提供的json path,寻址Integer对象,如果对象不存在,返回默认Integer对象
*
* @return Integer对象,如果path不存在或者Integer不存在,返回默认Integer对象
*/
public Integer getInt(final String path, int defaultValue) {
Integer object = this.getInt(path);
if (null == object) {
return defaultValue;
}
return object;
}
/**
* 根据用户提供的json path,寻址Long对象
*
* @return Long对象,如果path不存在或者Long不存在,返回null
*/
public Long getLong(final String path) {
String result = this.getString(path);
if (StringUtils.isBlank(result)) {
return null;
}
try {
return Long.valueOf(result);
} catch (Exception e) {
throw DataXException.asDataXException(
CommonErrorCode.CONFIG_ERROR,
String.format("任务读取配置文件出错. 配置文件路径[%s] 值非法, 期望是整数类型: %s. 请检查您的配置并作出修改.", path,
e.getMessage()));
}
}
/**
* 根据用户提供的json path,寻址Long对象,如果对象不存在,返回默认Long对象
*
* @return Long对象,如果path不存在或者Integer不存在,返回默认Long对象
*/
public Long getLong(final String path, long defaultValue) {
Long result = this.getLong(path);
if (null == result) {
return defaultValue;
}
return result;
}
/**
* 根据用户提供的json path,寻址Double对象
*
* @return Double对象,如果path不存在或者Double不存在,返回null
*/
public Double getDouble(final String path) {
String result = this.getString(path);
if (StringUtils.isBlank(result)) {
return null;
}
try {
return Double.valueOf(result);
} catch (Exception e) {
throw DataXException.asDataXException(
CommonErrorCode.CONFIG_ERROR,
String.format("任务读取配置文件出错. 配置文件路径[%s] 值非法, 期望是浮点类型: %s. 请检查您的配置并作出修改.", path,
e.getMessage()));
}
}
/**
* 根据用户提供的json path,寻址Double对象,如果对象不存在,返回默认Double对象
*
* @return Double对象,如果path不存在或者Double不存在,返回默认Double对象
*/
public Double getDouble(final String path, double defaultValue) {
Double result = this.getDouble(path);
if (null == result) {
return defaultValue;
}
return result;
}
/**
* 根据用户提供的json path,寻址List对象,如果对象不存在,返回null
*/
@SuppressWarnings("unchecked")
public List getList(final String path) {
List list = this.get(path, List.class);
if (null == list) {
return null;
}
return list;
}
public List getListWithJson(final String path, Class t) {
Object object = this.get(path, List.class);
if (null == object) {
return null;
}
return JSON.parseArray(JSON.toJSONString(object),t);
}
/**
* 根据用户提供的json path,寻址List对象,如果对象不存在,返回null
*/
@SuppressWarnings("unchecked")
public List getList(final String path, Class t) {
Object object = this.get(path, List.class);
if (null == object) {
return null;
}
List result = new ArrayList();
List origin = (List) object;
for (final Object each : origin) {
result.add((T) each);
}
return result;
}
/**
* 根据用户提供的json path,寻址List对象,如果对象不存在,返回默认List
*/
@SuppressWarnings("unchecked")
public List getList(final String path,
final List defaultList) {
Object object = this.getList(path);
if (null == object) {
return defaultList;
}
return (List) object;
}
/**
* 根据用户提供的json path,寻址List对象,如果对象不存在,返回默认List
*/
public List getList(final String path, final List defaultList,
Class t) {
List list = this.getList(path, t);
if (null == list) {
return defaultList;
}
return list;
}
/**
* 根据用户提供的json path,寻址包含Configuration的List,如果对象不存在,返回默认null
*/
public List getListConfiguration(final String path) {
List lists = getList(path);
if (lists == null) {
return null;
}
List result = new ArrayList();
for (final Object object : lists) {
result.add(Configuration.from(Configuration.toJSONString(object)));
}
return result;
}
/**
* 根据用户提供的json path,寻址Map对象,如果对象不存在,返回null
*/
@SuppressWarnings("unchecked")
public Map getMap(final String path) {
Map result = this.get(path, Map.class);
if (null == result) {
return null;
}
return result;
}
/**
* 根据用户提供的json path,寻址Map对象,如果对象不存在,返回null;
*/
@SuppressWarnings("unchecked")
public Map getMap(final String path, Class t) {
Map map = this.get(path, Map.class);
if (null == map) {
return null;
}
Map result = new HashMap();
for (final String key : map.keySet()) {
result.put(key, (T) map.get(key));
}
return result;
}
/**
* 根据用户提供的json path,寻址Map对象,如果对象不存在,返回默认map
*/
@SuppressWarnings("unchecked")
public Map getMap(final String path,
final Map defaultMap) {
Object object = this.getMap(path);
if (null == object) {
return defaultMap;
}
return (Map) object;
}
/**
* 根据用户提供的json path,寻址Map对象,如果对象不存在,返回默认map
*/
public Map getMap(final String path,
final Map defaultMap, Class t) {
Map result = getMap(path, t);
if (null == result) {
return defaultMap;
}
return result;
}
/**
* 根据用户提供的json path,寻址包含Configuration的Map,如果对象不存在,返回默认null
*/
@SuppressWarnings("unchecked")
public Map getMapConfiguration(final String path) {
Map map = this.get(path, Map.class);
if (null == map) {
return null;
}
Map result = new HashMap();
for (final String key : map.keySet()) {
result.put(key, Configuration.from(Configuration.toJSONString(map
.get(key))));
}
return result;
}
/**
* 根据用户提供的json path,寻址具体的对象,并转为用户提供的类型
*
*
*
* NOTE: 目前仅支持Map以及List下标寻址, 例如:
*
*
*
* 对于如下JSON
*
* {"a": {"b": {"c": [0,1,2,3]}}}
*
* config.get("") 返回整个Map
* config.get("a") 返回a下属整个Map
* config.get("a.b.c") 返回c对应的数组List
* config.get("a.b.c[0]") 返回数字0
*
* @return Java表示的JSON对象,如果转型失败,将抛出异常
*/
@SuppressWarnings("unchecked")
public T get(final String path, Class clazz) {
this.checkPath(path);
return (T) this.get(path);
}
/**
* 格式化Configuration输出
*/
public String beautify() {
return JSON.toJSONString(this.getInternal(),
JSONWriter.Feature.PrettyFormat);
}
/**
* 根据用户提供的json path,插入指定对象,并返回之前存在的对象(如果存在)
*
*
*
* 目前仅支持.以及数组下标寻址, 例如:
*
*
*
* config.set("a.b.c[3]", object);
*
*
* 对于插入对象,Configuration不做任何限制,但是请务必保证该对象是简单对象(包括Map、List),不要使用自定义对象,否则后续对于JSON序列化等情况会出现未定义行为。
*
* @param path
* JSON path对象
* @param object
* 需要插入的对象
* @return Java表示的JSON对象
*/
public Object set(final String path, final Object object) {
checkPath(path);
Object result = this.get(path);
setObject(path, extractConfiguration(object));
return result;
}
/**
* 获取Configuration下所有叶子节点的key
*
*
*
* 对于
*
* {"a": {"b": {"c": [0,1,2,3]}}, "x": "y"}
*
* 下属的key包括: a.b.c[0],a.b.c[1],a.b.c[2],a.b.c[3],x
*/
public Set getKeys() {
Set collect = new HashSet();
this.getKeysRecursive(this.getInternal(), "", collect);
return collect;
}
/**
* 删除path对应的值,如果path不存在,将抛出异常。
*/
public Object remove(final String path) {
final Object result = this.get(path);
if (null == result) {
throw DataXException.asDataXException(
CommonErrorCode.RUNTIME_ERROR,
String.format("配置文件对应Key[%s]并不存在,该情况是代码编程错误. 请联系DataX团队的同学.", path));
}
this.set(path, null);
return result;
}
/**
* 合并其他Configuration,并修改两者冲突的KV配置
*
* @param another
* 合并加入的第三方Configuration
* @param updateWhenConflict
* 当合并双方出现KV冲突时候,选择更新当前KV,或者忽略该KV
* @return 返回合并后对象
*/
public Configuration merge(final Configuration another,
boolean updateWhenConflict) {
Set keys = another.getKeys();
for (final String key : keys) {
// 如果使用更新策略,凡是another存在的key,均需要更新
if (updateWhenConflict) {
this.set(key, another.get(key));
continue;
}
// 使用忽略策略,只有another Configuration存在但是当前Configuration不存在的key,才需要更新
boolean isCurrentExists = this.get(key) != null;
if (isCurrentExists) {
continue;
}
this.set(key, another.get(key));
}
return this;
}
@Override
public String toString() {
return this.toJSON();
}
/**
* 将Configuration作为JSON输出
*/
public String toJSON() {
return Configuration.toJSONString(this.getInternal());
}
/**
* 拷贝当前Configuration,注意,这里使用了深拷贝,避免冲突
*/
public Configuration clone() {
Configuration config = Configuration
.from(Configuration.toJSONString(this.getInternal()));
config.addSecretKeyPath(this.secretKeyPathSet);
return config;
}
/**
* 按照configuration要求格式的path
* 比如:
* a.b.c
* a.b[2].c
* @param path
*/
public void addSecretKeyPath(String path) {
if(StringUtils.isNotBlank(path)) {
this.secretKeyPathSet.add(path);
}
}
public void addSecretKeyPath(Set pathSet) {
if(pathSet != null) {
this.secretKeyPathSet.addAll(pathSet);
}
}
public void setSecretKeyPathSet(Set keyPathSet) {
if(keyPathSet != null) {
this.secretKeyPathSet = keyPathSet;
}
}
public boolean isSecretPath(String path) {
return this.secretKeyPathSet.contains(path);
}
@SuppressWarnings("unchecked")
void getKeysRecursive(final Object current, String path, Set collect) {
boolean isRegularElement = !(current instanceof Map || current instanceof List);
if (isRegularElement) {
collect.add(path);
return;
}
boolean isMap = current instanceof Map;
if (isMap) {
Map mapping = ((Map) current);
for (final String key : mapping.keySet()) {
if (StringUtils.isBlank(path)) {
getKeysRecursive(mapping.get(key), key.trim(), collect);
} else {
getKeysRecursive(mapping.get(key), path + "." + key.trim(),
collect);
}
}
return;
}
boolean isList = current instanceof List;
if (isList) {
List lists = (List) current;
for (int i = 0; i < lists.size(); i++) {
getKeysRecursive(lists.get(i), path + String.format("[%d]", i),
collect);
}
return;
}
return;
}
public Object getInternal() {
return this.root;
}
private void setObject(final String path, final Object object) {
Object newRoot = setObjectRecursive(this.root, split2List(path), 0,
object);
if (isSuitForRoot(newRoot)) {
this.root = newRoot;
return;
}
throw DataXException.asDataXException(CommonErrorCode.RUNTIME_ERROR,
String.format("值[%s]无法适配您提供[%s], 该异常代表系统编程错误, 请联系DataX开发团队!",
ToStringBuilder.reflectionToString(object), path));
}
@SuppressWarnings("unchecked")
private Object extractConfiguration(final Object object) {
if (object instanceof Configuration) {
return extractFromConfiguration(object);
}
if (object instanceof List) {
List result = new ArrayList();
for (final Object each : (List) object) {
result.add(extractFromConfiguration(each));
}
return result;
}
if (object instanceof Map) {
Map result = new HashMap();
for (final String key : ((Map) object).keySet()) {
result.put(key,
extractFromConfiguration(((Map) object)
.get(key)));
}
return result;
}
return object;
}
private Object extractFromConfiguration(final Object object) {
if (object instanceof Configuration) {
return ((Configuration) object).getInternal();
}
return object;
}
Object buildObject(final List paths, final Object object) {
if (null == paths) {
throw DataXException.asDataXException(
CommonErrorCode.RUNTIME_ERROR,
"Path不能为null,该异常代表系统编程错误, 请联系DataX开发团队 !");
}
if (1 == paths.size() && StringUtils.isBlank(paths.get(0))) {
return object;
}
Object child = object;
for (int i = paths.size() - 1; i >= 0; i--) {
String path = paths.get(i);
if (isPathMap(path)) {
Map mapping = new HashMap();
mapping.put(path, child);
child = mapping;
continue;
}
if (isPathList(path)) {
List lists = new ArrayList(
this.getIndex(path) + 1);
expand(lists, this.getIndex(path) + 1);
lists.set(this.getIndex(path), child);
child = lists;
continue;
}
throw DataXException.asDataXException(
CommonErrorCode.RUNTIME_ERROR, String.format(
"路径[%s]出现非法值类型[%s],该异常代表系统编程错误, 请联系DataX开发团队! .",
StringUtils.join(paths, "."), path));
}
return child;
}
@SuppressWarnings("unchecked")
Object setObjectRecursive(Object current, final List paths,
int index, final Object value) {
// 如果是已经超出path,我们就返回value即可,作为最底层叶子节点
boolean isLastIndex = index == paths.size();
if (isLastIndex) {
return value;
}
String path = paths.get(index).trim();
boolean isNeedMap = isPathMap(path);
if (isNeedMap) {
Map mapping;
// 当前不是map,因此全部替换为map,并返回新建的map对象
boolean isCurrentMap = current instanceof Map;
if (!isCurrentMap) {
mapping = new HashMap();
mapping.put(
path,
buildObject(paths.subList(index + 1, paths.size()),
value));
return mapping;
}
// 当前是map,但是没有对应的key,也就是我们需要新建对象插入该map,并返回该map
mapping = ((Map) current);
boolean hasSameKey = mapping.containsKey(path);
if (!hasSameKey) {
mapping.put(
path,
buildObject(paths.subList(index + 1, paths.size()),
value));
return mapping;
}
// 当前是map,而且还竟然存在这个值,好吧,继续递归遍历
current = mapping.get(path);
mapping.put(path,
setObjectRecursive(current, paths, index + 1, value));
return mapping;
}
boolean isNeedList = isPathList(path);
if (isNeedList) {
List lists;
int listIndexer = getIndex(path);
// 当前是list,直接新建并返回即可
boolean isCurrentList = current instanceof List;
if (!isCurrentList) {
lists = expand(new ArrayList(), listIndexer + 1);
lists.set(
listIndexer,
buildObject(paths.subList(index + 1, paths.size()),
value));
return lists;
}
// 当前是list,但是对应的indexer是没有具体的值,也就是我们新建对象然后插入到该list,并返回该List
lists = (List) current;
lists = expand(lists, listIndexer + 1);
boolean hasSameIndex = lists.get(listIndexer) != null;
if (!hasSameIndex) {
lists.set(
listIndexer,
buildObject(paths.subList(index + 1, paths.size()),
value));
return lists;
}
// 当前是list,并且存在对应的index,没有办法继续递归寻找
current = lists.get(listIndexer);
lists.set(listIndexer,
setObjectRecursive(current, paths, index + 1, value));
return lists;
}
throw DataXException.asDataXException(CommonErrorCode.RUNTIME_ERROR,
"该异常代表系统编程错误, 请联系DataX开发团队 !");
}
private Object findObject(final String path) {
boolean isRootQuery = StringUtils.isBlank(path);
if (isRootQuery) {
return this.root;
}
Object target = this.root;
for (final String each : split2List(path)) {
if (isPathMap(each)) {
target = findObjectInMap(target, each);
continue;
} else {
target = findObjectInList(target, each);
continue;
}
}
return target;
}
@SuppressWarnings("unchecked")
private Object findObjectInMap(final Object target, final String index) {
boolean isMap = (target instanceof Map);
if (!isMap) {
throw new IllegalArgumentException(String.format(
"您提供的配置文件有误. 路径[%s]需要配置Json格式的Map对象,但该节点发现实际类型是[%s]. 请检查您的配置并作出修改.",
index, target.getClass().toString()));
}
Object result = ((Map) target).get(index);
if (null == result) {
throw new IllegalArgumentException(String.format(
"您提供的配置文件有误. 路径[%s]值为null,datax无法识别该配置. 请检查您的配置并作出修改.", index));
}
return result;
}
@SuppressWarnings({ "unchecked" })
private Object findObjectInList(final Object target, final String each) {
boolean isList = (target instanceof List);
if (!isList) {
throw new IllegalArgumentException(String.format(
"您提供的配置文件有误. 路径[%s]需要配置Json格式的Map对象,但该节点发现实际类型是[%s]. 请检查您的配置并作出修改.",
each, target.getClass().toString()));
}
String index = each.replace("[", "").replace("]", "");
if (!StringUtils.isNumeric(index)) {
throw new IllegalArgumentException(
String.format(
"系统编程错误,列表下标必须为数字类型,但该节点发现实际类型是[%s] ,该异常代表系统编程错误, 请联系DataX开发团队 !",
index));
}
return ((List) target).get(Integer.valueOf(index));
}
private List expand(List list, int size) {
int expand = size - list.size();
while (expand-- > 0) {
list.add(null);
}
return list;
}
private boolean isPathList(final String path) {
return path.contains("[") && path.contains("]");
}
private boolean isPathMap(final String path) {
return StringUtils.isNotBlank(path) && !isPathList(path);
}
private int getIndex(final String index) {
return Integer.valueOf(index.replace("[", "").replace("]", ""));
}
private boolean isSuitForRoot(final Object object) {
if (null != object && (object instanceof List || object instanceof Map)) {
return true;
}
return false;
}
private String split(final String path) {
return StringUtils.replace(path, "[", ".[");
}
private List split2List(final String path) {
return Arrays.asList(StringUtils.split(split(path), "."));
}
private void checkPath(final String path) {
if (null == path) {
throw new IllegalArgumentException(
"系统编程错误, 该异常代表系统编程错误, 请联系DataX开发团队!.");
}
for (final String each : StringUtils.split(".")) {
if (StringUtils.isBlank(each)) {
throw new IllegalArgumentException(String.format(
"系统编程错误, 路径[%s]不合法, 路径层次之间不能出现空白字符 .", path));
}
}
}
@SuppressWarnings("unused")
private String toJSONPath(final String path) {
return (StringUtils.isBlank(path) ? "$" : "$." + path).replace("$.[",
"$[");
}
private static void checkJSON(final String json) {
if (StringUtils.isBlank(json)) {
throw DataXException.asDataXException(CommonErrorCode.CONFIG_ERROR,
"配置信息错误. 因为您提供的配置信息不是合法的JSON格式, JSON不能为空白. 请按照标准json格式提供配置信息. ");
}
}
private Configuration(final String json) {
try {
this.root = JSON.parse(json);
} catch (Exception e) {
throw DataXException.asDataXException(CommonErrorCode.CONFIG_ERROR,
String.format("配置信息错误. 您提供的配置信息不是合法的JSON格式: %s . 请按照标准json格式提供配置信息. ", e.getMessage()));
}
}
private static String toJSONString(final Object object) {
return JSON.toJSONString(object);
}
public Set getSecretKeyPathSet() {
return secretKeyPathSet;
}
}
Contributor guide
Research direction
Start by reproducing the DataX serialization failure with fastjson2 2.0.61 on JDK 8, then read PropertyAccessorFactoryLambda.java around line 513 and the stack path through PropertyAccessorFactory and FieldWriter. Compare the behavior with 2.0.60 using Configuration.getSecretKeyPathSet(). Done means the reported serialization path no longer throws the lambda-creation RuntimeException on JDK 8.
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
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 45/100