Why Converting Java Beans to Maps Often Fails and How to Fix It

This article explains the hidden pitfalls when converting Java Beans to Map objects—such as type loss and incorrect property names caused by JSON libraries, BeanUtils, and CGLIB—and demonstrates reliable solutions using Dubbo's PojoUtils with clear code examples and visual debugging screenshots.

Java Backend Technology
Java Backend Technology
Java Backend Technology
Why Converting Java Beans to Maps Often Fails and How to Fix It

Background

In some business scenarios a Java Bean needs to be converted to a Map, which appears simple but actually contains many hidden pitfalls.

Pitfalls

2.0 Test Object

import lombok.Data;
import java.util.Date;

@Data
public class MockObject extends MockParent {
    private Integer aInteger;
    private Long aLong;
    private Double aDouble;
    private Date aDate;
}

@Data
public class MockParent {
    private Long parent;
}

2.1 JSON deserialization type loss

2.1.1 Problem reproduction

Using JSON frameworks (fastjson, gson, jackson) to convert a Bean to a Map can cause data‑type loss. For example, a Long value smaller than Integer.MAX_VALUE becomes an Integer after deserialization.

<!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>2.0.8</version>
</dependency>
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import java.util.Date;
import java.util.Map;

public class JsonDemo {
    public static void main(String[] args) {
        MockObject mockObject = new MockObject();
        mockObject.setAInteger(1);
        mockObject.setALong(2L);
        mockObject.setADate(new Date());
        mockObject.setADouble(3.4D);
        mockObject.setParent(3L);
        String json = JSON.toJSONString(mockObject);
        Map<String,Object> map = JSON.parseObject(json, new TypeReference<Map<String,Object>>(){});
        System.out.println(map);
    }
}
{"parent":3,"ADouble":3.4,"ALong":2,"AInteger":1,"ADate":1657299916477}

2.2 BeanMap property‑name errors

2.2.1 commons‑beanutils BeanMap

<!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
<dependency>
    <groupId>commons-beanutils</groupId>
    <artifactId>commons-beanutils</artifactId>
    <version>1.9.4</version>
</dependency>
import org.apache.commons.beanutils.BeanMap;
import third.fastjson.MockObject;
import java.util.Date;

public class BeanUtilsDemo {
    public static void main(String[] args) {
        MockObject mockObject = new MockObject();
        mockObject.setAInteger(1);
        mockObject.setALong(2L);
        mockObject.setADate(new Date());
        mockObject.setADouble(3.4D);
        mockObject.setParent(3L);
        BeanMap beanMap = new BeanMap(mockObject);
        System.out.println(beanMap);
    }
}

2.2.2 cglib BeanMap

<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib-nodep</artifactId>
    <version>3.2.12</version>
</dependency>
import net.sf.cglib.beans.BeanMap;
import third.fastjson.MockObject;
import java.util.Date;

public class BeanMapDemo {
    public static void main(String[] args) {
        MockObject mockObject = new MockObject();
        mockObject.setAInteger(1);
        mockObject.setALong(2L);
        mockObject.setADate(new Date());
        mockObject.setADouble(3.4D);
        mockObject.setParent(3L);
        BeanMap beanMap = BeanMap.create(mockObject);
        System.out.println(beanMap);
    }
}

Solution

3.1 Dubbo‑based fix

<!-- https://mvnrepository.com/artifact/org.apache.dubbo/dubbo -->
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo</artifactId>
    <version>3.0.9</version>
</dependency>
import org.apache.dubbo.common.utils.PojoUtils;
import third.fastjson.MockObject;
import java.util.Date;

public class DubboPojoDemo {
    public static void main(String[] args) {
        MockObject mockObject = new MockObject();
        mockObject.setAInteger(1);
        mockObject.setALong(2L);
        mockObject.setADate(new Date());
        mockObject.setADouble(3.4D);
        mockObject.setParent(3L);
        Object generalized = PojoUtils.generalize(mockObject);
        System.out.println(generalized);
    }
}

3.2 How it works

The core method PojoUtils.generalize(Object) recursively walks the object graph, handling enums, arrays, collections, maps and plain beans. It uses ReflectUtils.getPropertyNameFromBeanReadMethod to derive property names from getter methods, which correctly converts getALong() to aLong without the decapitalize bugs of Introspector.

public static Object generalize(Object pojo) {
    return generalize(pojo, new IdentityHashMap());
}

private static Object generalize(Object pojo, Map<Object,Object> history) {
    if (pojo == null) return null;
    if (pojo instanceof Enum) return ((Enum<?>)pojo).name();
    // handle arrays, collections, maps, primitives, Class, etc.
    // for beans, iterate over public getter methods:
    for (Method method : pojo.getClass().getMethods()) {
        if (ReflectUtils.isBeanPropertyReadMethod(method)) {
            ReflectUtils.makeAccessible(method);
            map.put(ReflectUtils.getPropertyNameFromBeanReadMethod(method),
                    generalize(method.invoke(pojo), history));
        }
    }
    // also handle public fields
    return map;
}

Because the property‑name extraction simply lower‑cases the first character after get or is, it produces the expected aLong, aDate, etc., and avoids the special‑case handling of Introspector.decapitalize that mis‑processes names like URL.

Conclusion

Converting Java Beans to Map objects is fraught with pitfalls: type loss when using JSON libraries and incorrect property‑name resolution in BeanMap implementations. Developers should verify conversion results with small demos, read the source code of the utilities they use, and consider Dubbo’s PojoUtils or a custom reflection‑based approach for reliable results.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaReflectionDubboJSONBean to MapBeanMap
Java Backend Technology
Written by

Java Backend Technology

Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.