【问题标题】:mapping jackson with @JsonAnySetter returns Unrecognized field with javassist class使用 @JsonAnySetter 映射杰克逊返回带有 javassist 类的 Unrecognized 字段
【发布时间】:2019-03-14 09:24:23
【问题描述】:

我正在尝试在 Tomcat 8.5 上使用 jersey 在 rest WS 中使用 jackson 将 json 字符串转换为对象。

对象是在运行时使用 javassist 创建的(信息来自 db)并添加一个用 @JsonAnySetter/Getter 注释的“其他”映射。

当我用 buildClass("MyClass") 调用 jackson 的映射器时,它会抛出 com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException

如果 MyClass 在启动时已加载到类路径中而不使用 buildClass,则映射工作正常。

我猜是加载器问题,但我不知道如何解决这个问题。

请查看并提供反馈。

public class ClassFactory{
public Class<?> buildClass(String className){
ClassPool pool = ClassPool.getDefault();
Loader cl = new Loader(pool);

CtClass cc = pool.makeClass(className);
ConstPool constPool = pool.get(className).getClassFile().getConstPool();

/* */
/* field creation loop */
/* */

// other map
CtField field = CtField.make("private java.util.Map other = new java.util.HashMap();", cc);
cc.addField(field);

// add other map getter
CtClass[] paramsAny = {pool.get(String.class.getName())};
cc.addMethod(CtNewMethod.make(pool.get(Object.class.getName()), "any", paramsAny,null, "{ return this.other.get($1);}", cc));
CtMethod m = cc.getDeclaredMethod("any", paramsAny);

// add @JsonAnyGetter to other map getter
AnnotationsAttribute annotationAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag );
annotationAttribute.addAnnotation(new Annotation(JsonAnyGetter.class.getName(), constPool));
m.getMethodInfo().addAttribute(annotationAttribute);

// add other map setter
CtClass[] paramsSet = {pool.get(String.class.getName()), pool.get(Object.class.getName())};
cc.addMethod(CtNewMethod.make(pool.get("void"), "set", paramsSet,null, "{this.other.put($1,$2);}", cc));
m = cc.getDeclaredMethod("set", paramsSet);

// add @JsonAnySetter to other map setter
annotationAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag );
annotationAttribute.addAnnotation(new Annotation(JsonAnySetter.class.getName(), constPool));
m.getMethodInfo().addAttribute(annotationAttribute);

//build class
return cc.toClass(cl,null);
}
}

这是生成的类的一部分

public class MyClass{
/* more fields */

private Map other = new HashMap();
@JsonAnyGetter
public Object any(String var1) {
    return this.other.get(var1);
}
@JsonAnySetter
public void set(String var1, Object var2) {
    this.other.put(var1, var2);
}

}

杰克逊映射器

ObjectReader reader = new ObjectMapper().reader();
Class<?> myClass = new ClassFactory().buildClass("MyClass");
Object myClassInstance =reader.forType(myClass).readValue(jsonString);

一些json

{
    /* more fields */
    "info1":"val1",
    "info2" :"val2"
}

【问题讨论】:

    标签: java json jackson javassist


    【解决方案1】:

    JacksonClassIntrospector 无法识别来自 javaassist 类加载器的注释加载。从两个不同的类加载器加载的同一个类被认为是不同的。尝试将加载类委托给这些注解的父类加载器。

        Loader cl = new Loader(pool);
        cl.delegateLoadingOf("com.fasterxml.jackson.annotation.JsonAnySetter");
        cl.delegateLoadingOf("com.fasterxml.jackson.annotation.JsonAnyGetter");
    

    【讨论】:

      【解决方案2】:

      这是因为您缺少属性“info1”和“info2”的 setter 和 getter。

      我用下面的代码对其进行了测试,反序列化工作正常。请注意我在类定义中添加 setter 和 getter 的行。

      public class ClassFactory {
          public Class<?> buildClass(String className) throws NotFoundException, CannotCompileException {
      
              Map<String, Class<?>> properties = new HashMap<String, Class<?>>();
              properties.put("info1", String.class);
              properties.put("info2", String.class);
              ClassPool pool = ClassPool.getDefault();
              Loader cl = new Loader(pool);
      
              CtClass cc = pool.makeClass(className);
              ConstPool constPool = pool.get(className).getClassFile().getConstPool();
      
              CtField field = CtField.make("private java.util.Map other = new java.util.HashMap();", cc);
              cc.addField(field);
      
      // add other map getter
              CtClass[] paramsAny = { pool.get(String.class.getName()) };
              cc.addMethod(CtNewMethod.make(pool.get(Object.class.getName()), "any", paramsAny, null,
                      "{ return this.other.get($1);}", cc));
              CtMethod m = cc.getDeclaredMethod("any", paramsAny);
      
      // add @JsonAnyGetter to other map getter
              AnnotationsAttribute annotationAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
              annotationAttribute.addAnnotation(new Annotation(JsonAnyGetter.class.getName(), constPool));
              m.getMethodInfo().addAttribute(annotationAttribute);
      
      // add other map setter
              CtClass[] paramsSet = { pool.get(String.class.getName()), pool.get(Object.class.getName()) };
              cc.addMethod(CtNewMethod.make(pool.get("void"), "set", paramsSet, null, "{this.other.put($1,$2);}", cc));
              m = cc.getDeclaredMethod("set", paramsSet);
      
      // add @JsonAnySetter to other map setter
              annotationAttribute = new AnnotationsAttribute(constPool, AnnotationsAttribute.visibleTag);
              annotationAttribute.addAnnotation(new Annotation(JsonAnySetter.class.getName(), constPool));
              m.getMethodInfo().addAttribute(annotationAttribute);        
      
              for (Entry<String, Class<?>> entry : properties.entrySet()) {
                  cc.addField(new CtField(resolveCtClass(entry.getValue()), entry.getKey(), cc));
                  // add getter
                  cc.addMethod(generateGetter(cc, entry.getKey(), entry.getValue()));
                  // add setter
                  cc.addMethod(generateSetter(cc, entry.getKey(), entry.getValue()));
              }
      
              return cc.toClass(cl, null);
          }
      
          private static CtMethod generateGetter(CtClass declaringClass, String fieldName, Class fieldClass)
                  throws CannotCompileException {
      
              String getterName = "get" + fieldName.substring(0, 1).toUpperCase()
                      + fieldName.substring(1);
      
              StringBuffer sb = new StringBuffer();
              sb.append("public ").append(fieldClass.getName()).append(" ")
                      .append(getterName).append("(){").append("return this.")
                      .append(fieldName).append(";").append("}");
              return CtMethod.make(sb.toString(), declaringClass);
          }
      
          private static CtMethod generateSetter(CtClass declaringClass, String fieldName, Class fieldClass)
                  throws CannotCompileException {
      
              String setterName = "set" + fieldName.substring(0, 1).toUpperCase()+ fieldName.substring(1);
      
              StringBuffer sb = new StringBuffer();
              sb.append("public void ").append(setterName).append("(")
                      .append(fieldClass.getName()).append(" ").append(fieldName)
                      .append(")").append("{").append("this.").append(fieldName)
                      .append("=").append(fieldName).append(";").append("}");
              return CtMethod.make(sb.toString(), declaringClass);
          }
          private static CtClass resolveCtClass(Class clazz) throws NotFoundException {
              ClassPool pool = ClassPool.getDefault();
              return pool.get(clazz.getName());
          }
      
      
      public class TestJson {
      
          public static void main(String[] args) throws NotFoundException, CannotCompileException, IOException {
              String jsonString = "{ \"info1\":\"val1\", \"info2\" :\"val2\"}";
              ObjectReader reader = new ObjectMapper().reader();
              Class<?> myClass = new ClassFactory().buildClass("MyClass");
              Object myClassInstance = reader.forType(myClass).readValue(jsonString);
      
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-04-11
        • 1970-01-01
        • 2011-10-04
        • 1970-01-01
        • 1970-01-01
        • 2014-04-22
        相关资源
        最近更新 更多