- 浏览: 275339 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
java老大爷:
技术扶持 技术交流 汇聚程序员精英 群里有马士兵2017 ...
大数据数据 -
yeruowei:
遇到同样的问题,特意登录点赞
导入数据库时出现ORA-01435: 用户不存在 -
1017974387:
特意登陆来给你赞。。。。。。
导入数据库时出现ORA-01435: 用户不存在 -
sgy1103:
您好:看了苯苯熊家庭记帐注册码破解很羡慕。我在用,不过换电脑后 ...
笨笨家庭记账本—破解 -
shellbye:
居然真是这个原因。。。哈哈
maven设置中ERROR: JAVA_HOME is set to an invalid directory
在以Spring为核心的Web应用中,使用Ext作为Web前台,通过Struts2作为数据交换的“跳板”。
原本Struts2自身具备的ModelDriven接口,在使用Ext前台后变得已经没有什么大用了。
由于有struts2-json-plugin的支持,可以很方便的获取前台的数据。
有点像Ext将数据序列化后,再由后台的Java进行反序列化。但是,Ext毕竟只能提供JSON数据,它的本质还只是POST过来的字符串而已。
然而借助struts2-json-plugin提供的 Object JSONUtil.deserialize(String) 方法,可以很简单的将 request 中的字符串“反序列化”为Java对象。
但是,在实战中发现,由于“反序列化”的时候并没有一个Java类作为“模板”,所以到底“反序列化”出来的对象是什么类型的,完全由JSONUtil.deserialize方法自己决定。而它只能参考唯一的参数——那个大大的JSON字符串来决定。于是 见到形如数字的就生成 Long,有小数点的就是 Double,其他统统 String,见到[ ]就是List,见到{ }就是Map。
然而我们Java端的JavaBean中真的是这些类型吗?
那个Long可能仅仅是int,或者根本就是个String;
那个List或许是个数组;
那个Map其实是另一个JavaBean。。。
但是,JSONUtil真的猜不到呀。
多么希望JSONUtil能够提供一个这样的重载方法:deserialize(String, Class)
但是,它真的没有。
于是我在我的框架中,从Struts2的Action入手。给所有的Action类编制一个抽象父类,并在其中实现这样的“智能反序列化”功能。
写这段代码时,发现最为麻烦的就是,如果JSONUtil反序列化后得到的对象中的某个属性是集合类型,而我们的JavaBean中,它其实应该是数组或JavaBean时,如何得到集合类型泛型中的类型。
举个例子,JSON字符串经JSONUtil处理后,有个属性 stuffs 的类型是List<E>,而按照JavaBean中的定义,这个 stuffs 的类型是个数组 Human[] stuffs ,这个相对简单一些,假设从JavaBean中反射得到各个属性并遍历时,得到的属性类型为propType:
beanInfo = Introspector.getBeanInfo(type);
for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) {
Class propType = descriptor.getPropertyType();
}
那么 Class innerCls = propertyType.getComponentType(); 就能得到数组的元素类型,如上例中的 Human。
更为复杂的是,当JSON中的 List<E> 对应于 JavaBean 中的 List<?> 时,如果上例中,JavaBean中 stuffs 属性的类型改为 List<Human> :
那么很遗憾,我没有找到方法可以从 propTpye (List<Human>的Class)中找到并提取 Human 这个类。
只能通过 stuffs 这个属性在其JavaBean中的setter方法,取得其参数里面的泛型,来发现这个Human。
// get the inner Type
ParameterizedType rType = (ParameterizedType) wm.getGenericParameterTypes()[0];
Type fType = rType.getActualTypeArguments()[0];
// Type to Class
Field field = fType.getClass().getDeclaredField("name");
field.setAccessible(true);
String name = (String) field.get(fType);
Class innerCls = Class.forName(name);
Map中泛型也与此类型,唯一的区别是,Map中有key和value,需要取两个泛型的类型。
剩下的工作就是递归了,这样才能无线层次的构筑复杂的JavaBean对象。
详细代码如下:
AbstractAction.java
/**
* Action的抽象骨架类
*
*/
public abstract class AbstractAction extends ActionSupport {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
// 此处省略一些与本功能无关的代码
/**
* 预处理。确认request有效,并强制编码格式为UTF-8
*/
private boolean proParseRequestData() {
ServletRequest request = ServletActionContext.getRequest();
try {
request.setCharacterEncoding("UTF-8");
} catch (UnsupportedEncodingException e) {
logger.warn("Can't encoding to UTF-8. use the default encode.");
}
if (request == null) {
logger.error("Can't get http servlet request.");
return false;
} else {
return true;
}
}
/**
* 获取数组型JSON字符串
* 因数组型数据,前台可能有两种提交方式。一种是提交多个同名的项目,一种是提交单个以[]包裹、逗号分割的JSON格式的字符串。本方法将这两种提交方式统一化处理为JSON字符串格式
*/
private String parseParametersJSONString(ServletRequest request, String propertyName) {
String[] stringArray = request.getParameterValues(propertyName);
if(stringArray == null){
return null;
}
if (stringArray.length == 1 && stringArray[0].startsWith("[")) {
return stringArray[0];
} else {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < stringArray.length; i++) {
sb.append("[");
sb.append(stringArray[i]);
sb.append("]");
if (i < stringArray.length - 1) {
sb.append(",");
}
}
return sb.toString();
}
}
/**
* 解析页面提交的所有数据 从request中提取JSON序列化字符串,并据此解析出复杂的JavaBean数据(VOBean)
*
* @param type
* 装填JavaBean的类型
* @return 装填好的JavaBean
*/
protected <T> T parseRequestData(Class<T> type) {
// 检查并预置HttpRequest
if (!proParseRequestData()) {
return null;
}
T result = null;
try {
result = (T) type.newInstance();
} catch (Exception e) {
logger.warn("Can't parse JOSN object.");
logger.warn(e.getMessage());
}
BeanInfo beanInfo = null;
try {
beanInfo = Introspector.getBeanInfo(type); // 获取类属性
} catch (Exception e) {
logger.warn("Can't parse JOSN object.");
logger.warn(e.getMessage());
}
ServletRequest request = ServletActionContext.getRequest();
// 给 JavaBean 对象的属性赋值
for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) {
Method wm = descriptor.getWriteMethod();
if (wm != null) {
String propertyName = descriptor.getName();
Class propType = descriptor.getPropertyType();
if (propType.isArray()) {
// 数组型属性
try {
String jsonString = parseParametersJSONString(request, propertyName);
Object jsonObj = JSONUtil.deserialize(jsonString);
Object value = parseProperties(jsonObj, propType);
wm.invoke(result, value);
} catch (Exception e) {
logger.warn("Can't set property " + propertyName);
logger.warn(e.toString());
}
} else if (List.class.isAssignableFrom(propType)) {
Class innerCls = null;
try {
String propertyString = parseParametersJSONString(request, propertyName);
if (propertyString != null) {
// get the inner Type
ParameterizedType rType = (ParameterizedType) wm.getGenericParameterTypes()[0];
Type fType = rType.getActualTypeArguments()[0];
// Type to Class
Field field = fType.getClass().getDeclaredField("name");
field.setAccessible(true);
String name = (String) field.get(fType);
innerCls = Class.forName(name);
Object jsonObj = JSONUtil.deserialize(propertyString);
Object value = parseProperties(jsonObj, propType, innerCls);
wm.invoke(result, value);
}
} catch (Exception e) {
logger.warn("Can't get inner generic class.");
logger.warn(e.getMessage());
}
} else if (Map.class.isAssignableFrom(propType)) {
try {
String propertyString = parseParametersJSONString(request, propertyName);
if (propertyString != null) {
// get the inner Type([0] is the Key type of the Map)
ParameterizedType rType1 = (ParameterizedType) wm.getGenericParameterTypes()[0];
Type fType1 = rType1.getActualTypeArguments()[0];
// Type to Class
Field field1 = fType1.getClass().getDeclaredField("name");
field1.setAccessible(true);
String name1 = (String) field1.get(fType1);
Class innerCls1 = Class.forName(name1);
// get the inner Type([1] is the Value type of the Map)
ParameterizedType rType2 = (ParameterizedType) wm.getGenericParameterTypes()[1];
Type fType2 = rType2.getActualTypeArguments()[0];
// Type to Class
Field field2 = fType2.getClass().getDeclaredField("name");
field2.setAccessible(true);
String name2 = (String) field2.get(fType2);
Class innerCls2 = Class.forName(name2);
Object jsonObj = JSONUtil.deserialize(propertyString);
Object value = parseProperties(jsonObj, propType, innerCls1, innerCls2);
wm.invoke(result, value);
}
} catch (Exception e) {
logger.warn("Can't get inner generic class.");
logger.warn(e.getMessage());
}
} else if (ClassUtil.isValueType(propType)) {
Object value = null;
try {
String propertyString = request.getParameter(propertyName);
if (propertyString != null) {
value = stringToObject(propertyString, propType);
wm.invoke(result, value);
}
} catch (Exception e) {
logger.warn("Can't set property " + propertyName);
logger.warn(e.getMessage());
}
} else {
Object value = null;
try {
String propertyString = request.getParameter(propertyName);
if (propertyString != null) {
Object jsonObj = JSONUtil.deserialize(propertyString);
value = parseProperties(jsonObj, propType);
wm.invoke(result, value);
}
} catch (Exception e) {
logger.warn("Can't set property " + propertyName);
logger.warn(e.getMessage());
}
}
}
}
// 返回结果
return result;
}
/**
* 解析页面提交的某个数据 从request中提取JSON序列化字符串,并据此解析出某个复杂的JavaBean数据(VOBean)
*
* @param type
* 装填JavaBean的类型
* @param propertyName
* 预解析的属性名(在request信息中的名)
* @return 装填好的JavaBean
*/
protected <T> T parseRequestData(Class<T> type, String propertyName) {
// 检查并预置HttpRequest
if (!proParseRequestData()) {
return null;
}
ServletRequest request = ServletActionContext.getRequest();
if (type.isArray()) {
try {
String jsonString = parseParametersJSONString(request, propertyName);
if (jsonString != null) {
Object jsonObj = JSONUtil.deserialize(jsonString);
return parseProperties(jsonObj, type);
}
} catch (Exception e) {
logger.warn("Can't set property " + propertyName);
logger.warn(e.getMessage());
}
} else if (List.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type)) {
try {
String propertyString = parseParametersJSONString(request, propertyName);
if (propertyString != null) {
// can't get the inner Type, use Object
Object jsonObj = JSONUtil.deserialize(propertyString);
return parseProperties(jsonObj, type, Object.class);
}
} catch (Exception e) {
logger.warn("Can't get inner generic class.");
logger.warn(e.getMessage());
}
} else if (ClassUtil.isValueType(type)) {
try {
String propertyString = request.getParameter(propertyName);
if (propertyString != null) {
return (T) stringToObject(propertyString, type);
}
} catch (Exception e) {
logger.warn("Can't get inner generic class.");
logger.warn(e.getMessage());
}
} else {
try {
String propertyString = request.getParameter(propertyName);
if (propertyString != null) {
Object jsonObj = JSONUtil.deserialize(propertyString);
return parseProperties(jsonObj, type);
}
} catch (Exception e) {
logger.warn("Can't get inner generic class.");
logger.warn(e.getMessage());
}
}
return null;
}
/**
* 解析属性
* @param <T> 返回值类型
* @param source JSON字符串反序列得到的源数据
* @param type 返回值的类型
* @return 解析得到的Java对象
*/
private <T> T parseProperties(Object source, Class<T> type) {
return parseProperties(source, type, null);
}
/**
* 解析属性
* @param <T> 返回值类型
* @param source JSON字符串反序列得到的源数据
* @param type 返回值的类型
* @param innerType 内部(泛型)类型,对应List<E>的E
* @return 解析得到的Java对象
*/
private <T> T parseProperties(Object source, Class<T> type, Class innerType) {
return parseProperties(source, type, innerType, null);
}
/**
* 解析属性
* @param <T> 返回值类型
* @param source JSON字符串反序列得到的源数据
* @param type 返回值的类型
* @param innerType1 内部(泛型)类型1,对应Map<K, V>的K
* @param innerType2 内部(泛型)类型2,对应Map<K, V>的V
* @return 解析得到的Java对象
*/
private <T> T parseProperties(Object source, Class<T> type, Class innerType1, Class innerType2) {
// JavaBean or Map
if (source instanceof Map) {
Map<Object, Object> jsonMap = (Map<Object, Object>) source;
if (Map.class.isAssignableFrom(type)) {
// type is Map
Map<Object, Object> rtnMap = new HashMap<Object, Object>();
for (Map.Entry<Object, Object> entry : jsonMap.entrySet()) {
rtnMap.put(parseProperties(entry.getKey(), innerType1), parseProperties(entry.getValue(), innerType2));
}
return (T) rtnMap;
} else {
// JavaBean
T obj = null;
try {
obj = type.newInstance();
} catch (Exception e) {
logger.warn("Can't parse JOSN object.");
logger.warn(e.getMessage());
}
BeanInfo beanInfo = null;
try {
beanInfo = Introspector.getBeanInfo(type); // 获取类属性
} catch (Exception e) {
logger.warn("Can't parse JOSN object.");
logger.warn(e.getMessage());
}
// 给 JavaBean 对象的属性赋值
for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) {
Method wm = descriptor.getWriteMethod();
if (wm != null) {
String propertyName = descriptor.getName();
Class propertyType = descriptor.getPropertyType();
try {
if (jsonMap.containsKey(propertyName)) {
Object value = jsonMap.get(propertyName);
if (value != null) {
if (propertyType.isArray()) {
// get the inner Type
Class innerCls = propertyType.getComponentType();
wm.invoke(obj, parseProperties(value, propertyType, innerCls));
} else if (List.class.isAssignableFrom(propertyType)) {
// get the inner Type
ParameterizedType rType = (ParameterizedType) wm.getGenericParameterTypes()[0];
Type fType = rType.getActualTypeArguments()[0];
// Type to Class
Field field = fType.getClass().getDeclaredField("name");
field.setAccessible(true);
String name = (String) field.get(fType);
Class innerCls = Class.forName(name);
wm.invoke(obj, parseProperties(value, propertyType, innerCls));
} else if (Map.class.isAssignableFrom(propertyType)) {
// get the inner Type([0] is the Key type of the Map)
ParameterizedType rType1 = (ParameterizedType) wm.getGenericParameterTypes()[0];
Type fType1 = rType1.getActualTypeArguments()[0];
// Type to Class
Field field1 = fType1.getClass().getDeclaredField("name");
field1.setAccessible(true);
String name1 = (String) field1.get(fType1);
Class innerCls1 = Class.forName(name1);
// get the inner Type([1] is the Value type of the Map)
ParameterizedType rType2 = (ParameterizedType) wm.getGenericParameterTypes()[1];
Type fType2 = rType2.getActualTypeArguments()[0];
// Type to Class
Field field2 = fType2.getClass().getDeclaredField("name");
field2.setAccessible(true);
String name2 = (String) field2.get(fType2);
Class innerCls2 = Class.forName(name2);
wm.invoke(obj, parseProperties(value, propertyType, innerCls1, innerCls2));
} else if (propertyType.isAssignableFrom(value.getClass())) {
wm.invoke(obj, value);
} else if(ClassUtil.isValueType(propertyType)){
wm.invoke(obj, stringToObject(value.toString(), propertyType));
} else {
wm.invoke(obj, parseProperties(value, propertyType));
}
}
}
} catch (Exception e) {
logger.warn("Can't set property " + propertyName);
logger.warn(e.getMessage());
}
}
}
return obj;
}
} else if (source instanceof List) {
// List from JSON
if (List.class.isAssignableFrom(type)) {
// Data into a List
if (ClassUtil.isValueType(type)) {
return (T) source;
} else {
List rtn = new ArrayList();
for (Object obj : (List) source) {
rtn.add(parseProperties(obj, innerType1));
}
return (T) rtn;
}
} else {
// Data into a Array
List<Object> innerList = (List<Object>) source;
Class arrayType = type.getComponentType();
Object[] array = (Object[]) Array.newInstance(arrayType, innerList.size());
for (int i = 0; i < innerList.size(); i++) {
Object src = innerList.get(i);
Object item = parseProperties(src, arrayType);
array[i] = item;
}
return (T) array;
}
} else {
return (T) source;
}
}
/**
* 将String转换为type所指定的类型
*
* @param str
* String型元数据
* @param type
* 转换后的数据类型的class
* @return 转换后的结果
* @throws NumberFormatException
* 当参数不能转换为数值型时
*/
private Object stringToObject(String str, Class type) {
try {
if (String.class.isAssignableFrom(type)) {
return str;
}
if (char.class.isAssignableFrom(type)) {
return str.charAt(0);
}
if (Character.class.isAssignableFrom(type)) {
return Character.valueOf(str.charAt(0));
}
if (int.class.isAssignableFrom(type)) {
return Integer.valueOf(str);
}
if (Integer.class.isAssignableFrom(type)) {
return Integer.valueOf(str);
}
if (boolean.class.isAssignableFrom(type)) {
return Boolean.valueOf(str);
}
if (Boolean.class.isAssignableFrom(type)) {
return Boolean.valueOf(str);
}
if (short.class.isAssignableFrom(type)) {
return Short.valueOf(str);
}
if (Short.class.isAssignableFrom(type)) {
return Short.valueOf(str);
}
if (long.class.isAssignableFrom(type)) {
return Long.valueOf(str);
}
if (Long.class.isAssignableFrom(type)) {
return Long.valueOf(str);
}
if (float.class.isAssignableFrom(type)) {
return Float.valueOf(str);
}
if (Float.class.isAssignableFrom(type)) {
return Float.valueOf(str);
}
if (double.class.isAssignableFrom(type)) {
return Double.valueOf(str);
}
if (Double.class.isAssignableFrom(type)) {
return Double.valueOf(str);
}
if (byte.class.isAssignableFrom(type)) {
return Byte.valueOf(str);
}
if (Byte.class.isAssignableFrom(type)) {
return Byte.valueOf(str);
}
if (Date.class.isAssignableFrom(type)) {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").parse(str);
}
if (Calendar.class.isAssignableFrom(type)) {
Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").parse(str);
Calendar result = Calendar.getInstance();
result.setTime(date);
return result;
}
if (BigInteger.class.isAssignableFrom(type)) {
return new BigInteger(str);
}
if (BigDecimal.class.isAssignableFrom(type)) {
return new BigDecimal(str);
}
return null;
} catch (Exception e) {
return null;
}
}
}
这里边用到了一个 ClassUtil.isValueType(Class) 方法用来判定给定的类型是为Java的值类型和基本型:
ClassUtil.java
public class ClassUtil {
/**
* 判定指定的 Class 对象是否表示一个值类型。 八个基本类型及它们的包装类、五个 Class 和 void。 即
* boolean、Boolean、byte、Byte、char、Character、short、Short、int、Integer、long、Long、float、Float、double、Double、String、BigInteger、BigDecimal、Date、Calendar、void。
*
* @param clazz
* @return 当且仅当指定类表示一个值类型时,才返回 true
*/
public static boolean isValueType(Class clazz) {
if (clazz.isPrimitive()) {
return true;
}
if (String.class.isAssignableFrom(clazz)) {
return true;
}
if (Byte.class.isAssignableFrom(clazz)) {
return true;
}
if (Character.class.isAssignableFrom(clazz)) {
return true;
}
if (Short.class.isAssignableFrom(clazz)) {
return true;
}
if (Integer.class.isAssignableFrom(clazz)) {
return true;
}
if (Long.class.isAssignableFrom(clazz)) {
return true;
}
if (Float.class.isAssignableFrom(clazz)) {
return true;
}
if (Double.class.isAssignableFrom(clazz)) {
return true;
}
if (Date.class.isAssignableFrom(clazz)) {
return true;
}
if (Calendar.class.isAssignableFrom(clazz)) {
return true;
}
if (BigInteger.class.isAssignableFrom(clazz)) {
return true;
}
if (BigDecimal.class.isAssignableFrom(clazz)) {
return true;
}
return false;
}
}
最后,在各个Action中的使用方法是:
XxxxxAction.java
Java代码
1.// 前台提交的request就是stuffs这个对象在前台的JSON对象
2.Human stuffs = parseRequestData(Human.class);
3.
4.// 前台提交的request中的项目并非某个JavaBean的众属性,而是单独接收它们
5.String listType = this.parseRequestData(String.class, "listType");
6.boolean hasBlankItem = this.parseRequestData(Boolean.class, "hasBlankItem");
7.Object[] params = this.parseRequestData(Object[].class, "params");
// 前台提交的request就是stuffs这个对象在前台的JSON对象
Human stuffs = parseRequestData(Human.class);
// 前台提交的request中的项目并非某个JavaBean的众属性,而是单独接收它们
String listType = this.parseRequestData(String.class, "listType");
boolean hasBlankItem = this.parseRequestData(Boolean.class, "hasBlankItem");
Object[] params = this.parseRequestData(Object[].class, "params");
就此我们的反序列化工具完成了,复杂的JavaBean可以成功反序列化了。
但是,期间的代码(主要是提取泛型中的类型),看上去有些丑陋,如果你知道更好的方法,请一定跟帖!
原本Struts2自身具备的ModelDriven接口,在使用Ext前台后变得已经没有什么大用了。
由于有struts2-json-plugin的支持,可以很方便的获取前台的数据。
有点像Ext将数据序列化后,再由后台的Java进行反序列化。但是,Ext毕竟只能提供JSON数据,它的本质还只是POST过来的字符串而已。
然而借助struts2-json-plugin提供的 Object JSONUtil.deserialize(String) 方法,可以很简单的将 request 中的字符串“反序列化”为Java对象。
但是,在实战中发现,由于“反序列化”的时候并没有一个Java类作为“模板”,所以到底“反序列化”出来的对象是什么类型的,完全由JSONUtil.deserialize方法自己决定。而它只能参考唯一的参数——那个大大的JSON字符串来决定。于是 见到形如数字的就生成 Long,有小数点的就是 Double,其他统统 String,见到[ ]就是List,见到{ }就是Map。
然而我们Java端的JavaBean中真的是这些类型吗?
那个Long可能仅仅是int,或者根本就是个String;
那个List或许是个数组;
那个Map其实是另一个JavaBean。。。
但是,JSONUtil真的猜不到呀。
多么希望JSONUtil能够提供一个这样的重载方法:deserialize(String, Class)
但是,它真的没有。
于是我在我的框架中,从Struts2的Action入手。给所有的Action类编制一个抽象父类,并在其中实现这样的“智能反序列化”功能。
写这段代码时,发现最为麻烦的就是,如果JSONUtil反序列化后得到的对象中的某个属性是集合类型,而我们的JavaBean中,它其实应该是数组或JavaBean时,如何得到集合类型泛型中的类型。
举个例子,JSON字符串经JSONUtil处理后,有个属性 stuffs 的类型是List<E>,而按照JavaBean中的定义,这个 stuffs 的类型是个数组 Human[] stuffs ,这个相对简单一些,假设从JavaBean中反射得到各个属性并遍历时,得到的属性类型为propType:
beanInfo = Introspector.getBeanInfo(type);
for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) {
Class propType = descriptor.getPropertyType();
}
那么 Class innerCls = propertyType.getComponentType(); 就能得到数组的元素类型,如上例中的 Human。
更为复杂的是,当JSON中的 List<E> 对应于 JavaBean 中的 List<?> 时,如果上例中,JavaBean中 stuffs 属性的类型改为 List<Human> :
那么很遗憾,我没有找到方法可以从 propTpye (List<Human>的Class)中找到并提取 Human 这个类。
只能通过 stuffs 这个属性在其JavaBean中的setter方法,取得其参数里面的泛型,来发现这个Human。
// get the inner Type
ParameterizedType rType = (ParameterizedType) wm.getGenericParameterTypes()[0];
Type fType = rType.getActualTypeArguments()[0];
// Type to Class
Field field = fType.getClass().getDeclaredField("name");
field.setAccessible(true);
String name = (String) field.get(fType);
Class innerCls = Class.forName(name);
Map中泛型也与此类型,唯一的区别是,Map中有key和value,需要取两个泛型的类型。
剩下的工作就是递归了,这样才能无线层次的构筑复杂的JavaBean对象。
详细代码如下:
AbstractAction.java
/**
* Action的抽象骨架类
*
*/
public abstract class AbstractAction extends ActionSupport {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
// 此处省略一些与本功能无关的代码
/**
* 预处理。确认request有效,并强制编码格式为UTF-8
*/
private boolean proParseRequestData() {
ServletRequest request = ServletActionContext.getRequest();
try {
request.setCharacterEncoding("UTF-8");
} catch (UnsupportedEncodingException e) {
logger.warn("Can't encoding to UTF-8. use the default encode.");
}
if (request == null) {
logger.error("Can't get http servlet request.");
return false;
} else {
return true;
}
}
/**
* 获取数组型JSON字符串
* 因数组型数据,前台可能有两种提交方式。一种是提交多个同名的项目,一种是提交单个以[]包裹、逗号分割的JSON格式的字符串。本方法将这两种提交方式统一化处理为JSON字符串格式
*/
private String parseParametersJSONString(ServletRequest request, String propertyName) {
String[] stringArray = request.getParameterValues(propertyName);
if(stringArray == null){
return null;
}
if (stringArray.length == 1 && stringArray[0].startsWith("[")) {
return stringArray[0];
} else {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < stringArray.length; i++) {
sb.append("[");
sb.append(stringArray[i]);
sb.append("]");
if (i < stringArray.length - 1) {
sb.append(",");
}
}
return sb.toString();
}
}
/**
* 解析页面提交的所有数据 从request中提取JSON序列化字符串,并据此解析出复杂的JavaBean数据(VOBean)
*
* @param type
* 装填JavaBean的类型
* @return 装填好的JavaBean
*/
protected <T> T parseRequestData(Class<T> type) {
// 检查并预置HttpRequest
if (!proParseRequestData()) {
return null;
}
T result = null;
try {
result = (T) type.newInstance();
} catch (Exception e) {
logger.warn("Can't parse JOSN object.");
logger.warn(e.getMessage());
}
BeanInfo beanInfo = null;
try {
beanInfo = Introspector.getBeanInfo(type); // 获取类属性
} catch (Exception e) {
logger.warn("Can't parse JOSN object.");
logger.warn(e.getMessage());
}
ServletRequest request = ServletActionContext.getRequest();
// 给 JavaBean 对象的属性赋值
for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) {
Method wm = descriptor.getWriteMethod();
if (wm != null) {
String propertyName = descriptor.getName();
Class propType = descriptor.getPropertyType();
if (propType.isArray()) {
// 数组型属性
try {
String jsonString = parseParametersJSONString(request, propertyName);
Object jsonObj = JSONUtil.deserialize(jsonString);
Object value = parseProperties(jsonObj, propType);
wm.invoke(result, value);
} catch (Exception e) {
logger.warn("Can't set property " + propertyName);
logger.warn(e.toString());
}
} else if (List.class.isAssignableFrom(propType)) {
Class innerCls = null;
try {
String propertyString = parseParametersJSONString(request, propertyName);
if (propertyString != null) {
// get the inner Type
ParameterizedType rType = (ParameterizedType) wm.getGenericParameterTypes()[0];
Type fType = rType.getActualTypeArguments()[0];
// Type to Class
Field field = fType.getClass().getDeclaredField("name");
field.setAccessible(true);
String name = (String) field.get(fType);
innerCls = Class.forName(name);
Object jsonObj = JSONUtil.deserialize(propertyString);
Object value = parseProperties(jsonObj, propType, innerCls);
wm.invoke(result, value);
}
} catch (Exception e) {
logger.warn("Can't get inner generic class.");
logger.warn(e.getMessage());
}
} else if (Map.class.isAssignableFrom(propType)) {
try {
String propertyString = parseParametersJSONString(request, propertyName);
if (propertyString != null) {
// get the inner Type([0] is the Key type of the Map)
ParameterizedType rType1 = (ParameterizedType) wm.getGenericParameterTypes()[0];
Type fType1 = rType1.getActualTypeArguments()[0];
// Type to Class
Field field1 = fType1.getClass().getDeclaredField("name");
field1.setAccessible(true);
String name1 = (String) field1.get(fType1);
Class innerCls1 = Class.forName(name1);
// get the inner Type([1] is the Value type of the Map)
ParameterizedType rType2 = (ParameterizedType) wm.getGenericParameterTypes()[1];
Type fType2 = rType2.getActualTypeArguments()[0];
// Type to Class
Field field2 = fType2.getClass().getDeclaredField("name");
field2.setAccessible(true);
String name2 = (String) field2.get(fType2);
Class innerCls2 = Class.forName(name2);
Object jsonObj = JSONUtil.deserialize(propertyString);
Object value = parseProperties(jsonObj, propType, innerCls1, innerCls2);
wm.invoke(result, value);
}
} catch (Exception e) {
logger.warn("Can't get inner generic class.");
logger.warn(e.getMessage());
}
} else if (ClassUtil.isValueType(propType)) {
Object value = null;
try {
String propertyString = request.getParameter(propertyName);
if (propertyString != null) {
value = stringToObject(propertyString, propType);
wm.invoke(result, value);
}
} catch (Exception e) {
logger.warn("Can't set property " + propertyName);
logger.warn(e.getMessage());
}
} else {
Object value = null;
try {
String propertyString = request.getParameter(propertyName);
if (propertyString != null) {
Object jsonObj = JSONUtil.deserialize(propertyString);
value = parseProperties(jsonObj, propType);
wm.invoke(result, value);
}
} catch (Exception e) {
logger.warn("Can't set property " + propertyName);
logger.warn(e.getMessage());
}
}
}
}
// 返回结果
return result;
}
/**
* 解析页面提交的某个数据 从request中提取JSON序列化字符串,并据此解析出某个复杂的JavaBean数据(VOBean)
*
* @param type
* 装填JavaBean的类型
* @param propertyName
* 预解析的属性名(在request信息中的名)
* @return 装填好的JavaBean
*/
protected <T> T parseRequestData(Class<T> type, String propertyName) {
// 检查并预置HttpRequest
if (!proParseRequestData()) {
return null;
}
ServletRequest request = ServletActionContext.getRequest();
if (type.isArray()) {
try {
String jsonString = parseParametersJSONString(request, propertyName);
if (jsonString != null) {
Object jsonObj = JSONUtil.deserialize(jsonString);
return parseProperties(jsonObj, type);
}
} catch (Exception e) {
logger.warn("Can't set property " + propertyName);
logger.warn(e.getMessage());
}
} else if (List.class.isAssignableFrom(type) || Map.class.isAssignableFrom(type)) {
try {
String propertyString = parseParametersJSONString(request, propertyName);
if (propertyString != null) {
// can't get the inner Type, use Object
Object jsonObj = JSONUtil.deserialize(propertyString);
return parseProperties(jsonObj, type, Object.class);
}
} catch (Exception e) {
logger.warn("Can't get inner generic class.");
logger.warn(e.getMessage());
}
} else if (ClassUtil.isValueType(type)) {
try {
String propertyString = request.getParameter(propertyName);
if (propertyString != null) {
return (T) stringToObject(propertyString, type);
}
} catch (Exception e) {
logger.warn("Can't get inner generic class.");
logger.warn(e.getMessage());
}
} else {
try {
String propertyString = request.getParameter(propertyName);
if (propertyString != null) {
Object jsonObj = JSONUtil.deserialize(propertyString);
return parseProperties(jsonObj, type);
}
} catch (Exception e) {
logger.warn("Can't get inner generic class.");
logger.warn(e.getMessage());
}
}
return null;
}
/**
* 解析属性
* @param <T> 返回值类型
* @param source JSON字符串反序列得到的源数据
* @param type 返回值的类型
* @return 解析得到的Java对象
*/
private <T> T parseProperties(Object source, Class<T> type) {
return parseProperties(source, type, null);
}
/**
* 解析属性
* @param <T> 返回值类型
* @param source JSON字符串反序列得到的源数据
* @param type 返回值的类型
* @param innerType 内部(泛型)类型,对应List<E>的E
* @return 解析得到的Java对象
*/
private <T> T parseProperties(Object source, Class<T> type, Class innerType) {
return parseProperties(source, type, innerType, null);
}
/**
* 解析属性
* @param <T> 返回值类型
* @param source JSON字符串反序列得到的源数据
* @param type 返回值的类型
* @param innerType1 内部(泛型)类型1,对应Map<K, V>的K
* @param innerType2 内部(泛型)类型2,对应Map<K, V>的V
* @return 解析得到的Java对象
*/
private <T> T parseProperties(Object source, Class<T> type, Class innerType1, Class innerType2) {
// JavaBean or Map
if (source instanceof Map) {
Map<Object, Object> jsonMap = (Map<Object, Object>) source;
if (Map.class.isAssignableFrom(type)) {
// type is Map
Map<Object, Object> rtnMap = new HashMap<Object, Object>();
for (Map.Entry<Object, Object> entry : jsonMap.entrySet()) {
rtnMap.put(parseProperties(entry.getKey(), innerType1), parseProperties(entry.getValue(), innerType2));
}
return (T) rtnMap;
} else {
// JavaBean
T obj = null;
try {
obj = type.newInstance();
} catch (Exception e) {
logger.warn("Can't parse JOSN object.");
logger.warn(e.getMessage());
}
BeanInfo beanInfo = null;
try {
beanInfo = Introspector.getBeanInfo(type); // 获取类属性
} catch (Exception e) {
logger.warn("Can't parse JOSN object.");
logger.warn(e.getMessage());
}
// 给 JavaBean 对象的属性赋值
for (PropertyDescriptor descriptor : beanInfo.getPropertyDescriptors()) {
Method wm = descriptor.getWriteMethod();
if (wm != null) {
String propertyName = descriptor.getName();
Class propertyType = descriptor.getPropertyType();
try {
if (jsonMap.containsKey(propertyName)) {
Object value = jsonMap.get(propertyName);
if (value != null) {
if (propertyType.isArray()) {
// get the inner Type
Class innerCls = propertyType.getComponentType();
wm.invoke(obj, parseProperties(value, propertyType, innerCls));
} else if (List.class.isAssignableFrom(propertyType)) {
// get the inner Type
ParameterizedType rType = (ParameterizedType) wm.getGenericParameterTypes()[0];
Type fType = rType.getActualTypeArguments()[0];
// Type to Class
Field field = fType.getClass().getDeclaredField("name");
field.setAccessible(true);
String name = (String) field.get(fType);
Class innerCls = Class.forName(name);
wm.invoke(obj, parseProperties(value, propertyType, innerCls));
} else if (Map.class.isAssignableFrom(propertyType)) {
// get the inner Type([0] is the Key type of the Map)
ParameterizedType rType1 = (ParameterizedType) wm.getGenericParameterTypes()[0];
Type fType1 = rType1.getActualTypeArguments()[0];
// Type to Class
Field field1 = fType1.getClass().getDeclaredField("name");
field1.setAccessible(true);
String name1 = (String) field1.get(fType1);
Class innerCls1 = Class.forName(name1);
// get the inner Type([1] is the Value type of the Map)
ParameterizedType rType2 = (ParameterizedType) wm.getGenericParameterTypes()[1];
Type fType2 = rType2.getActualTypeArguments()[0];
// Type to Class
Field field2 = fType2.getClass().getDeclaredField("name");
field2.setAccessible(true);
String name2 = (String) field2.get(fType2);
Class innerCls2 = Class.forName(name2);
wm.invoke(obj, parseProperties(value, propertyType, innerCls1, innerCls2));
} else if (propertyType.isAssignableFrom(value.getClass())) {
wm.invoke(obj, value);
} else if(ClassUtil.isValueType(propertyType)){
wm.invoke(obj, stringToObject(value.toString(), propertyType));
} else {
wm.invoke(obj, parseProperties(value, propertyType));
}
}
}
} catch (Exception e) {
logger.warn("Can't set property " + propertyName);
logger.warn(e.getMessage());
}
}
}
return obj;
}
} else if (source instanceof List) {
// List from JSON
if (List.class.isAssignableFrom(type)) {
// Data into a List
if (ClassUtil.isValueType(type)) {
return (T) source;
} else {
List rtn = new ArrayList();
for (Object obj : (List) source) {
rtn.add(parseProperties(obj, innerType1));
}
return (T) rtn;
}
} else {
// Data into a Array
List<Object> innerList = (List<Object>) source;
Class arrayType = type.getComponentType();
Object[] array = (Object[]) Array.newInstance(arrayType, innerList.size());
for (int i = 0; i < innerList.size(); i++) {
Object src = innerList.get(i);
Object item = parseProperties(src, arrayType);
array[i] = item;
}
return (T) array;
}
} else {
return (T) source;
}
}
/**
* 将String转换为type所指定的类型
*
* @param str
* String型元数据
* @param type
* 转换后的数据类型的class
* @return 转换后的结果
* @throws NumberFormatException
* 当参数不能转换为数值型时
*/
private Object stringToObject(String str, Class type) {
try {
if (String.class.isAssignableFrom(type)) {
return str;
}
if (char.class.isAssignableFrom(type)) {
return str.charAt(0);
}
if (Character.class.isAssignableFrom(type)) {
return Character.valueOf(str.charAt(0));
}
if (int.class.isAssignableFrom(type)) {
return Integer.valueOf(str);
}
if (Integer.class.isAssignableFrom(type)) {
return Integer.valueOf(str);
}
if (boolean.class.isAssignableFrom(type)) {
return Boolean.valueOf(str);
}
if (Boolean.class.isAssignableFrom(type)) {
return Boolean.valueOf(str);
}
if (short.class.isAssignableFrom(type)) {
return Short.valueOf(str);
}
if (Short.class.isAssignableFrom(type)) {
return Short.valueOf(str);
}
if (long.class.isAssignableFrom(type)) {
return Long.valueOf(str);
}
if (Long.class.isAssignableFrom(type)) {
return Long.valueOf(str);
}
if (float.class.isAssignableFrom(type)) {
return Float.valueOf(str);
}
if (Float.class.isAssignableFrom(type)) {
return Float.valueOf(str);
}
if (double.class.isAssignableFrom(type)) {
return Double.valueOf(str);
}
if (Double.class.isAssignableFrom(type)) {
return Double.valueOf(str);
}
if (byte.class.isAssignableFrom(type)) {
return Byte.valueOf(str);
}
if (Byte.class.isAssignableFrom(type)) {
return Byte.valueOf(str);
}
if (Date.class.isAssignableFrom(type)) {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").parse(str);
}
if (Calendar.class.isAssignableFrom(type)) {
Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").parse(str);
Calendar result = Calendar.getInstance();
result.setTime(date);
return result;
}
if (BigInteger.class.isAssignableFrom(type)) {
return new BigInteger(str);
}
if (BigDecimal.class.isAssignableFrom(type)) {
return new BigDecimal(str);
}
return null;
} catch (Exception e) {
return null;
}
}
}
这里边用到了一个 ClassUtil.isValueType(Class) 方法用来判定给定的类型是为Java的值类型和基本型:
ClassUtil.java
public class ClassUtil {
/**
* 判定指定的 Class 对象是否表示一个值类型。 八个基本类型及它们的包装类、五个 Class 和 void。 即
* boolean、Boolean、byte、Byte、char、Character、short、Short、int、Integer、long、Long、float、Float、double、Double、String、BigInteger、BigDecimal、Date、Calendar、void。
*
* @param clazz
* @return 当且仅当指定类表示一个值类型时,才返回 true
*/
public static boolean isValueType(Class clazz) {
if (clazz.isPrimitive()) {
return true;
}
if (String.class.isAssignableFrom(clazz)) {
return true;
}
if (Byte.class.isAssignableFrom(clazz)) {
return true;
}
if (Character.class.isAssignableFrom(clazz)) {
return true;
}
if (Short.class.isAssignableFrom(clazz)) {
return true;
}
if (Integer.class.isAssignableFrom(clazz)) {
return true;
}
if (Long.class.isAssignableFrom(clazz)) {
return true;
}
if (Float.class.isAssignableFrom(clazz)) {
return true;
}
if (Double.class.isAssignableFrom(clazz)) {
return true;
}
if (Date.class.isAssignableFrom(clazz)) {
return true;
}
if (Calendar.class.isAssignableFrom(clazz)) {
return true;
}
if (BigInteger.class.isAssignableFrom(clazz)) {
return true;
}
if (BigDecimal.class.isAssignableFrom(clazz)) {
return true;
}
return false;
}
}
最后,在各个Action中的使用方法是:
XxxxxAction.java
Java代码
1.// 前台提交的request就是stuffs这个对象在前台的JSON对象
2.Human stuffs = parseRequestData(Human.class);
3.
4.// 前台提交的request中的项目并非某个JavaBean的众属性,而是单独接收它们
5.String listType = this.parseRequestData(String.class, "listType");
6.boolean hasBlankItem = this.parseRequestData(Boolean.class, "hasBlankItem");
7.Object[] params = this.parseRequestData(Object[].class, "params");
// 前台提交的request就是stuffs这个对象在前台的JSON对象
Human stuffs = parseRequestData(Human.class);
// 前台提交的request中的项目并非某个JavaBean的众属性,而是单独接收它们
String listType = this.parseRequestData(String.class, "listType");
boolean hasBlankItem = this.parseRequestData(Boolean.class, "hasBlankItem");
Object[] params = this.parseRequestData(Object[].class, "params");
就此我们的反序列化工具完成了,复杂的JavaBean可以成功反序列化了。
但是,期间的代码(主要是提取泛型中的类型),看上去有些丑陋,如果你知道更好的方法,请一定跟帖!
相关推荐
spring使用logback的扩展,使用起来非常方便。在web.xml中配置: ... <listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListener</listener-class> </listener> 即可加载logback配置,使用logback。
### 搭建EXTJS和STRUTS2框架(ext和struts2简单实例) #### 一、概述 本文档将详细介绍如何在Java Web项目中搭建EXTJS和STRUTS2框架,并通过一个简单的实例来展示如何使这两个技术协同工作。EXTJS是一个用于构建交互...
logback与spring集成的文件,从官网上找的。上传的文件包括源文件和jar包,以下是连接: https://github.com/qos-ch/logback-extensions/wiki/Spring ...
Ext中的Combobox下来框在EditGrid中的应用。Combobox显示值问题得以解决。
ext-2.0ext-2.0ext-2.0ext-2.0ext-2.0ext-2.0ext-2.0ext-2.0ext-2.0ext-2.0ext-2.0ext-2.0ext-2.0ext-2.0ext-2.0ext-2.0ext-2.0ext-2.0ext-2.0ext-2.0ext-2.0ext-2.0ext-2.0ext-2.0
json-lib-ext-spring-1.0.2.jarjson-lib-ext-spring-1.0.2.jarjson-lib-ext-spring-1.0.2.jar
3. **整合EXT-desktop与Struts2**:在实际项目中,EXTJS 通过 AJAX 与 Struts2 进行通信,发送请求到服务器,接收响应数据并更新视图。登录流程可能如下: - 用户访问 `/Struts2qs/login.jsp`,这个 JSP 页面通常...
spketdwcs-ext-2.1.mxpspketdwcs-ext-2.1.mxpspketdwcs-ext-2.1.mxpspketdwcs-ext-2.1.mxpspketdwcs-ext-2.1.mxpspketdwcs-ext-2.1.mxpspketdwcs-ext-2.1.mxpspketdwcs-ext-2.1.mxpspketdwcs-ext-2.1.mxpspketdwcs-...
其中,“style-ext-html-webpack-plugin”是一个特别值得关注的插件,它专门针对HTML文件的内联样式进行了优化,以提高页面加载速度和用户体验。 **一、插件概述** “style-ext-html-webpack-plugin”是针对...
提供的`logback-ext-spring-0.1.2-SNAPSHOT.jar`可能包含Spring特定的Logback扩展,这些扩展可能提供了与Spring更好的集成,例如自动配置、日志切面等。具体使用方法需参照`Spring使用logback代替log4j.txt`文件的...
选择 “ExtJS”并点击“Add File”,然后在你的./ext-2.x/source目录中选取“ext.jsb” 文件; 3、设 置新的ExtJS Profile,选中并点击“JavaScript Profiles” 对话框右手边的“Defalut”按钮;
《ext3.0开发之-->葵花宝典》是一份全面深入探讨EXT3.0开发的资源集合,其中涵盖了丰富的中文API文档、PDF电子书、示例代码以及相关的资源文件,旨在为开发者提供一站式的EXT3.0学习平台。EXT3.0是一个流行的...
《深入解析bcprov-jdk15on与bcprov-ext-jdk15on:Java加密库的秘密武器》 在Java开发中,加密技术是至关重要的,它保障了数据的安全传输和存储。bcprov-jdk15on和bcprov-ext-jdk15on是Bouncy Castle项目提供的两个...
ext-ms-win-gdi-desktop-l1-1-0.dll 用于解决这个dll文件丢失问题,下载后将此文件放置在相关文件根目录下,即可解决丢失问题
Extjs中实现国际化要用到的文件ext-lang-zh_CN.js和ext-lang-en.js
2. **bcprov-ext-jdk15on 和 bcprov-jdk15on 区别** "ext"版本(bcprov-ext-jdk15on)包含了标准版本(bcprov-jdk15on)的所有功能,并且还提供了一些额外的扩展功能,如更多的密码算法、密钥格式和证书标准。这些...
总之,bcprov-ext-jdk15on-1.54.jar和bcprov-jdk15on-1.54.jar是Bouncy Castle提供的Java加密库,它们包含了丰富的加密算法,特别适合处理与SSL/TLS相关的加密问题。当遇到“Could not generate DH keypair”异常时...
"ext3+struts2+hibernate+spring的CRUD+分页"是一个典型的Java Web开发组合,用于实现全面的数据管理功能。这个组合充分利用了各个框架的优势,提供了一个强大的后端数据处理和前端展示的解决方案。 首先,EXT3是一...
标题中的"bcprov-ext-jdk15on-152"和"bcprov-jdk15on-152"是两个与Bouncy Castle库相关的Java档案文件,它们主要用于提供加密和安全服务。Bouncy Castle是一个开源的Java安全提供者,广泛用于密码学应用,包括加密、...
Ecliplse导入spket插件加载Ext库需要的文件