Java introspection: object to map

后端 未结 7 946
无人及你
无人及你 2020-11-28 05:52

I have a Java object obj that has attributes obj.attr1, obj.attr2 etc. The attributes are possibly accessed through an extra level of

相关标签:
7条回答
  • 2020-11-28 06:11

    Another way to user JacksonObjectMapper is the convertValue ex:

     ObjectMapper m = new ObjectMapper();
     Map<String,Object> mappedObject = m..convertValue(myObject, new TypeReference<Map<String, String>>() {});
    
    0 讨论(0)
  • 2020-11-28 06:12

    None of these work for nested properties, object mapper does a fair job except that you have to set all values on all fields you want to see in map and even then you cannot avoid/ignore objects own @Json annotations easily in ObjectMapper basically skip some of the properties. So unfortunately, you have to do something like the following, it is only a draft to just give an idea.

    /*
         * returns fields that have getter/setters including nested fields as
         * field0, objA.field1, objA.objB.field2, ... 
         * to take care of recursive duplicates, 
         * simply use a set<Class> to track which classes
         * have already been traversed
         */
        public static void getBeanUtilsNestedFields(String prefix, 
                Class clazz,  List<String> nestedFieldNames) throws Exception {
            PropertyDescriptor[] descriptors = BeanUtils.getPropertyDescriptors(clazz);
            for(PropertyDescriptor descr : descriptors){
                // if you want values, use: descr.getValue(attributeName)
                if(descr.getPropertyType().getName().equals("java.lang.Class")){
                    continue;
                }
                // a primitive, a CharSequence(String), Number, Date, URI, URL, Locale, Class, or corresponding array
                // or add more like UUID or other types
                if(!BeanUtils.isSimpleProperty(descr.getPropertyType())){
                    Field collectionfield = clazz.getDeclaredField(descr.getName());
                    if(collectionfield.getGenericType() instanceof ParameterizedType){
                        ParameterizedType integerListType = (ParameterizedType) collectionfield.getGenericType();
                        Class<?> actualClazz = (Class<?>) integerListType.getActualTypeArguments()[0];
                        getBeanUtilsNestedFields(descr.getName(), actualClazz, nestedFieldNames);
                    }
                    else{   // or a complex custom type to get nested fields
                        getBeanUtilsNestedFields(descr.getName(), descr.getPropertyType(), nestedFieldNames);
                    }
                }
                else{
                    nestedFieldNames.add(prefix.concat(".").concat(descr.getDisplayName()));
                }
            }
        }
    
    0 讨论(0)
  • 2020-11-28 06:18

    Here's a rough approximation, hopefully enough to get you pointed in the right direction:

    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;
    }
    
    0 讨论(0)
  • 2020-11-28 06:21

    maven dependencies

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

    for JSR310 New Date/Time API,there are some issue need to be improved eg:

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

    expects(pseudo-code):

    查询条件:{"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}
    

    instead,i got these:

    查询条件:{"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}
    

    after a few research,an effective trick was found,

    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);
    //incase of empty/null String
    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
    Map<String,Object> filter = mapper.convertValue(this,Map.class);
    System.out.printf("查询条件:%s\n", JSON.toJSONString(filter));
    return filter;
    

    output:

    查询条件:{"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}
    

    I used the above code for dynamical query in MyBatis
    eg.

     /***
         * 查询文件系统使用率
         * @param condition
         * @return
         */
        LinkedList<SnmpFileSystemUsage> queryFileSystemUsage(Map<String,Object> condition);
    
        List<SnmpFileSystemUsage> fooBar()
        { 
           return snmpBaseMapper.queryFileSystemUsage(QueryConditionBuilder
                    .newBuilder()
                    .withNodeId(nodeId)
                    .build()
                    .toFilter());
        }
    
    0 讨论(0)
  • 2020-11-28 06:22

    You can use JavaBeans introspection for this. Read up on the java.beans.Introspector class:

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

    Big caveat: My code deals with getter methods only; it will not find naked fields. For fields, see highlycaffeinated's answer. :-) (You will probably want to combine the two approaches.)

    0 讨论(0)
  • 2020-11-28 06:23

    Here is a really easy way to do this.

    Use Jackson JSON lib to convert the object to JSON.

    Then read the JSON and convert it to a Map.

    The map will contain everything you want.

    Here is the 4 liner

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

    And additional win of course is that this is recursive and will create maps of maps if it needs to

    0 讨论(0)
提交回复
热议问题