java - BeanUtils setProperty 抛出 IllegalArgumentException

标签 java reflection apache-commons-beanutils

环境细节

我有三个项目:

  • 项目 A:Java REST 服务应用程序
  • 项目 B:Java 客户端应用
  • Project C:一个简单的 POJO 列表

前两个项目有第三个作为依赖项。

数据流

项目 B项目 A 发出 HTTP 请求,该请求使用 项目 C 转换的模型对象进行回复它到 JSON

项目 B 使用 JSONObject 解码 JSON 响应并尝试使用 BeanUtils 获取原始 POJO 对象.

代码示例

ExamplePOJO 类(Project C 的一部分):

public class ExamplePOJO {

    private String id;

    private AnotherPOJO anotherPOJO;

    public void setId(String id) {
         this.id = id;
    }

    public String getId() {
         return id;
    }

    public void setAnotherPOJO(AnotherPOJO anotherPOJO ) {
         this.anotherPOJO = anotherPOJO ;
    }

    public AnotherPOJO getAnotherPOJO() {
         return anotherPOJO ;
    }

}

项目 A 示例端点:

@Path("/sample")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getExampleResponse() {

    try {

        ServiceResponse<ExamplePOJO> response = new ServiceResponse<ExamplePOJO>();

        ExamplePOJO eo = new ExamplePOJO();
        eo.setId("1");

        AnotherPOJO ap = new AnotherPOJO();
        eo.setAnotherPojo(ap);

        response.setResponse(eo);

        return ((ResponseBuilder) Response.ok().entity(response)).type("application/json").build();

    } catch(Exception E) {
        //error
    }
}

Project A 响应对象容器:

public class ServiceResponse<T> {

    private T response;

    public T getResponse() {
        return this.response;
    }

    public void setResponse(T response) {
         this.response = response;
    }

}

有趣的部分,项目 B:

public void callService() {

    //....HTTP request...

    //json object
    JSONObject json = new JSONObject(jsonResponse);

    //ServiceResponseEquivalent is the same as the ServiceResponse object of *Project A*
    decodeResponse(json, ServiceResponseEquivalent.class, ExamplePOJO.class);

}


//this is a recursive function
public <T, V> T void decodeResponse(JSONObject json, Class<?> responseModel, Class<V> responseObjectModel) {

    //this is the same as the ServiceResponse object of *Project A*
    Object reflectedInstance = responseModel.newInstance();

    //here I got the field "response" of ServiceResponse
    Field[] fields = reflectedInstance.getClass().getDeclaredFields();
    for(Field field: fields) {

         //I got the json object based on the field name
         Object objectFromResponse = json.get(field.getName());

         if(objectFromResponse instanceof JSONObject) {

             //this is the "response" property of *ServiceResponse* which I know is an instance of *ExamplePOJO* class (because who calls this function pass *Class<V> responseObjectModel*
             if(if(field.getName().equals("response")) {

                  //recursive call
                  objectFromResponse = decodeResponse(json.getJSONObject(field.getName()), responseObjectModel, responseObjectModel);
             } 
             //here I found another object inside the "response" object but I don't know which class is it. In this case it's an instance of *AnotherPOJO*
             else {

                 //I try to get the class from the name of the property: in order to work, the property must be named as its class
                 String className = "com.example.packace." + field.getName().toUpperCase().charAt(0) + field.getName().substring(1, field.getName().length());

                //className = com.example.package.AnotherPOJO                       
                //recursive call
                objectFromResponse = decodeResponse(json.getJSONObject(field.getName()), Class.forName(className), responseObjectModel);

                //I try to set the object inside the response one
                //HERE IT FAILS
                BeanUtils.setProperty(reflectedInstance, field.getName(), objectFromResponse);

             }
             //here we found another Object but we don't know 
         } else {

             //I add the element
             BeanUtils.setProperty(reflectedInstance, field.getName(), objectFromResponse);
         }
    }

}

JSON 示例

客户端收到此 JSON:

{
    "response": { //response is an instance of ExamplePOJO
        "id":"1",
        "anotherPOJO":{
             [...]
        }
    },
    [ ...other fields...]
 }

问题

decodeResponse 尝试在递归调用中解码 AnotherPOJO 对象时抛出此异常:

java.lang.IllegalArgumentException: Cannot invoke com.example.package.ExamplePOJO.setAnotherPOJO on bean class 'class com.example.package.ExamplePOJO' - argument type mismatch - had objects of type "com.example.package.AnotherPOJO" but expected signature "com.example.package.AnotherPOJO"

从异常中可以明显看出,对象是同一类的实例。

有什么想法吗?


哪种方法可以更好地解码未知类的对象?这:

String className = "com.example.packace." + field.getName().toUpperCase().charAt(0) + field.getName().substring(1, field.getName().length());

 //className = com.example.package.AnotherPOJO                      
 //recursive call
 objectFromResponse = decodeResponse(json.getJSONObject(field.getName()), Class.forName(className), responseObjectModel);

有一个明显的问题,即字段必须命名为它的类。

最佳答案

这似乎是类加载问题。 查看 Class.forName(String) 的源代码,它使用来自调用者的类加载器。这可能与加载目标 responseModel 的类加载器不同,因此,请尝试以下操作:

//recursive call
objectFromResponse = decodeResponse(
    json.getJSONObject(field.getName()),
    Class.forName(className, true, responseModel.getClassLoader()),
    responseObjectModel);

这应该确保模型子层次结构类由同一个类加载器加载。

关于java - BeanUtils setProperty 抛出 IllegalArgumentException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49631435/

相关文章:

JAVA - Map : 类型的对象不支持索引或映射属性

java - 如何让 BeanUtils 忽略空值

java - 将数字四舍五入到一定数量的位数 Java

c# - 设置动态创建的枚举值的赋值表达式

java - 忽略 BeanUtils.copyProperties 中的空值

c# - 使用反射查看方法内部是否调用了方法

java - 函数参数 Class<Type> 不接受类型的子代

java - Android:如何从文件系统中的文件创建 BitmapDrawable?

java - Gradle构建开始失败并显示错误-无法解析配置 ':compile'的所有文件

java - 一个应用程序(可执行 jar)是否需要权限来监听 centos 上的端口?