Java内省:要映射的对象

时间:2011-07-22 21:11:12

标签: java introspection

我有一个Java对象obj,它有obj.attr1obj.attr2等属性。这些属性可能通过额外的间接访问来访问:obj.getAttr1(),{{1如果不公开的话。

挑战:我想要一个接受对象的函数,并返回obj.getAttr2(),其中的键是字符串Map<String, Object>"attr1"等等。值是对应的对象"attr2"obj.attr1。 我想这个函数会被调用类似

的东西
  • obj.attr2
  • toMap(obj)(其中toMap(obj, "attr1", "attr3")attr1attr3属性的子集),
  • 或者如有必要,可能obj

我对Java的内省了解不多:你是如何用Java做的?

现在,对于我关心的每种对象类型,我都有一个专门的toMap(obj, "getAttr1", "getAttr3")实现,并且它是太多的样板。


注意:对于那些了解Python的人,我想要toMap()之类的东西。或obj.__dict__为子集变体。

7 个答案:

答案 0 :(得分:47)

用户JacksonObjectMapper的另一种方式是convertValue例如:

 ObjectMapper m = new ObjectMapper();
 Map<String,Object> mappedObject = m.convertValue(myObject,Map.class);

答案 1 :(得分:45)

使用Apache Commons BeanUtils:http://commons.apache.org/beanutils/

Map for JavaBeans的一个实现,它使用内省来获取和放置bean中的属性:

Map<Object, Object> introspected = new org.apache.commons.beanutils.BeanMap(object); 

注意:尽管API返回Map<Object, Object>(自1.9.0开始),但返回地图中键的实际类为java.lang.String

答案 2 :(得分:27)

您可以使用JavaBeans内省。阅读java.beans.Introspector课程:

public static Map<String, Object> introspect(Object obj) throws Exception {
    Map<String, Object> result = new HashMap<String, Object>();
    BeanInfo info = Introspector.getBeanInfo(obj.getClass());
    for (PropertyDescriptor pd : info.getPropertyDescriptors()) {
        Method reader = pd.getReadMethod();
        if (reader != null)
            result.put(pd.getName(), reader.invoke(obj));
    }
    return result;
}

重要提示:我的代码只处理getter方法;它找不到裸露的田野。对于字段,请参阅高度咖啡因的答案。 :-)(您可能希望将这两种方法结合起来。)

答案 3 :(得分:15)

这是一个粗略的近似值,希望足以让你指向正确的方向:

public Map<String, Object> getMap(Object o) {
    Map<String, Object> result = new HashMap<String, Object>();
    Field[] declaredFields = o.getClass().getDeclaredFields();
    for (Field field : declaredFields) {
        result.put(field.getName(), field.get(o));
    }
    return result;
}

答案 4 :(得分:5)

这是一种非常简单的简单方式。

使用Jackson JSON lib将对象转换为JSON。

然后阅读JSON并将其转换为Map。

地图将包含您想要的所有内容。

这是4班轮

ObjectMapper om = new ObjectMapper();
StringWriter sw = new StringWriter();
om.writeValue(object, sw);
Map<String, Object> map = om.readValue(sw.toString(), Map.class);

当然,额外的胜利是这是递归的,并且如果需要

将创建地图的地图

答案 5 :(得分:3)

这些都不适用于嵌套属性,对象映射器做得很好,除了你必须在地图上想要查看的所有字段上设置所有值,即使这样你也无法在ObjectMapper中轻易避免/忽略对象拥有的@Json注释基本上跳过了一些属性。所以不幸的是,你必须做类似下面的事情,只是给出一个想法的草稿。

{{1}}

答案 6 :(得分:0)

Maven依赖

    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
    </dependency>

....

ObjectMapper m = new ObjectMapper();
Map<String,Object> mappedObject = m.convertValue(myObject,Map.class);

对于JSR310 New Date / Time API,有些问题需要改进 例如:

import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.junit.Test;

import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Map;

@Data
@NoArgsConstructor
public class QueryConditionBuilder
{
    LocalDateTime startTime;
    LocalDateTime endTime;
    Long nodeId;
    Long fsId;
    Long memId;
    Long ifCardId;

    private QueryConditionBuilder(QueryConditionBuilder.Builder builder) {
        setStartTime(builder.startTime);
        setEndTime(builder.endTime);
        setNodeId(builder.nodeId);
        setFsId(builder.fsId);
        setMemId(builder.memId);
        setIfCardId(builder.ifCardId);
    }

    public static QueryConditionBuilder.Builder newBuilder() {
        return new QueryConditionBuilder.Builder();
    }

    public static QueryConditionBuilder newEmptyBuilder() {
        return new QueryConditionBuilder.Builder().build();
    }


    public Map<String,Object> toFilter()
    {
        Map<String,Object> filter = new ObjectMapper().convertValue(this,Map.class);
        System.out.printf("查询条件:%s\n", JSON.toJSONString(filter));
        return filter;
    }

    public static final class Builder {
        private LocalDateTime startTime;
        private LocalDateTime endTime;
        private Long nodeId = null;
        private Long fsId = null;
        private Long memId =null;
        private Long ifCardId = null;

        private Builder() {
        }

        public QueryConditionBuilder.Builder withStartTime(LocalDateTime val) {
            startTime = val;
            return this;
        }

        public QueryConditionBuilder.Builder withEndTime(LocalDateTime val) {
            endTime = val;
            return this;
        }

        public QueryConditionBuilder.Builder withNodeId(Long val) {
            nodeId = val;
            return this;
        }

        public QueryConditionBuilder.Builder withFsId(Long val) {
            fsId = val;
            return this;
        }

        public QueryConditionBuilder.Builder withMemId(Long val) {
            memId = val;
            return this;
        }

        public QueryConditionBuilder.Builder withIfCardId(Long val) {
            ifCardId = val;
            return this;
        }

        public QueryConditionBuilder build() {
            return new QueryConditionBuilder(this);
        }
    }

    @Test
    public void test()
    {     
        LocalDateTime now = LocalDateTime.now(ZoneId.of("+8"));
        LocalDateTime yesterday = now.plusHours(-24);

        Map<String, Object> condition = QueryConditionBuilder.newBuilder()
                .withStartTime(yesterday)
                .withEndTime(now)
                .build().toFilter();

        System.out.println(condition);
    }
}

期望(伪代码):

查询条件:{"startTime":{"2019-07-15T20:43:15"},"endTime":{"2019-07-16T20:43:15"}
{startTime={2019-07-15T20:43:15}, endTime={"2019-07-16T20:43:15"}, nodeId=null, fsId=null, memId=null, ifCardId=null}

相反,我得到了这些:

查询条件:{"startTime":{"dayOfMonth":15,"dayOfWeek":"MONDAY","dayOfYear":196,"hour":20,"minute":38,"month":"JULY","monthValue":7,"nano":263000000,"year":2019,"second":12,"chronology":{"id":"ISO","calendarType":"iso8601"}},"endTime":{"dayOfMonth":16,"dayOfWeek":"TUESDAY","dayOfYear":197,"hour":20,"minute":38,"month":"JULY","monthValue":7,"nano":263000000,"year":2019,"second":12,"chronology":{"id":"ISO","calendarType":"iso8601"}}}
{startTime={dayOfMonth=15, dayOfWeek=MONDAY, dayOfYear=196, hour=20, minute=38, month=JULY, monthValue=7, nano=263000000, year=2019, second=12, chronology={id=ISO, calendarType=iso8601}}, endTime={dayOfMonth=16, dayOfWeek=TUESDAY, dayOfYear=197, hour=20, minute=38, month=JULY, monthValue=7, nano=263000000, year=2019, second=12, chronology={id=ISO, calendarType=iso8601}}, nodeId=null, fsId=null, memId=null, ifCardId=null}

经过一些研究,发现了一个有效的窍门,

ObjectMapper mapper = new ObjectMapper();
JavaTimeModule module = new JavaTimeModule();
//https://github.com/networknt/light-4j/issues/82
mapper.registerModule(module);
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
Map<String,Object> filter = mapper.convertValue(this,Map.class);
System.out.printf("查询条件:%s\n", JSON.toJSONString(filter));
return filter;

输出:

查询条件:{"startTime":"2019-07-15T21:29:13.711","endTime":"2019-07-16T21:29:13.711"}
{startTime=2019-07-15T21:29:13.711, endTime=2019-07-16T21:29:13.711, nodeId=null, fsId=null, memId=null, ifCardId=null}

我使用上面的代码在MyBatis中动态查询 例如。

 /***
     * 查询文件系统使用率
     * @param condition
     * @return
     */
    LinkedList<SnmpFileSystemUsage> queryFileSystemUsage(Map<String,Object> condition);

    List<SnmpFileSystemUsage> fooBar()
    { 
       return snmpBaseMapper.queryFileSystemUsage(QueryConditionBuilder
                .newBuilder()
                .withNodeId(nodeId)
                .build()
                .toFilter());
    }