对象导航图语言(Object Graph Navigation Language),简称OGNL,它是一种功能强大的表达式语言,经过它简单一致的表达式语法,能够存取对象的任意属性,调用对象的方法,遍历整个对象的结构图, 实现字段类型转化等功能。它使用统一的表达式去存取对象的属性。这样能够更方便直观的取得数据。
例如如下对象结构:
订单Order类包含订单号orderId、用户User类对象, 用户User类包含用户姓名name、用户年龄age、地址类Address数组对象,地址类对象Address包含省province、市city、地址明细detail。
| Order
|-- orderId
|-- User
|---- name
|---- age
|---- Address List
|------ province
|------ city
|------ detail
我们通过如下方式来访问不同的属性:
- 访问订单号:order.orderNo
- 访问用户名:order.user.name
- 访问用户年龄:order.user.age
- 访问第一个地址所在省份:order.user.addressList[0].province
- 访问第2个地址所在市: order.user.addressList[1].city
- 访问第2个地址的明细: order.user.addressList[1].detail
如果上述Order、User、Address对象之间的组合结构用Map对象来存储,那么OGNL访问的表达式也一样,所以说OGNL是一种统一的表达式语言。
在java中如何自己来实现一个简单的ognl解析器呢,在不知道对象类型的情况下,我们根据属性名访问对象的属性,肯定要用反射,话不说直接上代码:
如下代码有3类解析方式:不同类型的解析方式详见代码注解
import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* OGNL解析器
* 对象导航图语言(Object Graph Navigation Language)
* @author zhouyong
* @date 2023/11/26 8:07 下午
*/
public class OgnlParser {
/**
* 根据属性名获取对象的属性值(支持OGNL表达式)
* eg: order.user.age , user.age , user.addressList[0] , user.addressArray[0] , user.addressList[0].city
*
* @param obj 对象,可以是Map,也支持对象里面嵌套Map,或者Map里面嵌套对象,且支持多层嵌套
* @param propertyNamePath
* 属性名的全路径,多层对象嵌套属性支持链式操作、数组下标的访问方式
* eg1: "a" 返回属性a的值
* eg2: "a[0]" 属性a为数组或者List,返回a的第一个元素值
* eg3: "a.b.c" 多层嵌套属性的访问方式,对象a包含对象b,对象b又包含c,通过该方式直接访问属性c的值
* eg4: "a.b.c[0]" 属性c为数组或者List,返回c的第一个元素值
* eg4: "a.b.c[0].d" 属性c为数组或者List,返回c的第一个元素的d属性值
*
* @return
*/
public static <T> T getFieldValue(Object obj, String propertyNamePath){
if(obj==null || propertyNamePath==null){
return null;
}
T fieldValue = null;
Object iteratorObj = obj;
String[] fieldNames = propertyNamePath.split("\\.");
List<String> fieldNameList = Stream.of(fieldNames).collect(Collectors.toList());
Iterator<String> fieldNameIterator = fieldNameList.iterator();
while(fieldNameIterator.hasNext()){
String fieldName = fieldNameIterator.next();
fieldValue = getPropertyValue(iteratorObj, fieldName);
if(fieldValue!=null){
if(fieldNameIterator.hasNext()){
iteratorObj = fieldValue;
}
}
}
return fieldValue;
}
/**
* 根据属性名获取属性值
* @param obj
* @param propertyName
* @return
*/
private static <T> T getPropertyValue(Object obj, String propertyName){
if(obj==null || propertyName==null){
return null;
}
Class objClass = obj.getClass();
T value = null;
String fieldName = parseFieldName(propertyName);
//如果是Map则直接通过key获取值
if(Map.class.isAssignableFrom(objClass)){
Object valueFromMap = (T)((Map) obj).get(fieldName);
value = getElementIfArray(valueFromMap, parseIndex(propertyName));
}
//否则通过反射获取字段值
else{
Field field = getField(objClass, fieldName);
if(field!=null){
boolean accessible = field.isAccessible();
if(!accessible){
field.setAccessible(true);
}
try {
Object valueFromField = field.get(obj);
value = getElementIfArray(valueFromField, parseIndex(propertyName));
} catch (Exception e) {
e.printStackTrace();
}finally {
if(!accessible){
field.setAccessible(false);
}
}
}
}
return value;
}
/**
* 数组或List的特殊处理
* @param value
* @param index 不为空表示属性名带下标,index为对应的下标志,如: list[0]解析出的index为0
* @return
*/
private static <T> T getElementIfArray(Object value, Integer index) {
if(value!=null && index!=null){
Class valueType = value.getClass();
//如果是List, 包含下标则返回对应的元素值
if(List.class.isAssignableFrom(valueType)){
List list = (List) value;
if(index<list.size()){
value = list.get(index);
}
}
//如果是数组,则返回指定下标的元素值
else if(valueType.isArray()){
//8种基本类型需要单独转换为对应基本类型的数组,直接转为Object[]会抛强制转换异常
if(char[].class.equals(valueType)){
char[] array = (char[]) value;
if(index<array.length){
value = array[index];
}
}else if(byte[].class.equals(valueType)){
byte[] array = (byte[]) value;
if(index<array.length){
value = array[index];
}
}else if(short[].class.equals(valueType)){
short[] array = (short[]) value;
if(index<array.length){
value = array[index];
}
}else if(int[].class.equals(valueType)){
int[] array = (int[]) value;
if(index<array.length){
value = array[index];
}
}else if(long[].class.equals(valueType)){
long[] array = (long[]) value;
if(index<array.length){
value = array[index];
}
}else if(float[].class.equals(valueType)){
float[] array = (float[]) value;
if(index<array.length){
value = array[index];
}
}else if(double[].class.equals(valueType)){
double[] array = (double[]) value;
if(index<array.length){
value = array[index];
}
}else if(boolean[].class.equals(valueType)){
boolean[] array = (boolean[]) value;
if(index<array.length){
value = array[index];
}
}
//非基本类型的数组可以直接强制转换为Object[]
else{
Object[] array = (Object[]) value;
if(index<array.length){
value = array[index];
}
}
}
}
return (T) value;
}
/**
* 根据字段名获取字段
* @param objClass
* @param fieldName
* @return
*/
private static Field getField(Class objClass, String fieldName){
if(objClass==null || fieldName==null){
return null;
}
Class superClass = objClass;
Field field = null;
do{
try {
field = superClass.getDeclaredField(fieldName);
} catch (Exception e) {
e.printStackTrace();
}
superClass = superClass.getSuperclass();
}while(!Object.class.equals(superClass) && field==null);
return field;
}
/**
* 如果属性名带下标,则去掉下标解析出属性名
* @param propertyName
* @return
*/
private static String parseFieldName(String propertyName) {
int index = propertyName.indexOf("[");
if(index>0){
propertyName = propertyName.substring(0,index);
}
return propertyName;
}
/**
* 如果属性名带下标,则解析下标
* @param propertyName
* @return
*/
private static Integer parseIndex(String propertyName) {
Integer index = null;
int startIdx = propertyName.indexOf("[");
if(startIdx>0 && propertyName.endsWith("]")){
try {
index = Integer.valueOf(propertyName.substring(startIdx+1, propertyName.length() - 1));
} catch (Exception e) {
e.printStackTrace();
}
}
return index;
}
}
针对上述代码我们在写一个单元测试验证:
/**
* @author zhouyong
* @date 2023/11/26 8:07 下午
*/
public class OgnlParserTest {
@Test
public void testByMap(){
Map order = new HashMap(5);
Map user = new HashMap(5);
Map address = new HashMap(5);
order.put("orderId", "order001");
order.put("user", user);
order.put("all", new Map[]{user,address});
user.put("name","张三");
user.put("sex", "男");
user.put("numbers", new int[]{1,2,3});
user.put("address", address);
address.put("province", "广东省");
address.put("city", "深圳市");
Assert.assertEquals("order001", OgnlParser.getFieldValue(order, "orderId"));
Assert.assertEquals(1, (int) OgnlParser.getFieldValue(order, "user.numbers[0]"));
Assert.assertEquals("广东省", OgnlParser.getFieldValue(order, "user.address.province"));
Assert.assertEquals("张三", OgnlParser.getFieldValue(order, "user.name"));
Assert.assertEquals("张三", OgnlParser.getFieldValue(order, "all[0].name"));
Assert.assertEquals("张三", OgnlParser.getFieldValue(user, "name"));
}
@Test
public void testByObject(){
Address address1 = new Address("广东省","深圳市", "罗湖区菜围街道");
Address address2 = new Address("湖南省","长沙市", "岳麓区梅溪湖街道");
List<Address> addressList = new ArrayList<>();
addressList.add(address1);
addressList.add(address2);
User user = new User("user001", "张三", addressList, new String[]{"[email protected]","[email protected]"});
Map otherInfo = new HashMap(4);
otherInfo.put("status", "已发货");
otherInfo.put("user001", user);
otherInfo.put("addressList", addressList);
otherInfo.put("emails", user.emails);
Order order = new Order("order001", new Date(), user, otherInfo);
Assert.assertEquals(order.orderId, OgnlParser.getFieldValue(order, "orderId"));
Assert.assertEquals(order.createdDate, OgnlParser.getFieldValue(order, "createdDate"));
Assert.assertEquals(order.otherInfo.get("status"), OgnlParser.getFieldValue(order, "otherInfo.status"));
Assert.assertEquals(order.users, OgnlParser.getFieldValue(order, "users"));
Assert.assertEquals(order.users[0], OgnlParser.getFieldValue(order, "users[0]"));
Assert.assertEquals(order.sequences, OgnlParser.getFieldValue(order, "sequences"));
int sequence0 = OgnlParser.getFieldValue(order, "sequences[0]");
Assert.assertEquals(order.sequences[0], sequence0);
Assert.assertEquals(user.userId, OgnlParser.getFieldValue(order, "otherInfo.user001.userId"));
Assert.assertEquals(user.userName, OgnlParser.getFieldValue(order, "otherInfo.user001.userName"));
String[] emails = OgnlParser.getFieldValue(order, "otherInfo.emails");
Assert.assertEquals(user.emails, emails);
String emails0 = OgnlParser.getFieldValue(order, "otherInfo.emails[0]");
Assert.assertEquals(user.emails[0], emails0);
Assert.assertEquals(user.emails[1], OgnlParser.getFieldValue(order, "otherInfo.emails[1]"));
Assert.assertEquals(user.emails, OgnlParser.getFieldValue(order, "otherInfo.user001.emails"));
Assert.assertEquals(user.emails[0], OgnlParser.getFieldValue(order, "otherInfo.user001.emails[0]"));
Assert.assertEquals(user.emails[1], OgnlParser.getFieldValue(order, "otherInfo.user001.emails[1]"));
Assert.assertEquals(user.addressList, OgnlParser.getFieldValue(order, "otherInfo.addressList"));
Assert.assertEquals(user.addressList.get(0), OgnlParser.getFieldValue(order, "otherInfo.addressList[0]"));
Assert.assertEquals(user.addressList.get(1), OgnlParser.getFieldValue(order, "otherInfo.addressList[1]"));
Assert.assertEquals(user.addressList.get(1).city, OgnlParser.getFieldValue(order, "otherInfo.addressList[1].city"));
Assert.assertEquals(user.addressList.get(1).province, OgnlParser.getFieldValue(order, "otherInfo.addressList[1].province"));
Assert.assertEquals(user.addressList.get(1).detail, OgnlParser.getFieldValue(order, "otherInfo.addressList[1].detail"));
Assert.assertEquals(user.addressList, OgnlParser.getFieldValue(order, "otherInfo.user001.addressList"));
Assert.assertEquals(user.addressList.get(0), OgnlParser.getFieldValue(order, "otherInfo.user001.addressList[0]"));
Assert.assertEquals(user.addressList.get(1), OgnlParser.getFieldValue(order, "otherInfo.user001.addressList[1]"));
Assert.assertEquals(user.addressList.get(1).city, OgnlParser.getFieldValue(order, "otherInfo.user001.addressList[1].city"));
Assert.assertEquals(user.addressList.get(1).province, OgnlParser.getFieldValue(order, "otherInfo.user001.addressList[1].province"));
Assert.assertEquals(user.addressList.get(1).detail, OgnlParser.getFieldValue(order, "otherInfo.user001.addressList[1].detail"));
Assert.assertEquals(order.user.userId, OgnlParser.getFieldValue(order, "user.userId"));
Assert.assertEquals(order.user.userName, OgnlParser.getFieldValue(order, "user.userName"));
Assert.assertEquals(user.emails, OgnlParser.getFieldValue(order, "user.emails"));
Assert.assertEquals(user.emails[0], OgnlParser.getFieldValue(order, "user.emails[0]"));
Assert.assertEquals(user.emails[1], OgnlParser.getFieldValue(order, "user.emails[1]"));
Assert.assertEquals(user.addressList, OgnlParser.getFieldValue(order, "user.addressList"));
Assert.assertEquals(user.addressList.get(0), OgnlParser.getFieldValue(order, "user.addressList[0]"));
Assert.assertEquals(user.addressList.get(1), OgnlParser.getFieldValue(order, "user.addressList[1]"));
Assert.assertEquals(user.addressList.get(1).city, OgnlParser.getFieldValue(order, "user.addressList[1].city"));
Assert.assertEquals(user.addressList.get(1).province, OgnlParser.getFieldValue(order, "user.addressList[1].province"));
Assert.assertEquals(user.addressList.get(1).detail, OgnlParser.getFieldValue(order, "user.addressList[1].detail"));
}
class Order{
private String orderId;
private Date createdDate;
private User user;
private User[] users;
private Map otherInfo;
private int[] sequences = new int[]{1,2,3};
public Order(String orderId, Date createdDate, User user, Map otherInfo) {
this.orderId = orderId;
this.createdDate = createdDate;
this.user = user;
this.otherInfo = otherInfo;
this.users = new User[]{user};
}
}
class User{
private String userId;
private String userName;
private List<Address> addressList;
private String[] emails;
public User(String userId, String userName, List<Address> addressList, String[] emails) {
this.userId = userId;
this.userName = userName;
this.addressList = addressList;
this.emails = emails;
}
}
class Address{
private String province;
private String city;
private String detail;
public Address(String province, String city, String detail) {
this.province = province;
this.city = city;
this.detail = detail;
}
}
}
如上代码均可在本地直接运行,感兴趣的同学可以自己动手试试。