JSON解析平铺获取数据

datax工程源码提供
Configuration 提供多级JSON配置信息无损存储


import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import com.zditec.system.utils.exception.CommonErrorCode;
import com.zditec.system.utils.exception.DataXException;
import com.zditec.system.utils.exception.ErrorCode;
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.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;

/**
 * Configuration 提供多级JSON配置信息无损存储 <br>
 * <br>
 * <p/>
 * 实例代码:<br>
 * <p/>
 * 获取job的配置信息<br>
 * Configuration configuration = Configuration.from(new File("Config.json")); <br>
 * String jobContainerClass =
 * configuration.getString("core.container.job.class"); <br>
 * <p/>
 * <br>
 * 设置多级List <br>
 * configuration.set("job.reader.parameter.jdbcUrl", Arrays.asList(new String[]
 * {"jdbc", "jdbc"}));
 * <p/>
 * <p/>
 * <br>
 * <br>
 * 合并Configuration: <br>
 * configuration.merge(another);
 * <p/>
 * <p/>
 * <br>
 * <br>
 * <br>
 * <p/>
 * Configuration 存在两种较好地实现方式<br>
 * 第一种是将JSON配置信息中所有的Key全部打平,用a.b.c的级联方式作为Map的Key,内部使用一个Map保存信息 <br>
 * 第二种是将JSON的对象直接使用结构化树形结构保存<br>
 * <p/>
 * 目前使用的第二种实现方式,使用第一种的问题在于: <br>
 * 1. 插入新对象,比较难处理,例如a.b.c="bazhen",此时如果需要插入a="bazhen",也即是根目录下第一层所有类型全部要废弃
 * ,使用"bazhen"作为value,第一种方式使用字符串表示key,难以处理这类问题。 <br>
 * 2. 返回树形结构,例如 a.b.c.d = "bazhen",如果返回"a"下的所有元素,实际上是一个Map,需要合并处理 <br>
 * 3. 输出JSON,将上述对象转为JSON,要把上述Map的多级key转为树形结构,并输出为JSON <br>
 */
public class Configuration {

    /**
     * 对于加密的keyPath,需要记录下来
     * 为的是后面分布式情况下将该值加密后抛到DataXServer中
     */
    private Set<String> 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<String, Object> object) {
		return Configuration.from(Configuration.toJSONString(object));
	}

	/**
	 * 从List对象加载Configuration
	 */
	public static Configuration from(final List<Object> 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,寻址具体的对象。
	 * <p/>
	 * <br>
	 * <p/>
	 * NOTE: 目前仅支持Map以及List下标寻址, 例如:
	 * <p/>
	 * <br />
	 * <p/>
	 * 对于如下JSON
	 * <p/>
	 * {"a": {"b": {"c": [0,1,2,3]}}}
	 * <p/>
	 * config.get("") 返回整个Map <br>
	 * config.get("a") 返回a下属整个Map <br>
	 * config.get("a.b.c") 返回c对应的数组List <br>
	 * 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的子集
	 * <p/>
	 * <br>
	 * 如果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<Object> getList(final String path) {
		List<Object> list = this.get(path, List.class);
		if (null == list) {
			return null;
		}
		return list;
	}

	public <T> List<T> getListWithJson(final String path, Class<T> 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 <T> List<T> getList(final String path, Class<T> t) {
		Object object = this.get(path, List.class);
		if (null == object) {
			return null;
		}

		List<T> result = new ArrayList<T>();

		List<Object> origin = (List<Object>) object;
		for (final Object each : origin) {
			result.add((T) each);
		}

		return result;
	}

	/**
	 * 根据用户提供的json path,寻址List对象,如果对象不存在,返回默认List
	 */
	@SuppressWarnings("unchecked")
	public List<Object> getList(final String path,
			final List<Object> defaultList) {
		Object object = this.getList(path);
		if (null == object) {
			return defaultList;
		}
		return (List<Object>) object;
	}

	/**
	 * 根据用户提供的json path,寻址List对象,如果对象不存在,返回默认List
	 */
	public <T> List<T> getList(final String path, final List<T> defaultList,
			Class<T> t) {
		List<T> list = this.getList(path, t);
		if (null == list) {
			return defaultList;
		}
		return list;
	}

	/**
	 * 根据用户提供的json path,寻址包含Configuration的List,如果对象不存在,返回默认null
	 */
	public List<Configuration> getListConfiguration(final String path) {
		List<Object> lists = getList(path);
		if (lists == null) {
			return null;
		}

		List<Configuration> result = new ArrayList<Configuration>();
		for (final Object object : lists) {
			result.add(Configuration.from(Configuration.toJSONString(object)));
		}
		return result;
	}

	/**
	 * 根据用户提供的json path,寻址Map对象,如果对象不存在,返回null
	 */
	@SuppressWarnings("unchecked")
	public Map<String, Object> getMap(final String path) {
		Map<String, Object> result = this.get(path, Map.class);
		if (null == result) {
			return null;
		}
		return result;
	}

	/**
	 * 根据用户提供的json path,寻址Map对象,如果对象不存在,返回null;
	 */
	@SuppressWarnings("unchecked")
	public <T> Map<String, T> getMap(final String path, Class<T> t) {
		Map<String, Object> map = this.get(path, Map.class);
		if (null == map) {
			return null;
		}

		Map<String, T> result = new HashMap<String, T>();
		for (final String key : map.keySet()) {
			result.put(key, (T) map.get(key));
		}

		return result;
	}

	/**
	 * 根据用户提供的json path,寻址Map对象,如果对象不存在,返回默认map
	 */
	@SuppressWarnings("unchecked")
	public Map<String, Object> getMap(final String path,
			final Map<String, Object> defaultMap) {
		Object object = this.getMap(path);
		if (null == object) {
			return defaultMap;
		}
		return (Map<String, Object>) object;
	}

	/**
	 * 根据用户提供的json path,寻址Map对象,如果对象不存在,返回默认map
	 */
	public <T> Map<String, T> getMap(final String path,
			final Map<String, T> defaultMap, Class<T> t) {
		Map<String, T> result = getMap(path, t);
		if (null == result) {
			return defaultMap;
		}
		return result;
	}

	/**
	 * 根据用户提供的json path,寻址包含Configuration的Map,如果对象不存在,返回默认null
	 */
	@SuppressWarnings("unchecked")
	public Map<String, Configuration> getMapConfiguration(final String path) {
		Map<String, Object> map = this.get(path, Map.class);
		if (null == map) {
			return null;
		}

		Map<String, Configuration> result = new HashMap<String, Configuration>();
		for (final String key : map.keySet()) {
			result.put(key, Configuration.from(Configuration.toJSONString(map
					.get(key))));
		}

		return result;
	}

	/**
	 * 根据用户提供的json path,寻址具体的对象,并转为用户提供的类型
	 * <p/>
	 * <br>
	 * <p/>
	 * NOTE: 目前仅支持Map以及List下标寻址, 例如:
	 * <p/>
	 * <br />
	 * <p/>
	 * 对于如下JSON
	 * <p/>
	 * {"a": {"b": {"c": [0,1,2,3]}}}
	 * <p/>
	 * config.get("") 返回整个Map <br>
	 * config.get("a") 返回a下属整个Map <br>
	 * config.get("a.b.c") 返回c对应的数组List <br>
	 * config.get("a.b.c[0]") 返回数字0
	 * 
	 * @return Java表示的JSON对象,如果转型失败,将抛出异常
	 */
	@SuppressWarnings("unchecked")
	public <T> T get(final String path, Class<T> clazz) {
		this.checkPath(path);
		return (T) this.get(path);
	}

	/**
	 * 格式化Configuration输出
	 */
	public String beautify() {
		return JSON.toJSONString(this.getInternal(),
				JSONWriter.Feature.PrettyFormat);
	}

	/**
	 * 根据用户提供的json path,插入指定对象,并返回之前存在的对象(如果存在)
	 * <p/>
	 * <br>
	 * <p/>
	 * 目前仅支持.以及数组下标寻址, 例如:
	 * <p/>
	 * <br />
	 * <p/>
	 * config.set("a.b.c[3]", object);
	 * <p/>
	 * <br>
	 * 对于插入对象,Configuration不做任何限制,但是请务必保证该对象是简单对象(包括Map<String,
	 * Object>、List<Object>),不要使用自定义对象,否则后续对于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
	 * <p/>
	 * <br>
	 * <p/>
	 * 对于<br>
	 * <p/>
	 * {"a": {"b": {"c": [0,1,2,3]}}, "x": "y"}
	 * <p/>
	 * 下属的key包括: a.b.c[0],a.b.c[1],a.b.c[2],a.b.c[3],x
	 */
	public Set<String> getKeys() {
		Set<String> collect = new HashSet<String>();
		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<String> 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<String> pathSet) {
        if(pathSet != null) {
            this.secretKeyPathSet.addAll(pathSet);
        }
    }

    public void setSecretKeyPathSet(Set<String> 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<String> collect) {
		boolean isRegularElement = !(current instanceof Map || current instanceof List);
		if (isRegularElement) {
			collect.add(path);
			return;
		}

		boolean isMap = current instanceof Map;
		if (isMap) {
			Map<String, Object> mapping = ((Map<String, Object>) 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<Object> lists = (List<Object>) 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<Object> result = new ArrayList<Object>();
			for (final Object each : (List<Object>) object) {
				result.add(extractFromConfiguration(each));
			}
			return result;
		}

		if (object instanceof Map) {
			Map<String, Object> result = new HashMap<String, Object>();
			for (final String key : ((Map<String, Object>) object).keySet()) {
				result.put(key,
						extractFromConfiguration(((Map<String, Object>) 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<String> 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<String, Object> mapping = new HashMap<String, Object>();
				mapping.put(path, child);
				child = mapping;
				continue;
			}

			if (isPathList(path)) {
				List<Object> lists = new ArrayList<Object>(
						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<String> 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<String, Object> mapping;

			// 当前不是map,因此全部替换为map,并返回新建的map对象
			boolean isCurrentMap = current instanceof Map;
			if (!isCurrentMap) {
				mapping = new HashMap<String, Object>();
				mapping.put(
						path,
						buildObject(paths.subList(index + 1, paths.size()),
								value));
				return mapping;
			}

			// 当前是map,但是没有对应的key,也就是我们需要新建对象插入该map,并返回该map
			mapping = ((Map<String, Object>) 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<Object> lists;
			int listIndexer = getIndex(path);

			// 当前是list,直接新建并返回即可
			boolean isCurrentList = current instanceof List;
			if (!isCurrentList) {
				lists = expand(new ArrayList<Object>(), listIndexer + 1);
				lists.set(
						listIndexer,
						buildObject(paths.subList(index + 1, paths.size()),
								value));
				return lists;
			}

			// 当前是list,但是对应的indexer是没有具体的值,也就是我们新建对象然后插入到该list,并返回该List
			lists = (List<Object>) 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<String, Object>) 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<Object>) target).get(Integer.valueOf(index));
	}

	private List<Object> expand(List<Object> 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<String> 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<String> getSecretKeyPathSet() {
		return secretKeyPathSet;
	}

	public static void main(String[] args) throws IOException {
		Configuration from = Configuration.from("{\n" +
				"    \"job\": {\n" +
				"        \"content\": [\n" +
				"            {\n" +
				"                \"reader\": {\n" +
				"                    \"name\": \"oraclereader\",\n" +
				"                    \"parameter\": {\n" +
				"                        \"column\": [\"OPER_ID\",\"TITLE\",\"BUSINESS_TYPE\",\"METHOD\",\"REQUEST_METHOD\",\"OPERATOR_TYPE\",\"OPER_NAME\",\"DEPT_NAME\",\"OPER_URL\",\"OPER_IP\",\"OPER_LOCATION\",\"OPER_PARAM\",\"JSON_RESULT\",\"STATUS\",\"ERROR_MSG\",\"OPER_TIME\",\"TENANT_ID\",\"APP_ID\"],\n" +
				"                        \"connection\": [\n" +
				"                            {\n" +
				"                                \"jdbcUrl\": [\"jdbc:oracle:thin:@10.56.0.15:1521/zdcomora\"],\n" +
				"                                \"table\": [\"SYS_OPER_LOG_2021_05\"]\n" +
				"                            }\n" +
				"                        ],\n" +
				"                        \"password\": \"unimis\",\n" +
				"                        \"username\": \"5aAdm!zD.com\",\n" +
				"\t\t\t\t\t\t\"where\":\"1=1\"\n" +
				"                    }\n" +
				"                },\n" +
				"                \"writer\": {\n" +
				"                    \"name\": \"oraclewriter\",\n" +
				"                    \"parameter\": {\n" +
				"                       \"column\": [\"OPER_ID\",\"TITLE\",\"BUSINESS_TYPE\",\"METHOD\",\"REQUEST_METHOD\",\"OPERATOR_TYPE\",\"OPER_NAME\",\"DEPT_NAME\",\"OPER_URL\",\"OPER_IP\",\"OPER_LOCATION\",\"OPER_PARAM\",\"JSON_RESULT\",\"STATUS\",\"ERROR_MSG\",\"OPER_TIME\",\"TENANT_ID\",\"APP_ID\"],\n" +
				"                        \"connection\": [\n" +
				"                            {\n" +
				"                                \"jdbcUrl\": \"jdbc:oracle:thin:@10.56.0.15:1521/zdcomora\",\n" +
				"                                \"table\": [\"PS_5A_OPER_LOG\"]\n" +
				"                            }\n" +
				"                        ],\n" +
				"                        \"password\": \"rhsc_5a\",\n" +
				"                        \"username\": \"InTsG2BDFUKC6U\"\n" +
				"                    }\n" +
				"                }\n" +
				"            }\n" +
				"        ],\n" +
				"        \"setting\": {\n" +
				"            \"speed\": {\n" +
				"                \"channel\": 6\n" +
				"            }\n" +
				"        }\n" +
				"    }\n" +
				"}");
		System.out.println(from.get("job"));
		System.out.println(from.get("job.content[0].reader"));
		System.out.println(from.get("job.content[0].reader.parameter"));
		System.out.println(from.get("job.content[0].reader.parameter.column"));
		System.out.println(from.getConfiguration("job.content[0].reader.parameter.column"));
		List<String> list = from.getList("job.content[0].reader.parameter.column", String.class);
		System.out.println(list);
		// File file = new File("d://dolphin-datax.txt");
//		String content = new String(Files.readAllBytes(Paths.get("d://dolphin-datax.json")));
		String content = new String(Files.readAllBytes(Paths.get("d://dolphin-datax2.txt")));
//		String content = new String(Files.readAllBytes(Paths.get("d://new.json")));
//		String regex1 = "\\$\\'\\{.*?\\}\\'";
//		content = content.replaceAll(regex1, "");

		content = content.replaceAll("[\\n\\r]", "");
		System.out.println(content);
		String regex = "\\$\\{.*?\\}|\\$\\[.*?\\]";
		content = content.replaceAll(regex, "");
		content = content.replaceAll(": ,", ": \"\",");
		content = content.replaceAll(":,", ": \"\",");
		// content = content.replaceAll("\\$", "");
//		content = content.replaceAll("[\\n\\r]", "");
		System.out.println(content);
		// com.alibaba.fastjson2.JSONObject jsonObject = JSONObject.parseObject(content);
		Configuration fromFile = Configuration.from(content);

		System.out.println(fromFile.get("job.content[0].reader"));
		System.out.println(fromFile.get("job.content[0].reader.parameter"));
		System.out.println(fromFile.getConfiguration("job.content[0].reader.parameter.column"));
		System.out.println(fromFile.get("job.content[0].reader.parameter.connection[0].querySql"));
	}
}

package com.zditec.system.utils.exception;


/**
 * @ClassName: CommonErrorCode
 */
public enum CommonErrorCode implements ErrorCode {

    /**
     * DataXJSON解析 错误枚举
     **/
    CONFIG_ERROR("Common-00", "您提供的配置文件存在错误信息,请检查您的JSON配置 ."),
    CONVERT_NOT_SUPPORT("Common-01", "同步数据出现业务脏数据情况,数据类型转换错误 ."),
    CONVERT_OVER_FLOW("Common-02", "同步数据出现业务脏数据情况,数据类型转换溢出 ."),
    RETRY_FAIL("Common-10", "方法调用多次仍旧失败 ."),
    RUNTIME_ERROR("Common-11", "运行时内部调用错误 ."),
    HOOK_INTERNAL_ERROR("Common-12", "Hook运行错误 ."),
    SHUT_DOWN_TASK("Common-20", "Task收到了shutdown指令,为failover做准备"),
    WAIT_TIME_EXCEED("Common-21", "等待时间超出范围"),
    TASK_HUNG_EXPIRED("Common-22", "任务hung住,Expired");

    private final String code;

    private final String describe;

    private CommonErrorCode(String code, String describe) {
        this.code = code;
        this.describe = describe;
    }

    @Override
    public String getCode() {
        return this.code;
    }

    @Override
    public String getDescription() {
        return this.describe;
    }

    @Override
    public String toString() {
        return String.format("Code:[%s], Describe:[%s]", this.code,
                this.describe);
    }

}

package com.zditec.system.utils.exception;


import lombok.Getter;

import java.io.PrintWriter;
import java.io.StringWriter;

/**
 * @ClassName: DataXException
 */
@Getter
public class DataXException extends RuntimeException {

    private static final long serialVersionUID = 1L;

    private ErrorCode errorCode;

    public DataXException(ErrorCode errorCode, String errorMessage) {
        super(errorCode.toString() + " - " + errorMessage);
        this.errorCode = errorCode;
    }

    public DataXException(String errorMessage) {
        super(errorMessage);
    }

    private DataXException(ErrorCode errorCode, String errorMessage, Throwable cause) {
        super(errorCode.toString() + " - " + getMessage(errorMessage) + " - " + getMessage(cause), cause);

        this.errorCode = errorCode;
    }

    public static DataXException asDataXException(ErrorCode errorCode, String message) {
        return new DataXException(errorCode, message);
    }

    public static DataXException asDataXException(String message) {
        return new DataXException(message);
    }

    public static DataXException asDataXException(ErrorCode errorCode, String message, Throwable cause) {
        if (cause instanceof DataXException) {
            return (DataXException) cause;
        }
        return new DataXException(errorCode, message, cause);
    }

    public static DataXException asDataXException(ErrorCode errorCode, Throwable cause) {
        if (cause instanceof DataXException) {
            return (DataXException) cause;
        }
        return new DataXException(errorCode, getMessage(cause), cause);
    }

    private static String getMessage(Object obj) {
        if (obj == null) {
            return "";
        }

        if (obj instanceof Throwable) {
            StringWriter str = new StringWriter();
            PrintWriter pw = new PrintWriter(str);
            ((Throwable) obj).printStackTrace(pw);
            return str.toString();
            // return ((Throwable) obj).getMessage();
        } else {
            return obj.toString();
        }
    }
}

package com.zditec.system.utils.exception;

/**
 * 尤其注意:最好提供toString()实现。例如:
 * 
 * <pre>
 * 
 * &#064;Override
 * public String toString() {
 * 	return String.format(&quot;Code:[%s], Description:[%s]. &quot;, this.code, this.describe);
 * }
 * </pre>
 * 
 */
public interface ErrorCode {
	// 错误码编号
	String getCode();

	// 错误码描述
	String getDescription();

	/** 必须提供toString的实现
	 * 
	 * <pre>
	 * &#064;Override
	 * public String toString() {
	 * 	return String.format(&quot;Code:[%s], Description:[%s]. &quot;, this.code, this.describe);
	 * }
	 * </pre>
	 * 
	 */
	String toString();
}

posted @ 2024-07-19 17:26  倔强的老铁  阅读(17)  评论(0编辑  收藏  举报