实现一个简单的OGNL解析器

介绍

对象导航图语言(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类解析方式:不同类型的解析方式详见代码注解

  • 普通对象:通过反射获取属性值以及属性class,属性class用于继续通过反射获取点号后面的属性值(如果有),依次类推不断迭代直到取出最后一层属性;
  • Map对象:map最简单直接通过key方式获取属性值;
  • 数组或List:数组要注意基本类型的数组不能直接强制转换为Object[],八种基本类型要分别单独处理。
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[]{"zhangsan@qq.com","zhangsan@163.com"});

        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;
        }
    }

}

如上代码均可在本地直接运行,感兴趣的同学可以自己动手试试。












  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
web开发常用jar包 常用jar包 commons-beanutils.jar Apache Commons包中的一个,包含了一些Bean工具类类。必须使用的jar包。 commons-collections.jar Apache Commons包中的一个,包含了一些Apache开发的集合类,功能比java.util.*强大 commons-lang.jar Apache Commons包中的一个,包含了一些数据类型工具类,是java.lang.*的扩展。必须使用的jar包。 commons-logging.jar: Apache Commons包中的一个,包含日志功能 commons-io.jar Apache Commons包中的一个,java.io.*的扩展,输入输出,支持文件上传 commons-fileupload.jar Apache Commons包中的一个,是一个通过Http接收上传的文件并处理结果文件的库 dom4j-1.4.jar 和 jaxen-1.1.1.jar 是一个Java的XML API,类似于jdom,用来读写XML文件的。Hibernate使用dom4j解析XML配置文件和XML映射元文件。必需的。 ehcache-1.2.jar Hibernate可以使用不同cache缓存工具作为二级缓存。EHCache是缺省的cache缓存工具。如果没有其它的可选缓存工具,则为必需的。 hibernate3.jar hibernate3的核心类库。 itext.jar 是用于生成PDF文档的一个java类库。通过iText不仅可以生成PDF或rtf的文档,而且可以将XML、Html文件转化为PDF文件。 iTextAsian.jar itext中关于亚洲编码的类库,在这里用于中文字体的输入。 junit.jar Junit包,当你运行Hibernate自带的测试代码的时候需要,否则就不用。 commons-digester.jar Apache Commons包中的一个,通过它可以很方便的解析xml文件生成java对象 aspectjrt.jar 和aspectjweaver.jar Annotation 方式实现 AOP commons-dbcp.jar commons-pool-1.2.jar DBCP数据库连接池 cglib-nodep-2.1_3.jar CGLIB是一个强大的高质量高性能的代码生成库,在运行时可以用它来扩展Java类 jfreechart-1.0.12.jar 使用java生成图表的工具 log4j-1.2.15.jar 通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件、甚至是套接口服务器 jxl-2.6.jar 通过java操作excel表格的工具类库 jta-1.1.jar Java事务API,为J2EE平台提供了分布式事务服务 lucene-core.jar 、lucene-highlighter.jar 、compass-index-patch.jar、 compass-2.1.0.jar 是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎 json-lib-2.2.3-jdk15.jar json和java转化的辅助工具 flexjson.jar java到json串的转换 gson-1.3.jar java到json串的转换 ognl-2.6.11.jar OGNL表达式所需要的包,支持EL htmlparser.jar 强大的html解析器 jcommon-1.0.15.jar 使用java生成图表的工具 freemarker-2.3.8.jar 模板相关操作需要包 struts2-core-2.0.14.jar struts2核心包 struts2-spring-plugin-2.0.14.jar struts2整合spring所需要的包 xwork-2.0.7.jar xwork核心包 antlr-2.7.6.jar 一个语言转换工具, Hibernate利用它实现 HQL 到 SQL 的转换模板相关操作需要包 javassist-3.9.0.GA.jar 代码生成工具 Hibernate用它在运行时扩展 Java类和实现,同cglib包 slf4j-api-1.5.8.jar和slf4j-log4j12-1.5.0.jar hibernate使用的一个日志系统 spring.jar spring核心包 spring-security-core-2.0.4.jar 和 spring-security-taglibs-2.0.4.jar 权限管理 commons-codec-1.3.jar 字符编码 xalan.jar, xerces.jar, xml-apis.jar: Xerces是XML解析器,Xalan是格式化器,xml-apis实际上是JAXP。 sitemesh.jar

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值